diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1e6c770 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,86 @@ +# Publishes the gem to RubyGems.org via OIDC trusted publishing whenever +# lib/unit-ruby/version.rb changes on main. No long-lived API key is stored: +# configure-rubygems-credentials exchanges a short-lived GitHub OIDC token for +# RubyGems credentials by assuming the OIDC API key role configured on +# rubygems.org. `bundle exec rake release` then builds the gem, creates and +# pushes the vX.Y.Z tag, and publishes to RubyGems.org. +name: Release + +on: + push: + branches: [main] + paths: + - lib/unit-ruby/version.rb + +# Never run two releases at once. +concurrency: + group: release + cancel-in-progress: false + +jobs: + push: + name: Push gem to RubyGems.org + runs-on: ubuntu-latest + permissions: + contents: write # push the version tag + id-token: write # mint the OIDC token for trusted publishing + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + fetch-depth: 0 + fetch-tags: true + persist-credentials: false + - name: Set up Ruby + uses: ruby/setup-ruby@12fd324f1d0b43274fdc8130f6980590a667c455 # v1.312.0 + with: + ruby-version: "3.4" + bundler-cache: true + - name: Read and validate gem version + id: version + run: | + version="$(ruby -e 'require "./lib/unit-ruby/version"; print Unit::VERSION')" + # Enforce semver before the value is used anywhere else, so a crafted + # version string can't smuggle shell metacharacters into later steps. + if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then + echo "Invalid version format: $version" >&2 + exit 1 + fi + echo "version=$version" >> "$GITHUB_OUTPUT" + - name: Check whether this version is already tagged + id: tag + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + if git rev-parse "v$VERSION" >/dev/null 2>&1; then + echo "Version v$VERSION already released; skipping." + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + - name: Configure RubyGems credentials + if: steps.tag.outputs.exists == 'false' + uses: rubygems/configure-rubygems-credentials@762a4b77c3300434bb57c7ce80b20e36231927aa # v2.0.0 + with: + role-to-assume: rg_oidc_akr_54hgswsa4knajxk89skj + - name: Set git identity and push URL + if: steps.tag.outputs.exists == 'false' + run: | + # Attribute the release tag to the last committer on HEAD. + git config --global user.email "$(git log -1 --pretty=format:'%ae')" + git config --global user.name "$(git log -1 --pretty=format:'%an')" + # Restore push credentials (checkout ran with persist-credentials: false) + # so `rake release` can push the tag. + git remote set-url origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY" + - name: Release + if: steps.tag.outputs.exists == 'false' + run: bundle exec rake release + - name: Wait for release to propagate + if: steps.tag.outputs.exists == 'false' + run: | + gem install rubygems-await + gem_tuple="$(ruby -rbundler/setup -rbundler -e ' + spec = Bundler.definition.specs.find {|s| s.name == ARGV[0] } + raise "No spec for #{ARGV[0]}" unless spec + print [spec.name, spec.version, spec.platform].join(":") + ' "unit-ruby")" + gem await "${gem_tuple}" diff --git a/CHANGELOG.md b/CHANGELOG.md index d7f4ac9..3c185e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog -## 0.1.0 +## 1.1.1 -Initial release \ No newline at end of file +- Publish releases automatically to RubyGems.org via OIDC trusted publishing when `version.rb` changes on `main`. + +## 1.1.0 + +- Support Rails 8 / Active Support 8 (widen the `activesupport` dependency to `>= 7.0, < 9`). +- Stop committing `Gemfile.lock` so applications resolve dependencies against the gemspec + +## 1.0.1 + +- Resolve dependency vulnerabilities by bumping vulnerable transitive dependencies. + +## 1.0.0 + +- First stable release, promoting the 0.12.x API to 1.0 with no breaking changes. +- Expanded README documentation and cleaned up the gemspec and CI configuration. + +## 0.1.0 + +Initial release diff --git a/README.md b/README.md index 71f3af0..98bb7a2 100644 --- a/README.md +++ b/README.md @@ -129,15 +129,13 @@ $ bundle exec rake install ## Releasing -In order to publish this gem on [RubyGems.org](https://rubygems.org/), you will need to create a RubyGems account. Note that multi-factor authentication must be set up on your account before a gem can be published. +Releases are minted automatically. When a change to `lib/unit-ruby/version.rb` lands on `main`, the [Release workflow](.github/workflows/release.yml) builds the gem, tags the commit `vX.Y.Z`, and publishes to [RubyGems.org](https://rubygems.org/gems/unit-ruby/). No manual `gem push` is required, and no RubyGems API key is stored — the workflow authenticates via [OIDC trusted publishing](https://guides.rubygems.org/trusted-publishing/). -Once your account is set up, the following operations will facilitate publishing the latest version of the gem: +To cut a release: -1. After making your changes to the gem, update the version number in `version.rb` and open a PR for review using semantic versioning -2. Ensure that PR request is approved by appropriate member(s) of the engineering team before publishing the gem in the below steps -3. Run `gem build unit-ruby`. This will build a version of the gem called `unit-ruby-[gem version number].gem` -4. Push this latest version of the gem to RubyGems.org by calling `gem push unit-ruby-[gem version number].gem` -5. Merge PR into `main` branch +1. Make your changes, then bump the version in `lib/unit-ruby/version.rb` following [semantic versioning](https://semver.org/) and add an entry to `CHANGELOG.md`. +2. Open a PR and get it approved by the appropriate member(s) of the engineering team. +3. Merge to `main`. The Release workflow publishes the new version automatically. If the version is unchanged, no release is cut. ## Contributing diff --git a/lib/unit-ruby/version.rb b/lib/unit-ruby/version.rb index f831f26..cd69ddc 100644 --- a/lib/unit-ruby/version.rb +++ b/lib/unit-ruby/version.rb @@ -1,3 +1,3 @@ module Unit - VERSION = '1.1.0' + VERSION = '1.1.1' end diff --git a/spec/version_spec.rb b/spec/version_spec.rb index 7f4e29c..9e12a83 100644 --- a/spec/version_spec.rb +++ b/spec/version_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Unit do it 'returns the correct version' do - expect(Unit::VERSION).to eq '1.1.0' + expect(Unit::VERSION).to eq '1.1.1' end end