🎉 I'm releasing 12 products in 12 months! If you love product, checkout my new blog workingoutloud.dev

Back to home

Automating Ruby Gem Package Releases With Github Actions

In the series so far, we have created a gem to test locally before pushing to the remote RubyGems repository.

This time, we will be setting up an automated configuration in order to automate the process of release based on conventional commits.

It will work off the code written in the previous part "Deploying Your Gem To RubyGems".

Source code can be found here.

Prerequisites

  1. Understanding how Conventional Commits works. Not much will be covered.
  2. Catch up with the previous posts on Series: Releasing your own Ruby Gem.

Getting started

We will clone the project rspec-github-actions (if you haven't been keeping up with the previous work):

$ git clone https://github.com/okeeffed/rspec-github-actions.git $ cd rspec-github-actions # Checkout the starting point $ git checkout 3-gem-deployed

At this stage, our project is now ready to continue working on.

Updating our GitHub Action for release

This post will really be a recap of the great work done on the blog post "Automating Ruby Gem Releases with GitHub Actions" at andrewm.codes.

Since that was such a straight-forward post, this will be more on the adjustments made to my own project to also automate the Ruby Gem release.

The method we are going to take makes use of Google's GitHub Action release-please-action. This action takes your commit history to help automate the package semantic version.

If you are not familiar with conventional commits, the quick summary gives a good overview of how it works.

Essentially, you prefix your git commits with a specified "type" and "optional scope" <type>[optional scope]: <description> and based on the type, it will have a different weight for the impact on the versioning.

The types fix and feat are associated with PATCH and MINOR updates respectively, while a BREAKING CHANGE or bang following the type <type>! will result in a major change.

If you look at the history of the logs I have committed for this project, you will see the following (using git log --pretty=oneline):

$ git log --pretty=oneline 0e0977aa (HEAD -> main, origin/main, origin/3-gem-deployed, 3-gem-deployed) feat: add the example folder 012bdeb7 (origin/2-gemspec, 2-gemspec) feat: add in gemspec file 3e0f797b (origin/1-rspec-with-github-actions, 1-rspec-with-github-actions) fix: README d5fd59c2 fix: remove lcov f639002d docs: add README 016d5ae7 feat: first attempt at post

Luckily, this bodes well for our GitHub Action.

Updating our GitHub Workflows

Let's make some adjustments to our .github/workflows/rspec.yml workflow:

name: Run RSpec tests on: push: branches: - "**" # matches every branch - "!main" # excludes main jobs: run-rspec-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: # Not needed with a .ruby-version file ruby-version: 2.7 # runs 'bundle install' and caches installed gems automatically bundler-cache: true - name: Run tests run: | bundle exec rspec

This will not run the RSpec tests action on the main branch, which we will set up a new action for.

Next, let's create a new workflow. Create a new file with touch .github/workflows/release.yml and then add the following code:

name: Release on: push: branches: - "main" # main only jobs: release: runs-on: ubuntu-latest steps: - uses: GoogleCloudPlatform/release-please-action@v2 id: release with: release-type: ruby package-name: contrived_math bump-minor-pre-major: true version-file: "lib/release/contrived_math/version.rb" # Checkout code if release was created - uses: actions/checkout@v2 if: ${{ steps.release.outputs.release_created }} # Setup ruby if a release was created - uses: ruby/setup-ruby@v1 with: # Not needed with a .ruby-version file ruby-version: 2.7 # runs 'bundle install' and caches installed gems automatically bundler-cache: true if: ${{ steps.release.outputs.release_created }} - name: Run tests run: | bundle exec rspec if: ${{ steps.release.outputs.release_created }} # Publish - name: publish gem run: | mkdir -p $HOME/.gem touch $HOME/.gem/credentials chmod 0600 $HOME/.gem/credentials printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials gem build *.gemspec gem push *.gem env: # Make sure to update the secret name # if yours isn't named RUBYGEMS_AUTH_TOKEN GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}" if: ${{ steps.release.outputs.release_created }}

This new action will run on main and will release our Ruby Gem if both testing and the release-action-please succeed.

This example is contrived and will only run things on the main branch, but adjust this for the release workflow you actually want in your own work.

Note: you will need to create a new RubyGems API key and add that to the repo as the secret named RUBYGEMS_AUTH_TOKEN.

Configuring our version for our gemspec file

Update contrived_math.gemspec to the following:

require_relative 'lib/release/contrived_math/version' Gem::Specification.new do |s| s.name = 'contrived_math' s.version = ContrivedMath::VERSION s.summary = 'Hello, World!' s.description = 'A simple hello world gem' s.authors = ["Dennis O'Keeffe"] s.email = 'hello@dennisokeeffe.com' s.files = ['lib/contrived_math.rb'] s.homepage = 'https://rubygems.org/gems/contrived_math' s.license = 'MIT' end

Now we will grab the version from the file lib/release/contrived_math/version.rb.

Let's make that file and add in some content now:

module ContrivedMath VERSION = '0.0.0'.freeze end

The file content comes from Google's example.

The GitHub Action at work

Assuming you already set up the secret for the RubyGems, we can push main and see the action at work.

New action running

New action running

The first time we run the action, it will note that it needs to create a release PR for us. We can see that in the action logs.

Action creating a release

Action creating a release

This release will create some release notes and a PR. The notes will followed the conventional commit types.

Release notes created

Release notes created

Created PR

Created PR

Next, we can approve and merge the PR.

PR approval

PR approval

Once merged, the bot will link the release notes for us.

PR merged and release notes added

PR merged and release notes added

The merge of the pull request will kick off another invocation of the release action. This time, the action will release the gem.

Note: The 0.1.0 release failed because I was a goose, so the following two images come from the 0.1.1 release which followed all the same previous steps after I implemented the fix.

Release action workflow running

Release action workflow running

Finally, a successful release will also show up on our live Ruby Gem.

The RubyGem release

The RubyGem release

Success! The release schedule has been automated through GitHub Actions.

Summary

Today's post demonstrated how to take our Ruby Gem repo and automate the release process based on changes noted in git history using Conventional Commits.

It is worth noting that these flows outlined today may not be appropriate to your own needs (I am only running release on the main branch will generally is not what you will want since main would be protected), but take the workflows from today and apply the changes for your own work as required.

Resources and further reading

Photo credit: pawel_czerwinski

Personal image

Dennis O'Keeffe

@dennisokeeffe92
  • Melbourne, Australia

Hi, I am a professional Software Engineer. Formerly of Culture Amp, UsabilityHub, Present Company and NightGuru.
I am currently working on Visibuild.

1,200+ PEOPLE ALREADY JOINED ❤️️

Get fresh posts + news direct to your inbox.

No spam. We only send you relevant content.

Automating Ruby Gem Package Releases With Github Actions

Introduction

Share this post