If premature optimization is the root of all evil, should procrastination until the right tool arrives be divine? If so, then yours truly would be the Patron saint of pragmatic & lazy programmers everywhere. I'm no adoption curve laggard, rather more like an early majority adopter with an extended view of the x-axis 😅 and I'm here to convince you that now is the right time to reevaluate your CI/CD needs.
GitHub Actions was announced last October and I never found the time to experiment with it. Last month they announced free access for all public repositories along with a few revamped features. It looks way more intriguing and given the long Labor Day weekend it felt like a great time to learn GitHub's new CI/CD killer app with my favorite open source gem minitest-spec-rails.
⚠️ GitHub Actions is in Beta access and will be generally available in November.
The value of Continuous Integration (CI) via tools like TravisCI & CircleCI have benefits that are immediately obvious. Constant test feedback in pull requests give us the confidence to move our projects forward at a faster pace.
But what about the Continuous Delivery (CD) part of CI/CD? In a world driven by containers and microVMs, how are we to ship our applications to Cooper Netties without a good pipeline? Continuous delivery is what powers this and like others I think GitOps solutions tightly integrated with GitHub are important to helping modernizing all of our workflow needs.
Here is a quick list of things I found compelling about GitHub Actions.
Enough hype and reasoning. Let's dig into my notes on how I setup my first CI workflow with GitHub Actions.
I used their Ruby example workflow as my starting point. From there I changed the name of the workflow to ci.yml
to better reflect my intent. Note how all workflows will be in the repo's .github/workflows
directory. The defaults were simple, basically checkout, bundle, and test using their latest Ubuntu virtual environment as the workflow's runner. Commit and see what happens... woohoo, our first error!
sqlite3.h is missing. Try `brew install sqlite3,
'yum' install sqlite-devel' or 'apt-get install libsqlite3-dev'
This was easy to fix by adding sudo apt-get install libsqlite3-dev
prior to our Bundle command. After that everything turns green and we have clutched our first duos win with GitHub Actions by our side. 🎉
This is your world and you can do anything you want, so why stop there? What about testing different Ruby versions? Another common pattern requires that we make sure our gem is compatible with multiple versions of downstream gems. I often reach to Thoughtbot's appraisal gem which allows us to generate multiple Gemfile variants.
To pull this all together we are going to need to jump into GitHub Actions' support for matrix builds. We are also going to make this CI workflow feel more personalized and organized to suit our personal needs. Here is my final .github/workflows/ci.yml
workflow file.
name: CI
on: [push]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
ruby:
- '2.5.x'
- '2.6.x'
rails:
- 'rails_v5.1.x'
- 'rails_v5.2.x'
- 'rails_v6.0.x'
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Setup System
run: |
sudo apt-get install libsqlite3-dev
- name: Setup Ruby
uses: actions/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: Bundle
env:
MTSR_RAILS_VERSION: ${{ matrix.rails }}
run: |
export BUNDLE_GEMFILE="${GITHUB_WORKSPACE}/gemfiles/${MTSR_RAILS_VERSION}.gemfile"
gem uninstall -aIx bundler
gem install bundler -v 1.17.3
bundle install --jobs 4 --retry 3
- name: Test
run: bundle exec rake
A few highlights on why I landed on the specifics of this workflow above.
Renamed the top level name from Ruby
to CI
. This is what shows up when you browse all Actions in GitHub's user interface. Keep the name logical to the workflow.
Each step can have a name
attribute as well. By default the checkout action does not so I added one. Again these step names show up as headers in the log view. So make them logical to you or your team. I ended up with:
GitHub Actions has several context objects that can be used from setting environment variables to expressions. Here I am using the matrix
context but there are others like github
, job
, steps
, secrets
, and more.
Lastly, I have standardized on Bundler 1.17.3
until a few v2 kinks are worked out. Feel free to disregard that step's run line to downgrade bundler.
So thats the spin. But any new product, especially beta access, is bound to give you a little grief. I certainly hit a few while learning today. None I would consider blockers to adopting GitHub Actions in your test projects or setting your sights for its eventual release. But here are the ones I found.
When I created my workflow using the "Ruby" example, I knew I wanted it to be called "CI". However, I neglected to set the top level name
property early on and even renamed it a few times. This means I had a few names tied to the ci.yml
workflow file and I'm guessing they will be forever burned into my repo's action link history. Oh well, I'll skeert more carefully on the next project.
One nice feature of TravisCI is that they make it easy to monitor a pull requests build state when new commits are pushed. For GitHub Actions, I found myself constantly watching a build fail, clicking back to my PR, and then finding the new build links again. It would be nice if the build page detected a push to the same PR and navigated you to the new build automatically.
Providing runtime reflection to your Workflows & Actions is critical to enabling developers to build abstract and dynamic automation tools. GitHub does a great job by providing both environment variables and context objects. However, I found it hard to setup my builds BUNDLE_GEMFILE
environment variable. I tried using their join
function to do this in one step but found myself wishing that the GITHUB_WORKSPACE
environment was a property on the github
context instead. So maybe if github.workspace
was there my join
function would have worked?
The software in each virtual environment is impressive! For example at the time of this writing the Ubuntu environments have Ruby 2.3.7
, 2.4.6
, 2.5.5
, and 2.6.3
already installed. When I took a closer look at the actions/setup-ruby repository I found they basically do some shebang path switching to use one of these preinstall Ruby versions. I totally see the value in this but expect if finer grain control is needed you would use one of two solutions:
We all want to show off our workflow's status in our project's README file. After some digging I found this Shields.io GitHub issue discussion which found the magic formula to generate an SVG using GitHub Actions. How cool is this!?! Native badge support shows they are in it to win it.
https://github.com/{USER}/{REPO}/workflows/{NAME}/badge.svg
Thanks for taking the time to read my post today on GitHub Actions. Got a fever for more continuous delivery examples? Stay tuned to @CustomInkTech on Twitter for updates. Next week I'll be writing about how to use GitHub Actions to deploy your Rails applications to AWS Lambda using Lamby.
New to GitHub Actions? I found these resoruces super helpful. You will almost certainly want to bookmark the developer documentation!