How to publish Ruby Gems from Bitbucket Pipelines

Aidan Samuel February 27, 2019

I don't know if this is the right place for this, but I wanted to publish a solution I developed - mainly because it took me a little while to piece together, and I figure it may be useful to other people. I'm also keen to hear any feedback on the approach.

 

The problem

I have a Ruby Gem in a Bitbucket git repo, and I wanted to automate the process of testing and publishing new versions of the gem to RubyGems.org.

We've been having an awesome time rolling out Pipelines for all our Google App Engine hosted projects, but until now, our gem always had to be published manually. Could we put that into a CI/CD pipeline too?!

 

Configuring

  1. Assuming you already have a gem on RubyGems, and and you have push capabilities, you can use your own account for publishing from the pipeline, but I'd recommend setting up a second account and giving that push rights to your gem: gem owner --add automated.deployment@domain.com your_gem
  2. When you first pushed a gem to RubyGems you needed to authenticate, and this process created a file at ~/.gem/credentials which is used for all subsequent pushes. You will need to recreate this file when pushing from the pipeline (and keyboard input is not an option). To do this, you will need to extract the API key from this file.
  3. Create a Repository variable called GEM_HOST_API_KEY, paste the API key in, and click the padlock to make it encrypted and hidden (don't want it appearing in your build logs!)

 

bitbucket-pipelines.yml

image: ruby:2.6

pipelines:
pull-requests:
'**':
- step:
name: Run unit tests
caches:
- bundler
script:
- bundle install
- bundle audit check --update # <-- optional
- rake test
tags:
release-*:
- step:
name: Test and publish
caches:
- bundler
script:
- bundle install
- bundle audit check --update # <-- optional
- rake test
- mkdir -p /root/.gem
- touch /root/.gem/credentials
- chmod 0600 /root/.gem/credentials
- 'printf -- "---\n:rubygems_api_key: $GEM_HOST_API_KEY\n" > /root/.gem/credentials'
- rake build release:rubygem_push

definitions:
caches:
bundler: /usr/local/bundle

 

Explanation

This pipeline configuration defines two pipelines:

  • One that's triggered on every pull request: it will simply run the unit tests (and bundler audit)
  • The second one is run whenever a release-* tag is added, e.g. git tag release-1.3.2 && git push --tags This is the important one that publishes the gem.
  • Both pipelines use caches to speed up the build process. It makes a big difference, and the caches last a week before expiring (I think).

Key lines explained:

# Here we create the directory to store the RubyGem authentication file:
- mkdir -p /root/.gem

# We create the file and restrict access before we write sensitive data to it
- touch /root/.gem/credentials

# Also, if `credentials` is too open, RubyGems refuses to use it, much like ssh and ~/.ssh/id_rsa
- chmod 0600 /root/.gem/credentials

# Using echo is the obvious choice, but printf behaves more reliably across platforms
# The line has to be quoted and arguments must come after the --, otherwise Cthulhu is awakened
# This file should ultimately look identical to the one on your local machine.
- 'printf -- "---\n:rubygems_api_key: $GEM_HOST_API_KEY\n" > /root/.gem/credentials'


# `gem build && gem push XXX.gem` is the usual choice, but how do you know the filename to push?
# rake build && rake release:rubygem_push do the same thing, but take care of the changing filenames for you
- rake build release:rubygem_push

Notes

  1. The bundle audit check --update command will only work if you've made bundler-audit a dependency of your gem. I'd recommend adding the following to your gemspec file: spec.add_development_dependency "bundler-audit", "~> 0.6"
  2. Naming the repository variable GEM_HOST_API_KEY is not arbitrary. Unfortunately, gem push will not use environment variables which is why we're forced to create the credentials file. But, a future version of RubyGems will use environment variables, specifically one called GEM_HOST_API_KEY. In other words, if you're using version RubyGems version >= 3.0.5, you can skip the four steps required to create the credentials file.

1 comment

Marty
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
February 27, 2019

Hi Aidan,

this is great content!! Why don't you turn this into an article??

Like Linette likes this
Aidan Samuel February 27, 2019

Umm, mostly because I don't know how!

 

When I created it, I think I only had 'Ask a question' and 'Start a discussion' as options.

Linette
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
March 3, 2019

Hi Aidan, thanks so much for sharing!

I'm compiling some examples for our documentation, and I think this might be a useful link to add to them. Would that be ok?

Like Marty likes this
Aidan Samuel March 3, 2019

Hi Linette, no problem at all! Feel free to use this however you like.

Linette
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
March 3, 2019

Yay!

Comment

Log in or Sign up to comment
TAGS
AUG Leaders

Atlassian Community Events