I recently helped a friend with GitHub Actions. It is a streamlined experience that you write code and run your code without leaving GitHub. The experience is ok (documentation is confusing). I am going to walk you through a setup for running a build of Rails app.
I recommend GitHub Actions for open source usage. From 1-person startup to 5000-people Enterprise, strongly recommend Buildkite.
Example: Rails app
You create a file under .github/workflows/:filename.yml
. You can name the Action by:
name: Build
And specify when do you want GitHub to take actions:
on: [push, pull_request]
Here we want the actions to be run on push
and pull_request
events. Other events can be found here.
Define to run your jobs to on Actions Visual Environment of latest Ubuntu (You can also choose Windows or macOS) with some environment variables:
jobs:
build:
runs-on: ubuntu-latest
env:
RAILS_ENV: test
Set up for Postgres
Let’s install a PostgreSQL 12.0 database:
services:
postgres:
image: postgres:12.0
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: "postgres"
POSTGRES_DB: postgres
ports:
- 5432/tcp
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
The envs are needed otherwise you’ll hit a mysterious error: postgres service is unhealthy. The options are needed because the postgres image does not provide a healthcheck.
Steps
Checkout the project
Specify the steps under jobs
, clone your project with actions/checkout:
jobs:
...
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
fetch-depth 1
makes it faster by doing a shallow clone.
By default it looks at on:
in your workflow yaml and runs on these events.
Install softwares for previous postgres service:
- name: apt-get
run: |
sudo apt-get update -y
sudo apt-get install -y libpq-dev postgresql-client
Note the -y
option (--assume-yes
) here. To prevent the step hanging because some software may need confirmation.
Setup Redis
- name: Set up Redis
uses: shogo82148/actions-setup-redis@v1
with:
redis-version: '5.x'
Setup Ruby
The next step is to setup latest version of Ruby 2.7 (by ruby/setup-ruby, NOT official actions/setup-ruby):
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7
ruby/setup-ruby is from official ruby organization maintaining by Benoit Daloze: https://github.com/ruby/setup-ruby.
Bundler is already installed by ruby/setup-ruby. Let’s set up the cache for gems.
Cache automagically:
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7
bundler-cache: true
Cache manually:
- uses: actions/cache@v2
with:
path: vendor/bundle
key: bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby }}-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby }}-
- name: bundle install
run: |
bundle config deployment true
bundle config path vendor/bundle
bundle install --jobs 8
Setup front-end dependencies
Now is a good time to install our front-end dependencies:
- name: Get Yarn cache directory
id: yarn-cache-dir
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Cache node modules
uses: actions/cache@v1
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir.outputs.dir }}
key: ${{ runner.os }}-js-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-js-
We can reference the result from other step by id
:
${{ steps.yarn-cache-dir.outputs.dir }}
Creating database
We can then create and migrate the database:
- name: Setup database
run: bin/rails db:create db:migrate
Run! Run!
Run tests
- name: RSpec
run: bin/rspec
Run rubocop:
- name: Rubocop
run: bin/rubocop --parallel
The --parallel
option to run cops in parallel.
That’s it! You can see above full example in gist.
All in all, I feel things could be more abstract. For example, we should be able to say cache: true
to let GitHub Actions handles the Bundler cache for us:
- name: bundle install
cache: true
run: |
bundle install --jobs 4
More Actions Configurations
-
Timeout for 10 mins.
timeout-minutes: 10
(can set in jobs or steps) -
Objects like
runner
ormatrix
:${{ runner.os }}
and${{ matrix.os }}
. Can use to dynamically construct a build name -
Strategies like
fail-fast: true
ormax-parallel
in Contexts -
Conditional
if:
to trigger a step (Example)
Where to find more actions
Real-world Scenarios
The main documentations of GitHub Actions are jumping all over the places... But we as developers are good at trial and errors, so here is a list of Open Source Ruby / Rails apps that are using GitHub Actions for your reference:
- discourse: runs tests in parallel, style linters, with dependencies like PostgreSQL, Redis
- minitest-spec-rails: setup a matrix of Ruby and Rails
- rails/rails: runs rubocop to lint styles
- byebug: A daily schedule build to build and push docker image
- ruby/www.ruby-lang.org: Jekyll app runs tests with two Rubies
- asciibook: use GitHub Actions to release gem and publish docker image
Hope you find this helpful.
Til next time,
Juanito