Publish a Ruby gem
Naming
In general, the gem name should be the same as the thing you require when using
it. This means using _
, and not -
to separate multi-word gem names. See the
name your gem guide for more detailed
guidance.
Also see the general policy on naming.
Versioning
Follow the guidelines on semver.org for assigning version numbers.
Versions should only be changed in a commit of their own, in a pull request of their own.
This alerts team members to the new version and allows for last-minute scrutiny before the new version is released. Also, by raising a separate pull request, we avoid version number conflicts between feature branches.
File layout
We should follow the scheme used by Bundler when creating gems (see this railscast).
Note
- The version is stored in a file by itself in
lib/<gem_name>/version.rb
.- The Gemfile references the gemspec for gem dependencies. All gem dependencies should be specified in the gemspec.
- The Gemfile.lock is never committed (it should be in the
.gitignore
file).
Releasing gem versions
We use GitHub Actions as a means to create new releases of gems. We have
a workflow that can be imported into apps and an
organisation GitHub secret, ALPHAGOV_RUBYGEMS_API_KEY
, that
can be used to authenticate with RubyGems.
To utilise these you will need to ask a GOV.UK GitHub Owner to grant your repository access to the secret and apply the workflow to your GitHub Action (example).
Should you have an unconventional need in building your gem for deployment, for example govuk_publishing_components requires an npm build step, you should not use the shared workflow, or adapt it for your edge case, and instead add an app specific workflow to release the gem (example).
Ruby version compatibility
Our policy is that our Ruby gems are compatible with all currently supported minor versions of Ruby. For example, in November 2022, There were supported Ruby releases of 2.7, 3.0 and 3.1, thus we expected gems to be compatible with each of those and be tested against them (there is documentation on the approach to test them).
We specify the minimum Ruby version supported in the gemspec file
and expect the .ruby-version
to match that version. For example, if Ruby 2.7
is the oldest supported minor version, we expect gems to require Ruby 2.7 or
greater and the .ruby-version
file to reference the most recent Ruby 2.7
release (which in November 2022 was 2.7.6).
When new minor versions of Ruby are released (typically each Christmas) we update gems to test against the new version. For example, when Ruby 3.2 was released, our gem test matrices were expanded to test against Ruby 3.2.
When Ruby versions reach end-of-life (typically April) we update gems
to drop support for that Ruby version and update the .ruby-version
files to
the next supported version. For example, when Ruby 2.7 reached end of life, we dropped 2.7
from the test matrices and we updated the .ruby-version
file to be the most
recent release of the 3.0 branch, which in March 2023 was 3.0.5
(see example).
We should then release the updated gem as a minor version. In the past, our policy was to release as a major version, but this was superfluous given that the change is low risk (Dependabot shouldn’t even raise a PR unless the upstream app is on a supported Ruby version). It also limited our ability to benefit from the automated workflows agreed in RFC-156, which apply only to patch and minor version upgrades. Note that patch versions are generally reserved only for bug fixes.
Manually publishing gems from the CLI
Sometimes you may be required to publish a gem outside of Jenkins, if you need
to release a patch or minor version below the current latest major version.
For example, say you have a gem at version 1.0.0 and later release a major breaking
change as version 2.0.0. If you still need to support apps using v1, and you want
to apply a bugfix as a patch version 1.0.1, you would not be able to deploy off
the main
branch as this will include all of the breaking v2 changes.
To manually publish from the command line:
- Get your branch ready: checkout the tag of the previous major version (e.g.
git checkout v1.0.0
) and then create a branch pointing at this commit (git checkout -b bump-version-to-1.0.1
). - Get your code ready: make your code changes, including adding a CHANGELOG entry and bumping the version number.
- Commit.
- Manually follow the steps of the
publishGem
function in govuk-jenkinslib i.e. check your new version doesn’t already exist, build & publish the gem, make and push a git tag. The RubyGems credentials can be found in govuk-secrets. - Tidy up: open a pull request for this branch against
main
. There will be merge conflicts, which you’ll have to manually resolve before you can merge. Do this without rebasing, as we want to keep the originalmain
history. You’ll end up with a meta commit likeMerge pull request #1 from username/branch-name
, but the full history of bothmain
and your branch will be intact.
Clearing the gemstash cache
When a new gem version is released, it may not be available immediately on Jenkins.
This is due to gemstash, our gem mirror. It caches versions for up to 30
minutes. To force it to clear, restart gemstash
on the apt
machine in the
relevant environment, e.g. for integration:
gds govuk c ssh -e integration apt 'sudo docker restart gemstash'
To clear the cache for the CI Jenkins instance, run this in integration.