Do you know the CI/CD initiative?

Applying these concepts using GitLab CI/CD tool

Nowadays, the DevOps culture is present in many projects and companies of all sizes. The main concepts of this culture are Continuous Integration and Continuous Delivery/Deployment (CI/CD), which consists of a method for frequent delivery of apps to customers using automation for the main stages of app development. CI/CD is a strongly recommended solution to the integration of new code, as problems can occur with operations and development teams (the infamous "integration hell").

Let's describe CI/CD's main concepts:

Continuous Integration

Continuous Integration or "CI" is about regularly doing specific and important software development steps after new code is written (build, test, and merge) in an automated way on a shared repository. CI is a solution for the problem of branch conflicts on an active codebase.

Continuous Delivery

There are two concepts tied to the CD part of CI/CD. The first, Continuous Delivery, is about being able to send changes that will be tested and uploaded to a shared repository and can later be deployed to any environment by an operations team. So it's a solution to the problem of poor visibility and communication between dev and business teams and ensures minimal effort to deploy new code.

Continuous Deployment

Finally, the second concept consists of automatically releasing new changes from the repository to the production environment, where it is usable by stakeholders/customers. It solves the problem of overloading operations teams with manual processes that slow down app delivery.


GitLab CI/CD

In this article, we will use the GitLab CI/CD tool to apply all the concepts described above. It takes care of catching bugs and errors early in the development cycle, ensuring that all the code deployed to the production environment complies with the code standards that you and your team established for your app/project. It can handle and automatize the steps of building, testing, deploying, and monitoring the code/app.

First of all, the usage of this tool starts with writing a setup file, named "gitlab-ci.yml" that describes all the steps or stages that our REST API example app needs to pass. This API consists of a simple app written in Ruby using the Sinatra library to help to define a couple of HTTP REST routes.

First step: we need to define the stages. For this example, we have:

  • Security: consists of ensuring that app dependencies versions are all correct and if vulnerabilities exist;
  • Test: consists of running the automatized tests;
  • Deploy: consists of sending the most recent app changes to staging and production envs. In this example, I choose Heroku to make our app available.
    - security
    - test
    - deploy

Now, we define a default job that will be reused at the security and test stages.

    image: ruby:2.7
        RAILS_ENV: test

            - "vendor/bundle"

        - apt-get update -yy && apt-get install build-essential libpq-dev -y
        - gem i bundler
        - bundle install --binstubs -j $(nproc)
        - bundle config set path 'vendor/bundle'
        - bundle update

        - merge_requests
        - main

This job is responsible to run a ruby docker image with env variables, defining a path to save cache (cache usage helps to reuse artifacts that have already been downloaded), and running commands to install required libs and app dependencies. Those are only executed for the merge request and integration operations of the main branch.

The security stage takes care of finding vulnerabilities in dependencies. For that, we shall write the following instructions:

    extends: .default_job
    stage: security
        - gem install brakeman
        - gem install bundler-audit
        - bundle audit --update
        - brakeman -z -q
        - bundle audit

Now, the test stage is responsible for running the test suite written for your application. In our case, we just need to run the command bundle exec rspec spec.

    extends: .default_job
    stage: test
        - bundle exec rspec spec

Finally, the last steps are deploying to staging and production. Both steps extend the deployment_job. This job is executed only on the main branch and consists of installing required dependencies to deploy to Heroku. For this, we will use dpl, a command-line tool to deploy code, html, packages, or build artifacts to various service providers.

    image: ruby:latest

        - apt-get update -qy
        - apt-get install -y ruby-dev
        - curl | sh
        - gem install dpl

        - main

Also, we need to set the env variables that will allow us to auth to Heroku and send our example app to staging and production environments. The variables are:

  • HEROKU_API_KEY: Token for authentication
  • HEROKU_APP_STAGING: staging environment app name
  • HEROKU_APP_PRODUCTION: production environment app name

Now, defining our steps for staging and production environment is easy. We just need to extend .deployment_job and execute the command dpl --provider=heroku --app=$HEROKU_APP_[STAGING/PRODUCTION] --api-key=$HEROKU_API_KEY

The production and staging sections are very similar, the only difference being the keyword when that allows us to send the new version of our application manually.

    extends: .deployment_job
    stage: deploy
        - dpl --provider=heroku --app=$HEROKU_APP_STAGING --api-key=$HEROKU_API_KEY

    extends: .deployment_job
    stage: deploy
    when: manual
        - dpl --provider=heroku --app=$HEROKU_APP_PRODUCTION --api-key=$HEROKU_API_KEY

And that's it! We have a CI/CD process configured!


We want to work with you! Check out our "What We Do" page.