Codest’s good practice for building software: CircleCI

CircleCI is a very simple tool that is well-configured as a supervisor of our projects. But is the configuration itself simple? This, of course, depends on the complexity of the project. In our case (mono repo) it turned out to be more difficult than expected. 

The configuration for Ruby on Rails projects is not complicated, and the documentation accurately describes each element of the config.yml. However, I would like to focus on the circleci tools that are used to help us keep the code clean and ensure good practice.

RUBOCOP

It is likely that RuboCope needs no introduction, however, for those who are not familiar with it, it is a static Ruby code analyzer and formater. If you already use rubocop in your project, simply add CircleCI to the configuration file:

- run:
    name: Rubocop
    command: bundle exec rubocop 

ESLINT

ESLint is a tool for identifying and reporting patterns found in the ECMAScript or JavaScript code, in order to make the code more consistent and to avoid errors.

- run:
    name: Eslint
    command: npm run eslint

RSPEC

In RSpec, tests are not only scripts that verify the application code, they are also detailed explanations of how the application should behave, expressed in simple English:

- run:
    name: RSpec
    command: |
      mkdir /tmp/test-results
      TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
        circleci tests split --split-by=timings)"
      bundle exec rspec \
        --format progress \
        --format RspecJunitFormatter \
        --out /tmp/test-results/rspec.xml \
        --format progress \
        $TEST_FILES

- store_test_results:
    path: /tmp/test-results

In the case of RSpec, we save the test result in a previously created catalog /tmp/test-results in the rspec.xml file, and then using the store_test_results key we store a given catalog. Now the Insights tab will give us access to information such as the median compilation time, the time of the last compilation or the success rate. You can read more about the Insights tab here. If we want to store the rspec.xml file as an “artifact” we need to add the store_artifacts key in our configuration file.

- store_artifacts:
    path: /tmp/test-results

BRAKEMAN

Brakeman is a static analysis tool that checks Ruby on Rails applications for security vulnerabilities. By default, Brakeman will return a non-zero exit code if security warnings are detected or scan errors are encountered. Therefore, we focused only on critical errors, and the warnings were turned off.

- run:
    name: Brakeman
    command: bundle exec brakeman --no-exit-on-warn

If we also want to store the scanning result in the same way as RSpec, our configuration will look like this, and we will have access to our file in the Artifacts tab.

- run:
    name: Brakeman
    command: |
      mkdir /tmp/brakeman
      bundle exec brakeman --no-exit-on-warn -o /tmp/brakeman/output.json

- store_artifacts:
    path: /tmp/brakeman

RUBYCRITIC

RubyCritic is a gem that uses gems for static analysis, such as Reek, Flay and Flog, to provide a report about the quality of your code. The report contains an A / B / C / D / F rating, every file in our project that we want to have scanned and accurate places that need improvement, and documentation with each alert (eg: TooManyMethods). This tool acts as a consultant in the project. On the basis of the report received, the final decision on whether our code actually needs to be corrected depends on the developer. In our circleci configuration, a separate job is assigned that is responsible for preparing the report and sending a special comment with the result on github.

The basic configuration of rubycritic is no different from the previous ones.

- run:
    name: Rubycritic
    command: bundle exec rubycritic ./app -p /tmp/rubycritic -f json -f html --no-browser

- store_artifacts:
    path: /tmp/rubycritic

As standard, we run through the bundle with information on which directory we want to scan ./app, in which place we want to save the result -p /tmp/rubycritic (rubycritic automatically creates a directory in which we will store our report), in what format -f json and option –no- browser. We also use the gem circleci-coverage_reporter, which after the scan, puts a comment on github in our pull request with a link to the report and a percentage rating of the scanned files.

Ruby

In order for the above gem to work properly together with circleci, we must add it to our project and generate two keys (one of them is circleci, the second is github).

Standard installation:

  • Gemfile
    gem 'circleci-coverage_reporter'
  • Rakefile
    require 'circleci/coverage_reporter/rake_task' if ENV['CIRCLECI']
  • .config.yml
- run:
    name: Run Rubycritic
    command: bundle exec rubycritic ./app -p /tmp/rubycritic -f json -f html --no-browser

- store_artifacts:
    path: /tmp/rubycritic

- run:
    name: Rubycritic notification
    command: bundle exec rake circleci:report_coverage

Now we have to generate two keys:

COVERAGE_REPORTER_CIRCLECI_TOKEN

API token

Section ‘settings’ of our project. After choosing ‘Create Token’ change scope for ‘all’ and fill in Token label. Token to API will be generated after clicking

COVERAGE_REPORTER_VCS_TOKEN

token

Scope for key to repo

After generating the keys, we must add them to our environment variables in Settings:

environment variable

add variable

Sample configuration:

jobs:
  build:
    docker:
      - image: circleci/ruby:2.5.1-node-browsers
      environment:
        BUNDLER_VERSION: 2.0.1
        BUNDLE_PATH: /bundle
        BUNDLE_JOBS: 4
        RAILS_ENV: test
    - image: circleci/postgres:10.1-alpine
      environment:
        POSTGRES_USER: postgres
        POSTGRES_DB: example_test
        POSTGRES_PASSWORD: example
    - image: circleci/redis:4.0.8-alpine
    working_directory: ~/insights
    environment:
        TZ: Europe/Warsaw
    steps:
      - checkout
      - run:
          name: Install bundler version 2.0.1
          command: gem install bundler -v 2.0.1
      - restore_cache:
          keys:
            - v1-ruby-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-ruby-dependencies-
      - run:
          name: Bundle Install
          command: bundle check || bundle install
      - save_cache:
          key: v1-ruby-dependencies-{{ checksum "Gemfile.lock" }}
          paths:
            - vendor/bundle
      - restore_cache:
          keys:
            - v1-npm-dependencies-{{ checksum "package.json" }}
            - v1-npm-dependencies-
      - run:
          name: Npm Install
          command: npm install
      - save_cache:
          key: v1-npm-dependencies-{{ checksum "package.json" }}
          paths:
            - ~/insights/node_modules
      - run:
        name: Database setup
        command: |
          bundle exec rake db:create
          bundle exec rake db:schema:load
      - run:
          name: Rubocop
          command: bundle exec rubocop
      - run:
          name: Eslint
          command: npm run eslint
      - run:
          name: RSpec
          command: |
            mkdir /tmp/test-results
            TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
              circleci tests split --split-by=timings)"
            bundle exec rspec \
              --format progress \
              --format RspecJunitFormatter \
              --out /tmp/test-results/rspec.xml \
              --format progress \
              $TEST_FILES
      - run:
          name: Brakeman
          command: bundle exec brakeman --no-exit-on-warn
      - store_test_results:
          path: /tmp/test-results
  rubycritic:
    docker:
      - image: circleci/ruby:2.5.1-node-browsers
        environment:
          BUNDLER_VERSION: 2.0.1
          BUNDLE_PATH: /bundle
          BUNDLE_JOBS: 4
          RAILS_ENV: test
          CIRCLE_ARTIFACTS: /tmp
    working_directory: ~/insights
    steps:
      - checkout
      - run:
          name: Install bundler version 2.0.1
          command: gem install bundler -v 2.0.1
      - restore_cache:
          keys:
            - v1-rubycritic-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-rubycritic-dependencies-
      - run:
          name: Bundle Install
          command: bundle check || bundle install
      - save_cache:
          key: v1-rubycritic-dependencies-{{ checksum "Gemfile.lock" }}
          paths:
            - vendor/bundle
      - run:
          name: Run Rubycritic
          command: bundle exec rubycritic ./app -p /tmp/rubycritic -f json -f html --no-browser
      - store_artifacts:
          path: /tmp/rubycritic
      - run:
          name: Rubycritic notification
          command: bundle exec rake circleci:report_coverage

workflows:
  version: 2
  build_and_rubycritic:
    jobs:
      - build
      - rubycritic:
          requires:
            - build

Sources:

  • List of available docker images for CircleCi
  • CircleCi
  • Used gems or tools

Read more:

– Codest’s good practice for building software: project documentation

– How to write a good and quality code?

– Open-closed principle. Do I ever have to use it?

Did you like it? Share this article

Next

Let's start a project

Estimate project