Set Up GitLab CI for Rails Applications

Mirko Akov
Apr 25, 2018 · 4 min read

First steps

We will use the official Ruby docker image as a start and add PostgreSQL service. It is good idea to cache our dependencies as this will remove the need to download them if there are no changes.

image: ruby:2.4.3

cache:
  paths:
    - vendor/bundle
    - node_modules

services:
  - postgres:10.1

variables:
  BUNDLE_PATH: vendor/bundle
  DISABLE_SPRING: 1
  DB_HOST: postgres

Before script

The before_script is the place, where you can put your setup steps that will run before every stage. Here we will put the installation of additional software that we need and the initial setup of the project, like running migration, compiling assets, etc.

before_script:
  # Install node and some other deps
  - curl -sL https://deb.nodesource.com/setup_8.x | bash -
  - apt-get update -yq
  - apt-get install -y apt-transport-https build-essential cmake nodejs python-software-properties software-properties-common unzip
  
  # Install yarn
  - wget -q -O - https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
  - echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list
  - apt-get update -yq
  - apt-get install -y yarn# Project setup
  # Check if the dependencies are ok, if not install what is missing
  - bundle check || bundle install --jobs $(nproc)
  - yarn install# database.yml.ci file contains the configurations for the CI
  # server, so let's copy to the configuration file
  - cp config/database.yml.ci config/database.yml- bundle exec rails db:create RAILS_ENV=test
  - bundle exec rails db:schema:load RAILS_ENV=test
  - bundle exec webpack
default: &default
  adapter: postgresql
  encoding: unicode
  username: postgres
  password: postgres
  host: <%= ENV['DB_HOST'] %>
  pool: 5
  database: ci_db

Defining the stages

The stages block of the config file defines all the stages of the build process of the app. We, at Evermore, usually go with three: test, lint and deploy.

stages:
  - test
  - lint
  - deploy

Test stage

GitLab allows to have as many task per stage as you like, which makes perfect sense for our unit and system tests. But to be able to run tests using headless Chrome, we need to install it and install chromedriver as well. As this is kinda expensive task, we only do it when we actually need it.

Tests:
  stage: test
  script:
    - bundle exec rails test -dSystem Tests:
  stage: test
  script:
    - ./bin/setup_chrome
    - bundle exec rails test:system
#!/bin/bashset -e# Install Chrome
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list
apt-get update -yqqq
apt-get install -y google-chrome-stable > /dev/null 2>&1
sed -i 's/"$@"/--no-sandbox "$@"/g' /opt/google/chrome/google-chrome# Install chromedriver
wget -O /tmp/chromedriver.zip http://chromedriver.storage.googleapis.com/2.35/chromedriver_linux64.zip
unzip /tmp/chromedriver.zip chromedriver -d /usr/bin/
rm /tmp/chromedriver.zip
chmod ugo+rx /usr/bin/chromedriver

Lint stage

At Evermore we have a coding style guide and one thing that we like to do is to automate the review process of lint violations. It is always a better idea if they come from a machine than a person :) To do this we use this awesome gem called pronto. It has various runners and rubocop is one of them.

Pronto:
  stage: lint
  allow_failure: true
  except:
    - master
  script:
    - bundle exec pronto run -f gitlab -c origin/master

Deploy stage

Deploy stage can be different depending on the needs. We usually use Heroku and have two environments — staging and production. We deploy to staging on successful build on branches (pull requests), so it is easy to review and deploy the master branch to production.

Deploy Production:
  stage: deploy
  retry: 2
  only:
    - master
  script:
    - ./bin/setup_heroku
    - dpl --provider=heroku --app=awesome-app --api-key=$HEROKU_API_KEY
    - heroku run rake db:migrate --exit-code --app awesome-appDeploy Staging:
  stage: deploy
  allow_failure: true
  retry: 2
  except:
    - master
  script:
    - ./bin/setup_heroku
    - dpl --provider=heroku --app=awesome-app-staging --api-key=$HEROKU_API_KEY
    - heroku run rake db:migrate --exit-code --app awesome-app-staging
    - heroku run rake db:seed --exit-code --app awesome-app-staging
#!/bin/bashset -eapt-get update -yq
apt-get install apt-transport-https software-properties-common python-software-properties -y
add-apt-repository "deb https://cli-assets.heroku.com/branches/stable/apt ./"
curl -L https://cli-assets.heroku.com/apt/release.key | apt-key add -
apt-get update -yq
apt-get install heroku -y
gem install dpl

And we are done

Of course, we can go a step further and create a custom docker image. This way we will cache some of the setup steps and speed up the builds. Strangely enough, I was not able to find an image that fits my needs for this specific project, so I created one — https://hub.docker.com/r/mupkoo/rails-ci/. Give it a try if you like.

Evermore

Development consultancy, specialised in building custom web solutions and applications since 2006. Visit us at: http://weareevermore.com

Mirko Akov

Written by

Full stack developer @weareevermore #ruby #elixir #ember.js

Evermore

Evermore

Development consultancy, specialised in building custom web solutions and applications since 2006. Visit us at: http://weareevermore.com