diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..11d5172 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,73 @@ +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" + # 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" + 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.2" + 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