diff --git a/.github/workflows/build-gems.yml b/.github/workflows/build-gems.yml index b703e614..0fa914df 100644 --- a/.github/workflows/build-gems.yml +++ b/.github/workflows/build-gems.yml @@ -13,6 +13,10 @@ concurrency: jobs: ci-data: uses: ./.github/workflows/native-gem-matrix.yml + with: + # Release branches build the platforms and Ruby versions pinned in their + # snapshot, if they have one. + snapshot: ${{ startsWith(github.ref_name, 'release-') }} native: name: Build native gems @@ -27,7 +31,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: - ruby-version: "4.0" + ruby-version: ${{ fromJSON(needs.ci-data.outputs.result).latest-ruby-version }} - uses: oxidize-rb/actions/cross-gem@v1 id: cross-gem @@ -43,8 +47,10 @@ jobs: smoke-test: name: Smoke test native gem (${{ matrix.ruby-platform }}) - needs: native + needs: [ci-data, native] runs-on: ${{ matrix.os }} + env: + BUILT: ${{ contains(fromJSON(needs.ci-data.outputs.result).supported-ruby-platforms, matrix.ruby-platform) }} strategy: fail-fast: false matrix: @@ -62,16 +68,23 @@ jobs: - ruby-platform: arm64-darwin os: macos-15 steps: + - name: Skip platforms that aren't built + if: env.BUILT != 'true' + run: echo "::notice::${{ matrix.ruby-platform }} isn't built for this ref, skipping" + - uses: ruby/setup-ruby@v1 + if: env.BUILT == 'true' with: - ruby-version: "4.0" + ruby-version: ${{ fromJSON(needs.ci-data.outputs.result).latest-ruby-version }} - uses: actions/download-artifact@v8 + if: env.BUILT == 'true' with: name: cross-gem-${{ matrix.ruby-platform }} path: pkg/ - name: Smoke gem install + if: env.BUILT == 'true' shell: bash run: | gem install pkg/wasmtime-*.gem --verbose diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dbf4456b..0fa6649e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,17 +14,11 @@ env: jobs: ci-data: - runs-on: ubuntu-latest - outputs: - result: ${{ steps.fetch.outputs.result }} - steps: - - id: fetch - uses: oxidize-rb/actions/fetch-ci-data@1.4.3 - with: - stable-ruby-versions: | - # See https://github.com/bytecodealliance/wasmtime-rb/issues/286 - # for details. - exclude: [head] + uses: ./.github/workflows/native-gem-matrix.yml + with: + # Release branches test the Ruby versions pinned in their snapshot, if + # they have one. + snapshot: ${{ startsWith(github.base_ref || github.ref_name, 'release-') }} ci: runs-on: ${{ matrix.os }} diff --git a/.github/workflows/native-gem-matrix.yml b/.github/workflows/native-gem-matrix.yml index 3d2aa7b4..28fbbb55 100644 --- a/.github/workflows/native-gem-matrix.yml +++ b/.github/workflows/native-gem-matrix.yml @@ -1,18 +1,46 @@ --- name: Native gem matrix +# Resolves the Ruby platforms to build native gems for and the Ruby versions to +# build and test against. +# +# By default, these are the live lists from oxidize-rb, which change as Ruby +# versions and platforms are added or dropped. Release branches and tags pin +# them instead in `.github/release-matrix.json`, so that a patch release builds +# the same gems as the release before it. release-bump.yml writes the file for +# every release from `main`, so tags and the release branches created from +# them have it; `main` itself keeps using the live lists: +# +# { +# "supported-ruby-platforms": ["x86_64-linux", ...], +# "stable-ruby-versions": ["3.3", "3.4", ...] +# } on: workflow_call: + inputs: + snapshot: + description: Use `.github/release-matrix.json` if `ref` has one + required: false + type: boolean + default: false + ref: + description: Ref to read the snapshot from (defaults to the ref the workflow runs on) + required: false + type: string + default: "" outputs: result: - description: Supported Ruby platforms and stable Ruby versions + description: >- + Supported Ruby platforms (`supported-ruby-platforms`), stable Ruby + versions (`stable-ruby-versions`) and the latest of them + (`latest-ruby-version`) value: ${{ jobs.resolve.outputs.result }} jobs: resolve: runs-on: ubuntu-latest outputs: - result: ${{ steps.fetch.outputs.result }} + result: ${{ steps.resolve.outputs.result }} steps: - id: fetch uses: oxidize-rb/actions/fetch-ci-data@1.4.3 @@ -24,5 +52,33 @@ jobs: # 3.0 is deprecated as stable ruby version according to: # https://github.com/oxidize-rb/actions/blob/main/fetch-ci-data/evaluate.rb#L54 exclude: [arm-linux, arm-linux-musl, x64-mingw32] + # See https://github.com/bytecodealliance/wasmtime-rb/issues/286 for + # why `head` is excluded. stable-ruby-versions: | exclude: [head] + + - uses: actions/checkout@v7 + if: inputs.snapshot + with: + ref: ${{ inputs.ref }} + sparse-checkout: .github/release-matrix.json + sparse-checkout-cone-mode: false + + - id: resolve + env: + LIVE: ${{ steps.fetch.outputs.result }} + SNAPSHOT: ${{ inputs.snapshot }} + run: | + result=$LIVE + file=.github/release-matrix.json + if [ "$SNAPSHOT" = true ] && [ -f "$file" ]; then + if ! jq -e '(.["supported-ruby-platforms"] | type == "array") and (.["stable-ruby-versions"] | type == "array")' "$file" >/dev/null; then + echo "::error::$file must have supported-ruby-platforms and stable-ruby-versions arrays" + exit 1 + fi + echo "::notice::Using the platforms and Ruby versions in $file" + result=$(jq -c --slurpfile snapshot "$file" '. + $snapshot[0]' <<<"$result") + fi + result=$(jq -c '.["latest-ruby-version"] = (.["stable-ruby-versions"] | max_by(split(".") | map(tonumber)))' <<<"$result") + echo "$result" | jq . + echo "result=$result" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/release-bump.yml b/.github/workflows/release-bump.yml index e197a7bf..137cb49c 100644 --- a/.github/workflows/release-bump.yml +++ b/.github/workflows/release-bump.yml @@ -32,8 +32,14 @@ concurrency: group: release-bump-${{ inputs.branch || 'main' }} jobs: + ci-data: + permissions: + contents: read + uses: ./.github/workflows/native-gem-matrix.yml + bump: name: Prepare release pull request + needs: ci-data runs-on: ubuntu-latest permissions: contents: write @@ -147,6 +153,19 @@ jobs: ruby "$RUNNER_TEMP/release.rb" changelog-entry "$VERSION" "$PREVIOUS" "$REPO_URL" git diff + # Releases from `main` record the platforms and Ruby versions they're + # built for, so that release branches created from their tags keep + # building the same ones (see native-gem-matrix.yml). Release branches + # keep theirs. + - name: Snapshot the platforms and Ruby versions + if: steps.version.outputs.version && env.TARGET_BRANCH == 'main' + env: + MATRIX: ${{ needs.ci-data.outputs.result }} + run: | + jq '{"supported-ruby-platforms": .["supported-ruby-platforms"], "stable-ruby-versions": .["stable-ruby-versions"]}' <<<"$MATRIX" > .github/release-matrix.json + git add .github/release-matrix.json + git diff --cached -- .github/release-matrix.json + # Pull requests opened with GITHUB_TOKEN don't run the required checks, so # the branches are only pushed, and a maintainer opens the pull requests # from the links in the run summary. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b643d180..52a22426 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,21 @@ concurrency: jobs: ci-data: uses: ./.github/workflows/native-gem-matrix.yml + with: + # This workflow always runs from `main`, but may build an older release + # branch or tag. The live lists of platforms and Ruby versions move on + # over time, and an older branch may not build for the new ones, so + # everything except `main` builds the ones pinned in its + # `.github/release-matrix.json`, if it has one. A patch release then + # ships the same gems as the release before it. + # + # The ref being built is the tag to release, else the `ref` of a dry run, + # else the ref the workflow runs on. A dry run of a release branch (e.g. + # `ref: release-48`) therefore builds exactly what its release will + # build, and a dry run of `main` uses the live lists, like `main`'s next + # release (see release-bump.yml). + snapshot: ${{ (inputs.tag || inputs.ref || github.ref_name) != 'main' }} + ref: ${{ inputs.tag || inputs.ref }} build: name: Build native gems @@ -50,7 +65,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: - ruby-version: "4.0" + ruby-version: ${{ fromJSON(needs.ci-data.outputs.result).latest-ruby-version }} - uses: oxidize-rb/actions/cross-gem@v1 id: cross-gem @@ -66,8 +81,10 @@ jobs: smoke-test: name: Smoke test native gem (${{ matrix.ruby-platform }}) - needs: build + needs: [ci-data, build] runs-on: ${{ matrix.os }} + env: + BUILT: ${{ contains(fromJSON(needs.ci-data.outputs.result).supported-ruby-platforms, matrix.ruby-platform) }} strategy: fail-fast: false matrix: @@ -85,16 +102,23 @@ jobs: - ruby-platform: arm64-darwin os: macos-15 steps: + - name: Skip platforms that aren't built + if: env.BUILT != 'true' + run: echo "::notice::${{ matrix.ruby-platform }} isn't built for this ref, skipping" + - uses: ruby/setup-ruby@v1 + if: env.BUILT == 'true' with: - ruby-version: "4.0" + ruby-version: ${{ fromJSON(needs.ci-data.outputs.result).latest-ruby-version }} - uses: actions/download-artifact@v8 + if: env.BUILT == 'true' with: name: cross-gem-${{ matrix.ruby-platform }} path: pkg/ - name: Smoke gem install + if: env.BUILT == 'true' shell: bash run: | gem install pkg/wasmtime-*.gem --verbose @@ -104,7 +128,7 @@ jobs: release: name: Release - needs: [build, smoke-test] + needs: [ci-data, build, smoke-test] runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 @@ -113,7 +137,7 @@ jobs: - uses: oxidize-rb/actions/setup-ruby-and-rust@v1 with: - ruby-version: "4.0" + ruby-version: ${{ fromJSON(needs.ci-data.outputs.result).latest-ruby-version }} bundler-cache: true cargo-cache: true cache-version: v1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c89ca992..df1733ff 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,10 +52,12 @@ pick the version following [Versioning](#versioning). 1. Open the pull request from the link in the workflow run's summary. Pull requests opened by workflows don't run the required checks, so they have to be opened by a maintainer. -1. Review the changelog and merge the pull request. This starts the - [Release tag] workflow, which tags the version and runs the release - workflow: it pushes the gem to RubyGems and creates a draft release on - GitHub. +1. Review the changelog and `.github/release-matrix.json`, and merge the pull + request. The JSON file lists the platforms and Ruby versions the gem is + built for; it pins them for later patch releases from a release branch. + Merging starts the [Release tag] workflow, which tags the version and runs + the release workflow: it pushes the gem to RubyGems and creates a draft + release on GitHub. 1. Edit the release notes if needed and publish the draft release. 1. If the release changes the supported versions, update the table in [SECURITY.md](SECURITY.md).