From 399727de3b234e4093eeacafc22e6d8c3ff2a069 Mon Sep 17 00:00:00 2001 From: Sampo Kuokkanen Date: Mon, 18 May 2026 22:59:10 +0900 Subject: [PATCH 01/13] Fix duplicate assertion in test_metaclass The final assertion of test_metaclass was identical to the one on line 161. Correcting to metametaclass_method_b. --- test/ruby/test_metaclass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ruby/test_metaclass.rb b/test/ruby/test_metaclass.rb index 8c1990a78cfb2c..6570fa5945b798 100644 --- a/test/ruby/test_metaclass.rb +++ b/test/ruby/test_metaclass.rb @@ -163,6 +163,6 @@ def metametaclass_method_b; end assert_nothing_raised{ metametaclass_of_bar.metaclass_method_c } assert_nothing_raised{ metametaclass_of_bar.metametaclass_method_o } assert_nothing_raised{ metametaclass_of_bar.metametaclass_method_f } - assert_raise(NoMethodError){ metametaclass_of_bar.metaclass_method_b } + assert_raise(NoMethodError){ metametaclass_of_bar.metametaclass_method_b } end end From bc8fc5da9116f40e9f44a795b97cdbe991206515 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 19 May 2026 12:55:47 +0900 Subject: [PATCH 02/13] Add tarball-test workflow for snapshot tarball CI Port the daily snapshot tarball pipeline from ruby/actions into ruby/ruby so the tarball build and per-OS tests run on every push and pull request. The make-snapshot composite action gains a srcdir input so the same logic can either clone ruby/ruby (daily upload from ruby/actions) or operate on the working tree (this workflow). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/make-snapshot/action.yml | 69 +++++ .github/workflows/tarball-macos.yml | 109 +++++++ .github/workflows/tarball-non-development.yml | 70 +++++ .github/workflows/tarball-test.yml | 66 +++++ .github/workflows/tarball-ubuntu.yml | 271 ++++++++++++++++++ .github/workflows/tarball-windows.yml | 169 +++++++++++ 6 files changed, 754 insertions(+) create mode 100644 .github/actions/make-snapshot/action.yml create mode 100644 .github/workflows/tarball-macos.yml create mode 100644 .github/workflows/tarball-non-development.yml create mode 100644 .github/workflows/tarball-test.yml create mode 100644 .github/workflows/tarball-ubuntu.yml create mode 100644 .github/workflows/tarball-windows.yml diff --git a/.github/actions/make-snapshot/action.yml b/.github/actions/make-snapshot/action.yml new file mode 100644 index 00000000000000..ab46de63772a28 --- /dev/null +++ b/.github/actions/make-snapshot/action.yml @@ -0,0 +1,69 @@ +name: 'make-snapshot' +description: 'Make snapshot tarballs' +inputs: + version: + description: 'Target Version' + required: false + shallow-since: + description: 'git fetch --shallow-since' + required: true + default: '2018-12-25 00:00:00' + fetch-branch: + description: 'fetch branch' + required: false + generate-tar-bz2: + description: 'Generate .tar.bz2' + required: false + srcdir: + description: 'srcdir for tool/make-snapshot. Empty = clone ruby/ruby into ./ruby.' + required: false + default: '' + +runs: + using: "composite" + steps: + - name: Install libraries + run: | + set -x + sudo apt-get update -q || : + sudo apt-get install --no-install-recommends -q -y build-essential git bison autoconf ruby p7zip-full curl + shell: bash + - name: Checkout ruby/ruby for tool/make-snapshot + if: inputs.srcdir == '' + run: git clone --single-branch --depth=1 https://github.com/ruby/ruby ruby + shell: bash + - name: Fetch branches and notes (clone mode) + if: inputs.srcdir == '' + run: | + set -x + cd ruby + git fetch --shallow-since='${{ inputs.shallow-since }}' + [ -n "${{ inputs.fetch-branch }}" ] && git fetch origin '+${{ inputs.fetch-branch }}:${{ inputs.fetch-branch }}' + git fetch origin '+refs/notes/commits:refs/notes/commits' + git fetch origin '+refs/notes/log-fix:refs/notes/log-fix' + shell: bash + - name: Fetch notes (local srcdir mode) + if: inputs.srcdir != '' + working-directory: ${{ inputs.srcdir }} + run: | + git fetch origin '+refs/notes/commits:refs/notes/commits' || : + git fetch origin '+refs/notes/log-fix:refs/notes/log-fix' || : + shell: bash + - name: Make snapshot + run: | + SRCDIR="${{ inputs.srcdir }}" + [ -z "$SRCDIR" ] && SRCDIR=ruby + if [ -n "${{ inputs.generate-tar-bz2 }}" ]; then + ruby "$SRCDIR/tool/make-snapshot" -archname=$archname -srcdir="$SRCDIR" pkg ${{ inputs.version }} + else + ruby "$SRCDIR/tool/make-snapshot" -archname=$archname -srcdir="$SRCDIR" -packages=gzip,xz,zip pkg ${{ inputs.version }} + fi + shell: bash + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: Packages + path: pkg + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: Info + path: pkg/info diff --git a/.github/workflows/tarball-macos.yml b/.github/workflows/tarball-macos.yml new file mode 100644 index 00000000000000..7309c69d246e21 --- /dev/null +++ b/.github/workflows/tarball-macos.yml @@ -0,0 +1,109 @@ +name: tarball-macos (reusable) + +on: + workflow_call: + inputs: + archname: + description: 'archname (e.g. snapshot-master, snapshot-ruby_3_3)' + required: true + type: string + allow-failures: + description: 'TEST_BUNDLED_GEMS_ALLOW_FAILURES value' + required: false + type: string + default: '' + patch-url: + description: 'Patch URL forwarded from workflow_dispatch' + required: false + type: string + default: '' + rebuild-homebrew-ruby: + description: 'Uninstall and reinstall Homebrew Ruby on macos-15-intel' + required: false + type: boolean + default: false + +jobs: + macos: + strategy: + matrix: + test_task: [check, test-bundled-gems] + os: [macos-14, macos-15, macos-15-intel] + include: + - os: macos-14 + test_task: test-bundler-parallel + fail-fast: false + runs-on: ${{ matrix.os }} + env: + TEST_BUNDLED_GEMS_ALLOW_FAILURES: ${{ inputs.allow-failures }} + steps: + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: Packages + path: pkg + - name: Extract + run: tar xf pkg/*.tar.xz + - name: Apply patch + run: | + set -x + curl -sSL "${RUBY_PATCH_URL}" -o ruby.patch + cd snapshot-*/ + git apply ../ruby.patch + shell: bash + env: + RUBY_PATCH_URL: ${{ inputs.patch-url }} + if: inputs.patch-url != '' + - name: Install libraries + run: | + with_retry () { + "$@" || { sleep 5 && "$@"; } || { sleep 60 && "$@"; } + } + set -x + with_retry brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline libyaml + - name: Uninstall Homebrew Ruby + run: | + for formula in $(brew list 2>/dev/null | grep '^ruby'); do + brew uninstall --force "$formula" + done + sudo rm -rf /usr/local/lib/ruby + if: inputs.rebuild-homebrew-ruby && matrix.os == 'macos-15-intel' + - name: Set ENV + run: | + echo "JOBS=-j$((1 + $(sysctl -n hw.activecpu)))" >> $GITHUB_ENV + - name: configure + run: cd snapshot-*/ && ./configure --with-openssl-dir=$(brew --prefix openssl@1.1) --with-readline-dir=$(brew --prefix readline) --with-libyaml-dir=$(brew --prefix libyaml) + - name: make + run: cd snapshot-*/ && make $JOBS + - name: Reinstall Homebrew Ruby + run: brew install ruby + if: inputs.rebuild-homebrew-ruby && matrix.os == 'macos-15-intel' && (matrix.test_task == 'test-bundled-gems' || matrix.test_task == 'test-bundler-parallel') + - name: Tests + run: cd snapshot-*/ && make $JOBS -s ${{ matrix.test_task }} + env: + RUBY_TESTOPTS: "-q --tty=no" + RUBY_DEBUG_TEST_NO_REMOTE: "1" + # leaked-globals since 2.7 + - name: Leaked Globals + run: cd snapshot-*/ && make -s leaked-globals + if: matrix.test_task == 'check' + - name: make install without root privilege + run: cd snapshot-*/ && make $JOBS install DESTDIR="/tmp/destdir" + if: matrix.test_task == 'check' + - name: make install + run: cd snapshot-*/ && sudo make $JOBS install + if: matrix.test_task == 'check' + - name: ruby -v + run: /usr/local/bin/ruby -v + if: matrix.test_task == 'check' + - uses: ruby/action-slack@54175162371f1f7c8eb94d7c8644ee2479fcd375 # v3.2.2 + with: + payload: | + { + "attachments": [{ + "text": "${{ job.status }}: ${{ matrix.os }} / ${{ matrix.test_task }} ", + "color": "danger" + }] + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SNAPSHOT_SLACK_WEBHOOK_URL }} + if: failure() diff --git a/.github/workflows/tarball-non-development.yml b/.github/workflows/tarball-non-development.yml new file mode 100644 index 00000000000000..09e6b44a879fbb --- /dev/null +++ b/.github/workflows/tarball-non-development.yml @@ -0,0 +1,70 @@ +name: tarball-non-development (reusable) + +on: + workflow_call: {} + +jobs: + non_development: + runs-on: ubuntu-24.04 + env: + ruby_prefix: /tmp/ruby-snapshot + steps: + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: Packages + path: pkg + - name: Extract + run: tar xf pkg/*.tar.xz + - name: Substitute patchlevel + run: | + set -x + cd snapshot-*/ + sed -i.orig 's/^\( *# *define *RUBY_PATCHLEVEL\) *-.*/\1 0/' version.h + diff -u version.h.orig version.h || : + rm -f version.h.orig + - name: Install libraries + run: | + set -x + sudo apt-get update -q || : + sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison- autoconf- + - name: Set ENV + run: | + echo "JOBS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV + - name: configure + run: cd snapshot-*/ && ./configure --prefix="${ruby_prefix}" + - name: make + run: cd snapshot-*/ && make $JOBS + - name: Leaked Globals + run: cd snapshot-*/ && make -s leaked-globals + - name: make install + run: cd snapshot-*/ && make $JOBS install + - name: Set PATH + run: echo "PATH=${ruby_prefix}/bin:$PATH" >> $GITHUB_ENV + - name: Check patchlevel + id: check-patchlevel + run: | + exec "${ruby_prefix}/bin/ruby" -vx "$0" + #!ruby + puts "RUBY_PATCHLEVEL=#{RUBY_PATCHLEVEL.inspect}" + abort unless RUBY_PATCHLEVEL >= 0 + working-directory: ${{ env.ruby_prefix }} + continue-on-error: true + - name: Check LOADPATH + id: check-loadpath + run: | + exec ${ruby_prefix}/bin/ruby -vx "$0" + #!ruby + paths = $:.grep(/\+/) + pp paths + abort unless paths.empty? + working-directory: ${{ env.ruby_prefix }} + continue-on-error: true + - name: Check pathnames + id: check-pathnames + run: | + ! find -name '*+*' | grep + + working-directory: ${{ env.ruby_prefix }} + continue-on-error: true + - name: result + run: false + if: ${{ contains(steps.*.outcome, 'failure') }} diff --git a/.github/workflows/tarball-test.yml b/.github/workflows/tarball-test.yml new file mode 100644 index 00000000000000..1d7711ca485efe --- /dev/null +++ b/.github/workflows/tarball-test.yml @@ -0,0 +1,66 @@ +name: tarball-test +on: + push: + paths-ignore: + - 'doc/**' + - '**/man/*' + - '**.md' + - '**.rdoc' + - '**/.document' + - '.*.yml' + pull_request: + # Do not use paths-ignore for required status checks + # https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks + merge_group: + +concurrency: + group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} + cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }} + +permissions: + contents: read + +jobs: + tarball: + runs-on: ubuntu-latest + env: + archname: snapshot-master + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + - uses: ./.github/actions/make-snapshot + with: + srcdir: '.' + + ubuntu: + needs: tarball + uses: ./.github/workflows/tarball-ubuntu.yml + with: + archname: snapshot-master + allow-failures: power_assert + remove-gnupg: true + notify-ruby-sha: true + branch-label: master + secrets: inherit + + macos: + needs: tarball + uses: ./.github/workflows/tarball-macos.yml + with: + archname: snapshot-master + allow-failures: power_assert + secrets: inherit + + windows: + needs: tarball + uses: ./.github/workflows/tarball-windows.yml + with: + archname: snapshot-master + mode: modern + secrets: inherit + + non_development: + needs: tarball + uses: ./.github/workflows/tarball-non-development.yml + secrets: inherit diff --git a/.github/workflows/tarball-ubuntu.yml b/.github/workflows/tarball-ubuntu.yml new file mode 100644 index 00000000000000..1b4aebd30288ae --- /dev/null +++ b/.github/workflows/tarball-ubuntu.yml @@ -0,0 +1,271 @@ +name: tarball-ubuntu (reusable) + +on: + workflow_call: + inputs: + archname: + description: 'archname (e.g. snapshot-master, snapshot-ruby_3_3)' + required: true + type: string + allow-failures: + description: 'TEST_BUNDLED_GEMS_ALLOW_FAILURES value' + required: false + type: string + default: '' + patch-url: + description: 'Patch URL forwarded from workflow_dispatch' + required: false + type: string + default: '' + apt-mode: + description: '"none" | "git-only" | "ruby-and-git" — controls per-test_task apt install handling' + required: false + type: string + default: 'none' + setup-host-ruby: + description: 'Install Ruby 3.2 via ruby/setup-ruby for test-bundled-gems' + required: false + type: boolean + default: true + remove-gnupg: + description: 'Forcely remove ~/.gnupg after tests' + required: false + type: boolean + default: false + fixed-dirs-extra: + description: 'Create $HOME/.local/share, $HOME/.ssh on top of minimal fixed dirs' + required: false + type: boolean + default: true + ruby-bin: + description: 'Path to ruby for post-install version check' + required: false + type: string + default: '/usr/local/bin/ruby' + notify-ruby-sha: + description: 'Post ruby SHA failure notification to SIMPLER_ALERTS_URL' + required: false + type: boolean + default: false + branch-label: + description: 'Branch label for SIMPLER_ALERTS_URL notification. Defaults to "master".' + required: false + type: string + default: 'master' + +jobs: + ubuntu: + strategy: + matrix: + test_task: [check, test-bundler-parallel, test-bundled-gems] + os: [ubuntu-24.04, ubuntu-22.04] + fail-fast: false + runs-on: ${{ matrix.os }} + env: + TEST_BUNDLED_GEMS_ALLOW_FAILURES: ${{ inputs.allow-failures }} + steps: + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: Packages + path: pkg + - name: Extract + run: tar xf pkg/*.tar.xz + - name: Apply patch + run: | + set -x + curl -sSL "${RUBY_PATCH_URL}" -o ruby.patch + cd snapshot-*/ + git apply ../ruby.patch + shell: bash + env: + RUBY_PATCH_URL: ${{ inputs.patch-url }} + if: inputs.patch-url != '' + - name: Install libraries (apt-mode=none) + run: | + set -x + sudo apt-get update -q + sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison- autoconf- + if: inputs.apt-mode == 'none' + - name: Install libraries (apt-mode=git-only) + run: | + set -x + sudo apt-get update -q || : + # postfix `-` means `uninstall` + APT_INSTALL_GIT=git- + case "${{ matrix.test_task }}" in + test-bundled-gems) + # test-bundled-gems-fetch requires git + unset APT_INSTALL_GIT + ;; + test-bundler*) + # avoid Bundler::Source::Git::GitNotInstalledError + unset APT_INSTALL_GIT + ;; + *) + ;; + esac + sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison- autoconf- $APT_INSTALL_GIT + if: inputs.apt-mode == 'git-only' + - name: Install libraries (apt-mode=ruby-and-git) + run: | + set -x + sudo apt-get update -q || : + # postfix `-` means `uninstall` + APT_INSTALL_RUBY=ruby- + APT_INSTALL_GIT=git- + case "${{ matrix.test_task }}" in + test-bundled-gems) + # test-bundled-gems requires executable host ruby + APT_INSTALL_RUBY=ruby + # test-bundled-gems-fetch requires git + unset APT_INSTALL_GIT + ;; + test-bundler*) + # avoid Bundler::Source::Git::GitNotInstalledError + unset APT_INSTALL_GIT + ;; + *) + ;; + esac + sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison- autoconf- $APT_INSTALL_RUBY $APT_INSTALL_GIT + if: inputs.apt-mode == 'ruby-and-git' + - uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0 + with: + ruby-version: 3.2 + # test-bundled-gems requires executable host ruby + if: inputs.setup-host-ruby && matrix.test_task == 'test-bundled-gems' + - name: Fixed world writable dirs + run: | + mkdir -p $HOME/.local + mkdir -p $HOME/.cache/gem/specs + mkdir -p $HOME/.bundle/cache + chmod a-w $HOME/.bundle + # chmod a-w $HOME + # allow to write $HOME and check stats of HOME around tests (see below) + chmod -v a-w $HOME/.config + sudo chmod -R a-w /usr/share + sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v a-w $d; done' || : + - name: Fixed world writable dirs (extra) + run: | + mkdir -p $HOME/.local/share + mkdir -p $HOME/.ssh + if: inputs.fixed-dirs-extra + - name: Set ENV + run: | + echo "JOBS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV + - name: configure + run: cd snapshot-*/ && ./configure + - name: make + run: cd snapshot-*/ && make $JOBS + - name: Save stats of HOME + run: | + set -euxo pipefail + cd snapshot-*/ + cat >test.rb <<'EOF' + require 'pathname' + require 'digest' + out = [] + [ + Dir.home, + ].each do |dir| + Pathname(dir).each_child do |pn| + st = pn.stat + if st.file? + content = Digest::SHA1.hexdigest(pn.read) + elsif st.directory? && st.nlink <= 10 + content = pn.children.map(&:basename).map(&:to_s).sort + end + out << [pn.to_s, "%o"%st.mode, st.nlink, st.uid, st.gid, st.size, content].to_s + rescue + out << [pn.to_s, $!.inspect].to_s + end + end + File.open("/tmp/stat-before-tests.txt", "w") do |io| + io.puts out.sort + end + EOF + make runruby + rm -f test.rb + - name: Tests + run: cd snapshot-*/ && make $JOBS -s ${{ matrix.test_task }} + env: + RUBY_TESTOPTS: "-q --tty=no" + # not sure why ~/.gnupg is remained + # see tool/test/test_sync_default_gems.rb + - name: Forcely remove test directory + run: rm -rf $HOME/.gnupg + if: inputs.remove-gnupg + - name: Diff stats of HOME + run: | + set -euxo pipefail + cd snapshot-*/ + cat >test.rb <<'EOF' + require 'pathname' + require 'digest' + out = [] + [ + Dir.home, + ].each do |dir| + Pathname(dir).each_child do |pn| + st = pn.stat + if st.file? + content = Digest::SHA1.hexdigest(pn.read) + elsif st.directory? && st.nlink <= 10 + content = pn.children.map(&:basename).map(&:to_s).sort + end + out << [pn.to_s, "%o"%st.mode, st.nlink, st.uid, st.gid, st.size, content].to_s + rescue + out << [pn.to_s, $!.inspect].to_s + end + end + File.open("/tmp/stat-after-tests.txt", "w") do |io| + io.puts out.sort + end + EOF + make runruby + rm -f test.rb + diff -u /tmp/stat-before-tests.txt /tmp/stat-after-tests.txt + # leaked-globals since 2.7 + - name: Leaked Globals + run: cd snapshot-*/ && make -s leaked-globals + if: matrix.test_task == 'check' + - name: make install without root privilege + run: cd snapshot-*/ && make $JOBS install DESTDIR="/tmp/destdir" + if: matrix.test_task == 'check' + - name: make install + run: cd snapshot-*/ && sudo make $JOBS install + if: matrix.test_task == 'check' + - name: ruby -v + run: ${{ inputs.ruby-bin }} -v + if: matrix.test_task == 'check' + - name: Show .local + run: find $HOME/.local -ls + - uses: ruby/action-slack@54175162371f1f7c8eb94d7c8644ee2479fcd375 # v3.2.2 + with: + payload: | + { + "attachments": [{ + "text": "${{ job.status }}: ${{ matrix.os }} / ${{ matrix.test_task }} ", + "color": "danger" + }] + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SNAPSHOT_SLACK_WEBHOOK_URL }} + if: failure() + - name: Get ruby/ruby sha + id: ruby_sha + run: cd snapshot-*/ && ./ruby -e 'puts "sha=#{RUBY_REVISION}"' >> $GITHUB_OUTPUT + if: failure() && inputs.notify-ruby-sha + - uses: ruby/action-slack@54175162371f1f7c8eb94d7c8644ee2479fcd375 # v3.2.2 + with: + payload: | + { + "ci": "GitHub Actions", + "env": "snapshot: ${{ matrix.os }} / ${{ matrix.test_task }}", + "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", + "commit": "${{ steps.ruby_sha.outputs.sha }}", + "branch": "${{ inputs.branch-label }}" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} + if: failure() && inputs.notify-ruby-sha diff --git a/.github/workflows/tarball-windows.yml b/.github/workflows/tarball-windows.yml new file mode 100644 index 00000000000000..50fcdadc11db8f --- /dev/null +++ b/.github/workflows/tarball-windows.yml @@ -0,0 +1,169 @@ +name: tarball-windows (reusable) + +on: + workflow_call: + inputs: + archname: + description: 'archname (e.g. snapshot-master)' + required: true + type: string + patch-url: + description: 'Patch URL forwarded from workflow_dispatch' + required: false + type: string + default: '' + mode: + description: '"modern" (vssetup + 2022/2025-vs2026) or "legacy" (vs2022 + vcvars 14.2 + test-all/test-spec split)' + required: false + type: string + default: 'modern' + +jobs: + windows: + strategy: + matrix: + include: ${{ fromJSON(inputs.mode == 'legacy' + && '[{"os":"2022","vs":2022,"vcvars":"10.0.22621.0 -vcvars_ver=14.2","test_task":"check"}]' + || '[{"os":"2022","test_task":"check"},{"os":"2025-vs2026","test_task":"check"}]') }} + fail-fast: false + runs-on: windows-${{ matrix.os }} + defaults: + run: + shell: cmd + working-directory: build + name: Windows ${{ matrix.os }} (${{ matrix.test_task }}) + env: + GITPULLOPTIONS: --no-tags origin ${{github.ref}} + PATCH: C:\msys64\usr\bin\patch.exe + OS_VER: windows-${{ matrix.os }} + # see https://github.com/ruby/ruby/commit/9ff4399decef0036897d3cfb9ac2c710dea913ca + OPENSSL_MODULES: C:\vcpkg\installed\x64-windows\bin + steps: + - run: md build + working-directory: + + - uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0 + with: + ruby-version: '3.1' + bundler: none + windows-toolchain: none + + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 + id: setup-msys2 + with: + update: true + install: >- + patch + - name: patch path + shell: msys2 {0} + run: echo PATCH=$(cygpath -wa $(command -v patch)) >> $GITHUB_ENV + if: ${{ steps.setup-msys2.outcome == 'success' }} + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: C:\vcpkg\installed + key: ${{ runner.os }}-vcpkg-installed-${{ env.OS_VER }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-vcpkg-installed-${{ env.OS_VER }}- + ${{ runner.os }}-vcpkg-installed- + - name: Install libraries with vcpkg + run: | + vcpkg --triplet x64-windows install libffi libyaml openssl readline zlib + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: Packages + path: pkg + - name: Extract + run: 7z x pkg/*.zip + working-directory: + - name: Apply patch + run: | + set -x + curl -sSL "${RUBY_PATCH_URL}" -o ruby.patch + cd ${{ inputs.archname }}/ + git apply ../ruby.patch + shell: bash + env: + RUBY_PATCH_URL: ${{ inputs.patch-url }} + if: inputs.patch-url != '' + working-directory: + + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: snapshot-*/.downloaded-cache + key: downloaded-cache + + - name: setup env (modern) + # %TEMP% is inconsistent with %TMP% and test-all expects they are consistent. + # https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302 + run: | + set > old.env + call ..\${{ inputs.archname }}\win32\vssetup.cmd + set TMP=%USERPROFILE%\AppData\Local\Temp + set TEMP=%USERPROFILE%\AppData\Local\Temp + set /a TEST_JOBS=(15 * %NUMBER_OF_PROCESSORS% / 10) > nul + set > new.env + if: inputs.mode == 'modern' + - name: setup env (legacy) + run: | + if not "%VCVARS%" == "" goto :vcset + set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + :vcset + set > old.env + call %VCVARS% ${{ matrix.vcvars }} + set TMP=%USERPROFILE%\AppData\Local\Temp + set TEMP=%USERPROFILE%\AppData\Local\Temp + set /a TEST_JOBS=(15 * %NUMBER_OF_PROCESSORS% / 10) > nul + set > new.env + if: inputs.mode == 'legacy' + + - name: update env + shell: pwsh + run: | + $old = (Get-Content old.env); $new = (Get-Content new.env) + del *.env + Compare-Object $old $new | + Where-Object { $_.SideIndicator -eq '=>' } | + Select-Object -ExpandProperty InputObject | + Add-Content -Path $env:GITHUB_ENV + - name: link libraries + run: | + for %%I in (C:\vcpkg\installed\x64-windows\bin\*.dll) do ( + if not %%~nI == readline mklink %%~nxI %%I + ) + - name: Configure + run: >- + ../${{ inputs.archname }}/win32/configure.bat --disable-install-doc + --with-opt-dir=C:/vcpkg/installed/x64-windows + - run: nmake incs + - run: nmake extract-extlibs + - run: nmake + env: + YACC: win_bison + + - name: ruby -v + run: | + .\ruby -v + + - run: nmake test + timeout-minutes: 5 + + - run: nmake ${{ matrix.test_task }} + env: + RUBY_TESTOPTS: -j${{env.TEST_JOBS}} + timeout-minutes: 70 + continue-on-error: ${{ matrix.continue-on-error || false }} + + - uses: ruby/action-slack@54175162371f1f7c8eb94d7c8644ee2479fcd375 # v3.2.2 + with: + payload: | + { + "attachments": [{ + "text": "${{ job.status }}: ${{ env.OS_VER }} / ${{ matrix.test_task }} ", + "color": "danger" + }] + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SNAPSHOT_SLACK_WEBHOOK_URL }} + if: failure() From 11465260ab4f979d8add2beed4c596751615d663 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 19 May 2026 13:06:51 +0900 Subject: [PATCH 03/13] Materialize local master branch before make-snapshot tool/make-snapshot reads HEAD via tool/lib/vcs.rb#_get_revisions and, on detached checkouts (pull_request, merge_group), parses "(HEAD detached at )" into branch name "pull/N/merge" which then breaks `git clone -b` in the export step. It also calls #upstream during ChangeLog generation, which requires the branch to track a remote. Fetch origin/master, force a local master at HEAD, and link its upstream so both lookups land on a real branch. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/tarball-test.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/tarball-test.yml b/.github/workflows/tarball-test.yml index 1d7711ca485efe..425f5595a3eb19 100644 --- a/.github/workflows/tarball-test.yml +++ b/.github/workflows/tarball-test.yml @@ -29,6 +29,15 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + # tool/make-snapshot derives the branch name from HEAD and looks up + # the upstream during ChangeLog generation. Detached checkouts + # (pull_request, merge_group) lack a local master with tracking, so + # pin one to HEAD and connect it to origin/master. + - name: Materialize local master branch + run: | + git fetch --no-tags --depth=1 origin +refs/heads/master:refs/remotes/origin/master + git checkout -B master HEAD + git branch --set-upstream-to=origin/master master - uses: ./.github/actions/make-snapshot with: srcdir: '.' From 942f45b2af17f3b6041baef3d466136c0e16d7b8 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 19 May 2026 13:33:34 +0900 Subject: [PATCH 04/13] Pass inputs through env to avoid template injection zizmor flags inline `${{ inputs.X }}` expansions inside run scripts as template-injection errors. Reusable workflow inputs come from the in-repo caller and are trusted, but routing them through env avoids the class of mistake entirely and silences the audit without per-line ignores. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/make-snapshot/action.yml | 18 ++++++++++++------ .github/workflows/tarball-ubuntu.yml | 4 +++- .github/workflows/tarball-windows.yml | 11 ++++++++--- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/.github/actions/make-snapshot/action.yml b/.github/actions/make-snapshot/action.yml index ab46de63772a28..20ea5461981319 100644 --- a/.github/actions/make-snapshot/action.yml +++ b/.github/actions/make-snapshot/action.yml @@ -34,11 +34,14 @@ runs: shell: bash - name: Fetch branches and notes (clone mode) if: inputs.srcdir == '' + env: + SHALLOW_SINCE: ${{ inputs.shallow-since }} + FETCH_BRANCH: ${{ inputs.fetch-branch }} run: | set -x cd ruby - git fetch --shallow-since='${{ inputs.shallow-since }}' - [ -n "${{ inputs.fetch-branch }}" ] && git fetch origin '+${{ inputs.fetch-branch }}:${{ inputs.fetch-branch }}' + git fetch --shallow-since="$SHALLOW_SINCE" + [ -n "$FETCH_BRANCH" ] && git fetch origin "+$FETCH_BRANCH:$FETCH_BRANCH" git fetch origin '+refs/notes/commits:refs/notes/commits' git fetch origin '+refs/notes/log-fix:refs/notes/log-fix' shell: bash @@ -50,13 +53,16 @@ runs: git fetch origin '+refs/notes/log-fix:refs/notes/log-fix' || : shell: bash - name: Make snapshot + env: + SRCDIR: ${{ inputs.srcdir }} + GENERATE_TAR_BZ2: ${{ inputs.generate-tar-bz2 }} + VERSION: ${{ inputs.version }} run: | - SRCDIR="${{ inputs.srcdir }}" [ -z "$SRCDIR" ] && SRCDIR=ruby - if [ -n "${{ inputs.generate-tar-bz2 }}" ]; then - ruby "$SRCDIR/tool/make-snapshot" -archname=$archname -srcdir="$SRCDIR" pkg ${{ inputs.version }} + if [ -n "$GENERATE_TAR_BZ2" ]; then + ruby "$SRCDIR/tool/make-snapshot" -archname=$archname -srcdir="$SRCDIR" pkg $VERSION else - ruby "$SRCDIR/tool/make-snapshot" -archname=$archname -srcdir="$SRCDIR" -packages=gzip,xz,zip pkg ${{ inputs.version }} + ruby "$SRCDIR/tool/make-snapshot" -archname=$archname -srcdir="$SRCDIR" -packages=gzip,xz,zip pkg $VERSION fi shell: bash - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 diff --git a/.github/workflows/tarball-ubuntu.yml b/.github/workflows/tarball-ubuntu.yml index 1b4aebd30288ae..a1ad7b9f5722a3 100644 --- a/.github/workflows/tarball-ubuntu.yml +++ b/.github/workflows/tarball-ubuntu.yml @@ -236,7 +236,9 @@ jobs: run: cd snapshot-*/ && sudo make $JOBS install if: matrix.test_task == 'check' - name: ruby -v - run: ${{ inputs.ruby-bin }} -v + env: + RUBY_BIN: ${{ inputs.ruby-bin }} + run: '"$RUBY_BIN" -v' if: matrix.test_task == 'check' - name: Show .local run: find $HOME/.local -ls diff --git a/.github/workflows/tarball-windows.yml b/.github/workflows/tarball-windows.yml index 50fcdadc11db8f..4ced135e61b259 100644 --- a/.github/workflows/tarball-windows.yml +++ b/.github/workflows/tarball-windows.yml @@ -80,11 +80,12 @@ jobs: run: | set -x curl -sSL "${RUBY_PATCH_URL}" -o ruby.patch - cd ${{ inputs.archname }}/ + cd "$ARCHNAME" git apply ../ruby.patch shell: bash env: RUBY_PATCH_URL: ${{ inputs.patch-url }} + ARCHNAME: ${{ inputs.archname }} if: inputs.patch-url != '' working-directory: @@ -96,9 +97,11 @@ jobs: - name: setup env (modern) # %TEMP% is inconsistent with %TMP% and test-all expects they are consistent. # https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302 + env: + ARCHNAME: ${{ inputs.archname }} run: | set > old.env - call ..\${{ inputs.archname }}\win32\vssetup.cmd + call ..\%ARCHNAME%\win32\vssetup.cmd set TMP=%USERPROFILE%\AppData\Local\Temp set TEMP=%USERPROFILE%\AppData\Local\Temp set /a TEST_JOBS=(15 * %NUMBER_OF_PROCESSORS% / 10) > nul @@ -133,8 +136,10 @@ jobs: if not %%~nI == readline mklink %%~nxI %%I ) - name: Configure + env: + ARCHNAME: ${{ inputs.archname }} run: >- - ../${{ inputs.archname }}/win32/configure.bat --disable-install-doc + ../%ARCHNAME%/win32/configure.bat --disable-install-doc --with-opt-dir=C:/vcpkg/installed/x64-windows - run: nmake incs - run: nmake extract-extlibs From 07e79c381a3f56959733137bb05913bf997849c9 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 19 May 2026 13:38:52 +0900 Subject: [PATCH 05/13] Run tarball-test on a daily schedule and gate Slack on it Add a daily 18:30 UTC schedule trigger to match the existing ruby/actions snapshot cron, and limit the Slack failure notifications in the reusable workflows to schedule runs. PRs and merge_group runs are still gated by the workflow's CI status, but won't spam the Snapshot/SimplerAlerts channels or fail on missing webhook secrets in forks. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/tarball-macos.yml | 2 +- .github/workflows/tarball-test.yml | 2 ++ .github/workflows/tarball-ubuntu.yml | 6 +++--- .github/workflows/tarball-windows.yml | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tarball-macos.yml b/.github/workflows/tarball-macos.yml index 7309c69d246e21..3e2f2e0debc2f2 100644 --- a/.github/workflows/tarball-macos.yml +++ b/.github/workflows/tarball-macos.yml @@ -106,4 +106,4 @@ jobs: } env: SLACK_WEBHOOK_URL: ${{ secrets.SNAPSHOT_SLACK_WEBHOOK_URL }} - if: failure() + if: failure() && github.event_name == 'schedule' diff --git a/.github/workflows/tarball-test.yml b/.github/workflows/tarball-test.yml index 425f5595a3eb19..77933865118a13 100644 --- a/.github/workflows/tarball-test.yml +++ b/.github/workflows/tarball-test.yml @@ -12,6 +12,8 @@ on: # Do not use paths-ignore for required status checks # https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks merge_group: + schedule: + - cron: '30 18 * * *' # Daily at 18:30 UTC concurrency: group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} diff --git a/.github/workflows/tarball-ubuntu.yml b/.github/workflows/tarball-ubuntu.yml index a1ad7b9f5722a3..2b69b5066734d7 100644 --- a/.github/workflows/tarball-ubuntu.yml +++ b/.github/workflows/tarball-ubuntu.yml @@ -253,11 +253,11 @@ jobs: } env: SLACK_WEBHOOK_URL: ${{ secrets.SNAPSHOT_SLACK_WEBHOOK_URL }} - if: failure() + if: failure() && github.event_name == 'schedule' - name: Get ruby/ruby sha id: ruby_sha run: cd snapshot-*/ && ./ruby -e 'puts "sha=#{RUBY_REVISION}"' >> $GITHUB_OUTPUT - if: failure() && inputs.notify-ruby-sha + if: failure() && inputs.notify-ruby-sha && github.event_name == 'schedule' - uses: ruby/action-slack@54175162371f1f7c8eb94d7c8644ee2479fcd375 # v3.2.2 with: payload: | @@ -270,4 +270,4 @@ jobs: } env: SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} - if: failure() && inputs.notify-ruby-sha + if: failure() && inputs.notify-ruby-sha && github.event_name == 'schedule' diff --git a/.github/workflows/tarball-windows.yml b/.github/workflows/tarball-windows.yml index 4ced135e61b259..9c7940e1729477 100644 --- a/.github/workflows/tarball-windows.yml +++ b/.github/workflows/tarball-windows.yml @@ -171,4 +171,4 @@ jobs: } env: SLACK_WEBHOOK_URL: ${{ secrets.SNAPSHOT_SLACK_WEBHOOK_URL }} - if: failure() + if: failure() && github.event_name == 'schedule' From 540ffb4a74d66695e9e8b6eca0e76bc5b0004d2e Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 19 May 2026 13:41:21 +0900 Subject: [PATCH 06/13] Forcely -> Forcibly Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/tarball-ubuntu.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tarball-ubuntu.yml b/.github/workflows/tarball-ubuntu.yml index 2b69b5066734d7..6568942055e5f4 100644 --- a/.github/workflows/tarball-ubuntu.yml +++ b/.github/workflows/tarball-ubuntu.yml @@ -28,7 +28,7 @@ on: type: boolean default: true remove-gnupg: - description: 'Forcely remove ~/.gnupg after tests' + description: 'Forcibly remove ~/.gnupg after tests' required: false type: boolean default: false @@ -192,7 +192,7 @@ jobs: RUBY_TESTOPTS: "-q --tty=no" # not sure why ~/.gnupg is remained # see tool/test/test_sync_default_gems.rb - - name: Forcely remove test directory + - name: Forcibly remove test directory run: rm -rf $HOME/.gnupg if: inputs.remove-gnupg - name: Diff stats of HOME From e65d75790ed1465b7816b6a6a5c69e9c437e09a7 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 19 May 2026 13:43:40 +0900 Subject: [PATCH 07/13] Promote archname to an explicit composite input The composite action previously read $archname from the caller's job env, leaving the dependency implicit. Declare archname as a required input and pass it via with: so the action is self-contained and the linkage is visible at the call site. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/make-snapshot/action.yml | 8 ++++++-- .github/workflows/tarball-test.yml | 3 +-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/actions/make-snapshot/action.yml b/.github/actions/make-snapshot/action.yml index 20ea5461981319..cd144d2bf6d1d0 100644 --- a/.github/actions/make-snapshot/action.yml +++ b/.github/actions/make-snapshot/action.yml @@ -1,6 +1,9 @@ name: 'make-snapshot' description: 'Make snapshot tarballs' inputs: + archname: + description: 'archname passed to tool/make-snapshot (e.g. snapshot-master)' + required: true version: description: 'Target Version' required: false @@ -54,15 +57,16 @@ runs: shell: bash - name: Make snapshot env: + ARCHNAME: ${{ inputs.archname }} SRCDIR: ${{ inputs.srcdir }} GENERATE_TAR_BZ2: ${{ inputs.generate-tar-bz2 }} VERSION: ${{ inputs.version }} run: | [ -z "$SRCDIR" ] && SRCDIR=ruby if [ -n "$GENERATE_TAR_BZ2" ]; then - ruby "$SRCDIR/tool/make-snapshot" -archname=$archname -srcdir="$SRCDIR" pkg $VERSION + ruby "$SRCDIR/tool/make-snapshot" "-archname=$ARCHNAME" -srcdir="$SRCDIR" pkg $VERSION else - ruby "$SRCDIR/tool/make-snapshot" -archname=$archname -srcdir="$SRCDIR" -packages=gzip,xz,zip pkg $VERSION + ruby "$SRCDIR/tool/make-snapshot" "-archname=$ARCHNAME" -srcdir="$SRCDIR" -packages=gzip,xz,zip pkg $VERSION fi shell: bash - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 diff --git a/.github/workflows/tarball-test.yml b/.github/workflows/tarball-test.yml index 77933865118a13..bc6948f2ab9efc 100644 --- a/.github/workflows/tarball-test.yml +++ b/.github/workflows/tarball-test.yml @@ -25,8 +25,6 @@ permissions: jobs: tarball: runs-on: ubuntu-latest - env: - archname: snapshot-master steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -42,6 +40,7 @@ jobs: git branch --set-upstream-to=origin/master master - uses: ./.github/actions/make-snapshot with: + archname: snapshot-master srcdir: '.' ubuntu: From 2c8d3e1612cada8f8a32e182d3d5ef7698d8814d Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 19 May 2026 13:54:41 +0900 Subject: [PATCH 08/13] Derive archname and branch-label from a single env.BRANCH Cherry-picking tarball-test.yml to maintenance branches previously required editing six places (four archname literals, branch-label, and the Materialize step). Route everything through env.BRANCH on the tarball job and expose it via outputs so downstream reusable workflow calls reference needs.tarball.outputs.branch. Maintenance branches now flip one line. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/tarball-test.yml | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tarball-test.yml b/.github/workflows/tarball-test.yml index bc6948f2ab9efc..49a3800e540b9f 100644 --- a/.github/workflows/tarball-test.yml +++ b/.github/workflows/tarball-test.yml @@ -25,40 +25,46 @@ permissions: jobs: tarball: runs-on: ubuntu-latest + # Cherry-pick to maintenance branches by changing only env.BRANCH below; + # archname / branch-label / Materialize all derive from it. + env: + BRANCH: master + outputs: + branch: ${{ env.BRANCH }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 # tool/make-snapshot derives the branch name from HEAD and looks up # the upstream during ChangeLog generation. Detached checkouts - # (pull_request, merge_group) lack a local master with tracking, so - # pin one to HEAD and connect it to origin/master. - - name: Materialize local master branch + # (pull_request, merge_group) lack a local branch with tracking, so + # pin one to HEAD and connect it to the matching origin ref. + - name: Materialize local branch run: | - git fetch --no-tags --depth=1 origin +refs/heads/master:refs/remotes/origin/master - git checkout -B master HEAD - git branch --set-upstream-to=origin/master master + git fetch --no-tags --depth=1 origin "+refs/heads/$BRANCH:refs/remotes/origin/$BRANCH" + git checkout -B "$BRANCH" HEAD + git branch --set-upstream-to="origin/$BRANCH" "$BRANCH" - uses: ./.github/actions/make-snapshot with: - archname: snapshot-master + archname: snapshot-${{ env.BRANCH }} srcdir: '.' ubuntu: needs: tarball uses: ./.github/workflows/tarball-ubuntu.yml with: - archname: snapshot-master + archname: snapshot-${{ needs.tarball.outputs.branch }} allow-failures: power_assert remove-gnupg: true notify-ruby-sha: true - branch-label: master + branch-label: ${{ needs.tarball.outputs.branch }} secrets: inherit macos: needs: tarball uses: ./.github/workflows/tarball-macos.yml with: - archname: snapshot-master + archname: snapshot-${{ needs.tarball.outputs.branch }} allow-failures: power_assert secrets: inherit @@ -66,7 +72,7 @@ jobs: needs: tarball uses: ./.github/workflows/tarball-windows.yml with: - archname: snapshot-master + archname: snapshot-${{ needs.tarball.outputs.branch }} mode: modern secrets: inherit From 9af2c5527d5c359467a0328d378583024932597f Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 19 May 2026 14:24:26 +0900 Subject: [PATCH 09/13] Run test-bundler-parallel on macos-15 and pin macos-15-intel to check Promote test-bundler-parallel from a macos-14 include entry into the base test_task list so macos-15 picks it up too, and limit macos-15-intel to a single check job via include. The Intel runner is slower and noisier; running only check keeps coverage without paying for the full test set there. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/tarball-macos.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tarball-macos.yml b/.github/workflows/tarball-macos.yml index 3e2f2e0debc2f2..d6552d516a7f12 100644 --- a/.github/workflows/tarball-macos.yml +++ b/.github/workflows/tarball-macos.yml @@ -27,11 +27,11 @@ jobs: macos: strategy: matrix: - test_task: [check, test-bundled-gems] - os: [macos-14, macos-15, macos-15-intel] + test_task: [check, test-bundled-gems, test-bundler-parallel] + os: [macos-14, macos-15] include: - - os: macos-14 - test_task: test-bundler-parallel + - os: macos-15-intel + test_task: check fail-fast: false runs-on: ${{ matrix.os }} env: From efd35db39c1fdee06cac7812b74d25dc52db02dd Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 15 May 2026 16:57:15 +0900 Subject: [PATCH 10/13] Make `rb_load_entrypoint` accept two arguments directly `rb_load_entrypoint` is used only by `rb_box_load`; it checks that the argument has two-elements (implicitly assuming it is an array), extracts its contents, and then simply discards the argument. --- box.c | 3 +-- internal/load.h | 2 +- load.c | 18 +++--------------- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/box.c b/box.c index b55e8bb137a576..0ca7838be96045 100644 --- a/box.c +++ b/box.c @@ -860,8 +860,7 @@ rb_box_load(int argc, VALUE *argv, VALUE box) rb_vm_frame_flag_set_box_require(GET_EC()); - VALUE args = rb_ary_new_from_args(2, fname, wrap); - return rb_load_entrypoint(args); + return rb_load_entrypoint(fname, wrap); } static VALUE diff --git a/internal/load.h b/internal/load.h index fb880a43ba42e0..6cffe0ce64bcde 100644 --- a/internal/load.h +++ b/internal/load.h @@ -12,7 +12,7 @@ /* load.c */ VALUE rb_get_expanded_load_path(void); -VALUE rb_load_entrypoint(VALUE args); +VALUE rb_load_entrypoint(VALUE fname, VALUE wrap); VALUE rb_require_relative_entrypoint(VALUE fname); int rb_require_internal(VALUE fname); NORETURN(void rb_load_fail(VALUE, const char*)); diff --git a/load.c b/load.c index f7bdf07641a84e..1ee66e2bfcd3e9 100644 --- a/load.c +++ b/load.c @@ -875,8 +875,8 @@ rb_load_protect(VALUE fname, int wrap, int *pstate) if (state != TAG_NONE) *pstate = state; } -static VALUE -load_entrypoint_internal(VALUE fname, VALUE wrap) +VALUE +rb_load_entrypoint(VALUE fname, VALUE wrap) { VALUE path, orig_fname; @@ -897,18 +897,6 @@ load_entrypoint_internal(VALUE fname, VALUE wrap) return Qtrue; } -VALUE -rb_load_entrypoint(VALUE args) -{ - VALUE fname, wrap; - if (RARRAY_LEN(args) != 2) { - rb_bug("invalid arguments: %ld", RARRAY_LEN(args)); - } - fname = rb_ary_entry(args, 0); - wrap = rb_ary_entry(args, 1); - return load_entrypoint_internal(fname, wrap); -} - /* * call-seq: * load(filename, wrap=false) -> true @@ -944,7 +932,7 @@ rb_f_load(int argc, VALUE *argv, VALUE _) { VALUE fname, wrap; rb_scan_args(argc, argv, "11", &fname, &wrap); - return load_entrypoint_internal(fname, wrap); + return rb_load_entrypoint(fname, wrap); } static char * From 6c140117e7b7a15e165fa2b7270c8c580905ec64 Mon Sep 17 00:00:00 2001 From: Piotr Kubaj Date: Mon, 18 May 2026 12:13:34 +0000 Subject: [PATCH 11/13] [ppc64le] Add ELFv2 global entry prologue to coroutine_transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hand-written `coroutine_transfer` in `coroutine/ppc64le/Context.S` lacks the ELFv2 ABI global entry prologue. When the function is reached via the PLT (lazy resolution path), r2 (TOC pointer) is left pointing at whatever the resolver had loaded — typically the dynamic linker's own TOC — instead of libruby's TOC. The wrong r2 propagates through the fiber switch into `fiber_entry`, which jumps into `fiber_restore_thread` → `rb_current_ec_set`. The first PLT thunk inside the new fiber (`__plt___tls_get_addr` for the `ruby_current_ec` thread-local) computes its GOT entry as `r2 + offset` and segfaults dereferencing the bogus address. This manifests as a SIGSEGV on any code path that uses fibers — including `Enumerator#next`, so even a one-liner crashes: ruby -e 'a=[1,2,3]; e=a.to_enum; loop { p e.next }' # [BUG] Segmentation fault at 0x000000081009ac38 Reproduced on FreeBSD 16-CURRENT/powerpc64le with Ruby 4.0.4. The same asm exists unchanged in 3.3 and 3.4, so they are likely affected as well. Linux ppc64le may not hit this if its rtld preserves the caller's r2 across PLT resolution, but per the ELFv2 ABI spec §2.3.1.2 a global function entered via the PLT is responsible for setting up its own r2 from r12 — relying on the resolver is non-conformant. The fix adds the standard two-instruction global entry prologue plus a `.localentry` directive of 8, matching what the compiler emits for ordinary C functions (e.g. `rb_current_ec_set` carries ``, i.e. STO_PPC64_LOCAL=3). The local entry point now lands at the original first instruction (`addi 1,1,-160`), so `coroutine_initialize` in `Context.h` continues to work unchanged — `start + 8` (which skips fiber_entry's global prologue to reach its local entry) is still correct because the offset between global and local entry is identical across all functions in this module. References: - Power Architecture 64-Bit ELF V2 ABI Specification, §2.3.1.2 "Function Calling Sequence" - STO_PPC64_LOCAL encoding: §3.3 "Symbol Values" --- coroutine/ppc64le/Context.S | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/coroutine/ppc64le/Context.S b/coroutine/ppc64le/Context.S index f7bcae2c3af400..819264c245ce9b 100644 --- a/coroutine/ppc64le/Context.S +++ b/coroutine/ppc64le/Context.S @@ -1,11 +1,18 @@ #define TOKEN_PASTE(x,y) x##y +.abiversion 2 .text .align 2 .globl PREFIXED_SYMBOL(coroutine_transfer) .type PREFIXED_SYMBOL(coroutine_transfer), @function PREFIXED_SYMBOL(coroutine_transfer): + # Global entry: set up TOC pointer (r2) from r12. + # Required by ELFv2 ABI when this function is reached via the PLT. + addis 2, 12, .TOC. - PREFIXED_SYMBOL(coroutine_transfer)@ha + addi 2, 2, .TOC. - PREFIXED_SYMBOL(coroutine_transfer)@l + .localentry PREFIXED_SYMBOL(coroutine_transfer), .-PREFIXED_SYMBOL(coroutine_transfer) + # Make space on the stack for caller registers addi 1,1,-160 From 67e14b1a0f943504ea8c9665c0db95643d2cd225 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 18 May 2026 11:05:49 +0900 Subject: [PATCH 12/13] pathname: Refine `del_trailing_separator` tests for the common cases --- test/pathname/test_pathname.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb index c5b19c8b18fbd3..8646b20de577f8 100644 --- a/test/pathname/test_pathname.rb +++ b/test/pathname/test_pathname.rb @@ -175,19 +175,19 @@ def del_trailing_separator(path) if DOSISH_UNC defassert(:del_trailing_separator, "//", "//") - defassert(:del_trailing_separator, "//a", "//a") - defassert(:del_trailing_separator, "//a", "//a/") - defassert(:del_trailing_separator, "//a", "//a//") - defassert(:del_trailing_separator, "//a/b", "//a/b") - defassert(:del_trailing_separator, "//a/b", "//a/b/") - defassert(:del_trailing_separator, "//a/b", "//a/b//") - defassert(:del_trailing_separator, "//a/b/c", "//a/b/c") - defassert(:del_trailing_separator, "//a/b/c", "//a/b/c/") - defassert(:del_trailing_separator, "//a/b/c", "//a/b/c//") else defassert(:del_trailing_separator, "/", "///") - defassert(:del_trailing_separator, "///a", "///a/") end + defassert(:del_trailing_separator, "//a", "//a") + defassert(:del_trailing_separator, "//a", "//a/") + defassert(:del_trailing_separator, "//a", "//a//") + defassert(:del_trailing_separator, "//a/b", "//a/b") + defassert(:del_trailing_separator, "//a/b", "//a/b/") + defassert(:del_trailing_separator, "//a/b", "//a/b//") + defassert(:del_trailing_separator, "//a/b/c", "//a/b/c") + defassert(:del_trailing_separator, "//a/b/c", "//a/b/c/") + defassert(:del_trailing_separator, "//a/b/c", "//a/b/c//") + defassert(:del_trailing_separator, "///a", "///a/") if DOSISH defassert(:del_trailing_separator, "a", "a\\") From 3ef48ef9c8fb3c19d64b4bcf41ed10edb0bfeecc Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 18 May 2026 11:08:25 +0900 Subject: [PATCH 13/13] pathname: Improve `absolute?` tests along with `relative?` tests Check that these two methods opposite always. --- test/pathname/test_pathname.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb index 8646b20de577f8..6354e6a9b5dd3c 100644 --- a/test/pathname/test_pathname.rb +++ b/test/pathname/test_pathname.rb @@ -260,13 +260,12 @@ def test_join assert_equal(Pathname("/foo/var"), r) end - def test_absolute - assert_equal(true, Pathname("/").absolute?) - assert_equal(false, Pathname("a").absolute?) - end - def relative?(path) - Pathname.new(path).relative? + path = Pathname.new(path) + relative = path.relative? + absolute = path.absolute? + assert_equal(!relative, absolute) + relative end defassert(:relative?, true, '') @@ -281,7 +280,7 @@ def relative?(path) defassert(:relative?, !DOSISH_DRIVE_LETTER, 'A:/') defassert(:relative?, !DOSISH_DRIVE_LETTER, 'A:/a') - if File.dirname('//') == '//' + if DOSISH_UNC defassert(:relative?, false, '//') defassert(:relative?, false, '//a') defassert(:relative?, false, '//a/')