From 13702c1f67030649d8380dc91230b7397e325025 Mon Sep 17 00:00:00 2001 From: Ben Fairless Date: Wed, 12 Aug 2026 09:55:05 +0800 Subject: [PATCH 1/3] Add automated gem release via RubyGems trusted publishing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a release workflow that publishes morph-cli to rubygems.org whenever a version bump lands on main, using RubyGems trusted publishing (OIDC) via rubygems/release-gem — no long-lived API keys. - Triggers on pushes to main that touch lib/morph-cli/version.rb (plus manual workflow_dispatch) - A check job compares MorphCLI::VERSION against the versions already on rubygems.org and skips publishing when the version is already released, guarding against republishing - The release job runs in the 'rubygems' environment with id-token and contents write permissions; rubygems/release-gem runs 'rake release', which builds the gem, pushes the vX.Y.Z tag and publishes via trusted publishing - Document exact version-bump steps in a new 'Releasing a new version' README section, along with the one-time trusted publisher setup a gem owner must perform on rubygems.org (repository openaustralia/morph-cli, workflow release.yml, environment rubygems) and the matching GitHub environment The already-published guard was verified against the live rubygems.org API for both the published (0.2.5) and unpublished (0.3.0) cases. Assisted-by: opencode/anthropic.claude-fable-5 Signed-off-by: Ben Fairless --- .github/workflows/release.yml | 70 +++++++++++++++++++++++++++++++++++ README.md | 38 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0c753fd --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,70 @@ +name: Release + +# Publishes the gem to rubygems.org via trusted publishing (OIDC) whenever a +# version bump lands on main. The check job guards against republishing a +# version that is already on rubygems.org, so merges that don't change the +# version (or reverts) are a no-op. +# +# One-time setup required on rubygems.org before this workflow can publish — +# see "Releasing a new version" in the README. + +on: + push: + branches: [main] + paths: + - "lib/morph-cli/version.rb" + workflow_dispatch: + +permissions: {} + +concurrency: + group: release + cancel-in-progress: false + +jobs: + check: + name: Check if version needs publishing + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + release: ${{ steps.check.outputs.release }} + version: ${{ steps.check.outputs.version }} + steps: + - uses: actions/checkout@v5 + with: + persist-credentials: false + - name: Compare version against rubygems.org + id: check + run: | + version=$(ruby -Ilib -r morph-cli/version -e 'puts MorphCLI::VERSION') + echo "version=$version" >> "$GITHUB_OUTPUT" + published=$(curl -fsS "https://rubygems.org/api/v1/versions/morph-cli.json" | jq -r '.[].number') + if echo "$published" | grep -Fxq "$version"; then + echo "morph-cli $version is already on rubygems.org; nothing to do" + echo "release=false" >> "$GITHUB_OUTPUT" + else + echo "morph-cli $version is not yet published; releasing" + echo "release=true" >> "$GITHUB_OUTPUT" + fi + + release: + name: Publish to RubyGems + needs: check + if: needs.check.outputs.release == 'true' + runs-on: ubuntu-latest + environment: rubygems + permissions: + id-token: write # mandatory for RubyGems trusted publishing + contents: write # required for `rake release` to push the release tag + steps: + - uses: actions/checkout@v5 + with: + persist-credentials: false + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.4" + bundler-cache: true + - name: Release gem v${{ needs.check.outputs.version }} + uses: rubygems/release-gem@v1 diff --git a/README.md b/README.md index 1130113..ce9631b 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,44 @@ and our contributor licence agreement. 4. Push to the branch (`git push origin feature/my-new-feature`) 5. Create a new pull request +## Releasing a new version + +Releases are published to [rubygems.org](https://rubygems.org/gems/morph-cli) +automatically by the [release workflow](.github/workflows/release.yml) using +[RubyGems trusted publishing](https://guides.rubygems.org/trusted-publishing/) +— no API keys involved. A version bump merged to `main` results in a published +gem. To release: + +1. Create a branch off `main`. +2. Bump the version number in `lib/morph-cli/version.rb`, following + [Semantic Versioning](https://semver.org). +3. Move the relevant entries in `CHANGELOG.md` from "Unreleased" into a new + section for the version. +4. Commit (signed off), open a pull request and get it reviewed and merged as + usual. +5. On merge to `main`, the release workflow checks whether that version is + already on rubygems.org. If it isn't, it builds the gem, creates and pushes + the `vX.Y.Z` git tag, and publishes the gem. If the version is already + published the workflow does nothing, so it is safe to merge non-version + changes at any time. + +### One-time trusted publishing setup (gem owners) + +Before the release workflow can publish, a gem owner needs to configure a +trusted publisher for morph-cli on rubygems.org (once only): + +1. Sign in to rubygems.org and go to the + [morph-cli trusted publishers settings](https://rubygems.org/gems/morph-cli/trusted_publishers) + (Gem page → Ownership → Trusted publishers). +2. Create a new **GitHub Actions** trusted publisher with: + - **Repository owner:** `openaustralia` + - **Repository name:** `morph-cli` + - **Workflow filename:** `release.yml` + - **Environment:** `rubygems` +3. In this GitHub repository, create the matching environment: Settings → + Environments → New environment → name it `rubygems`. Optionally add + required reviewers there to gate publishing behind a manual approval. + ## License The gem is available as open source under the terms of the From 8c4605d35646268001b524c150fece4d6e25e8be Mon Sep 17 00:00:00 2001 From: Ben Fairless Date: Wed, 12 Aug 2026 10:04:19 +0800 Subject: [PATCH 2/3] Handle never-published gem in release version check Addresses review feedback: curl -f exits non-zero on the 404 the rubygems.org versions API returns for a gem with no published versions, which would have failed the check job and blocked a first release. Treat that case as 'no versions published' and fall through to releasing. rubygems.org rejecting duplicate pushes remains the backstop if the check ever fails open. Assisted-by: opencode/anthropic.claude-fable-5 Signed-off-by: Ben Fairless --- .github/workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0c753fd..402dcee 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,7 +39,10 @@ jobs: run: | version=$(ruby -Ilib -r morph-cli/version -e 'puts MorphCLI::VERSION') echo "version=$version" >> "$GITHUB_OUTPUT" - published=$(curl -fsS "https://rubygems.org/api/v1/versions/morph-cli.json" | jq -r '.[].number') + # A 404 means the gem has never been published; treat that as "no + # versions published". Duplicate pushes are also rejected by + # rubygems.org itself, so this check failing open is still safe. + published=$(curl -fsS "https://rubygems.org/api/v1/versions/morph-cli.json" | jq -r '.[].number' || true) if echo "$published" | grep -Fxq "$version"; then echo "morph-cli $version is already on rubygems.org; nothing to do" echo "release=false" >> "$GITHUB_OUTPUT" From f05e6962b19e8dc25ff1a1d4f2784d7b799e0dcf Mon Sep 17 00:00:00 2001 From: Ben Fairless Date: Wed, 12 Aug 2026 11:50:53 +0800 Subject: [PATCH 3/3] Use Ruby 3.2 in release workflow Match the Ruby 3.2.2 baseline pinned for Morph.io compatibility. Assisted-by: Claude Code:anthropic.claude-fable-5 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 402dcee..11d5172 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: "3.4" + ruby-version: "3.2" bundler-cache: true - name: Release gem v${{ needs.check.outputs.version }} uses: rubygems/release-gem@v1