11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CircleCI 2.1 設定サンプル(Rails + Rspec + Rubocop + Jest + ESLint + Yarn + Postgres)

Last updated at Posted at 2020-02-09

環境

  • Circle CI 2.1
  • Rails 5 + PostgresSQL 9.6 + Yarn
    • 主にCircle CIの設定のため、上記環境には大きく依存していません。

やっていること

  • テストの実行(rspecjest
    • simplecov を使って、rspec 実行結果の code coverage を CircleCI 上のARTIFACTSに残しています。
  • Linterの実行(rubocopeslint
  • セキュリティチェック(brakeman
  • デプロイ
    • developmasterブランチへマージされた時に、capistranoを使って各staging、production環境にデプロイ。
    • 結果をslackに通知。
    • capistranoの設定ファイル(config/deploy/staging.rb等)で、set :branch, ENV['BRANCH'] || 'develop'等として、ブランチの設定がされている必要があります。
  • 上記に付随する処理(ソースコード、ライブラリのインストール及びキャッシング)

出来ていないこと

  • fingerprintを環境変数で設定すべき

設定例

.circleci/config.yml
version: 2.1

orbs:
  slack: circleci/slack@3.3.0

executors:
  default:
    working_directory: ~/repo # default: ~/project
    docker:
      - image: circleci/ruby:2.6.3-node
        environment:
          # BUNDLE_PATH=/usr/local/bundle is set in the original docker image. Should be overwritten.
          # https://hub.docker.com/layers/circleci/ruby/2.6.3-node/images/sha256-371f81f654d9a2d5e3b71bbf3616ac076686358838bc123d27ba35ff6d8df321?context=explore
          BUNDLE_PATH: vendor/bundle
          RAILS_ENV: test
  extended:
    working_directory: ~/repo
    docker:
      - image: circleci/ruby:2.6.3-node
        environment:
          BUNDLE_PATH: vendor/bundle
          RAILS_ENV: test
          PGHOST: 127.0.0.1
          PGUSER: root
      - image: circleci/postgres:9.6.2-alpine
        environment:
          POSTGRES_USER: root
          POSTGRES_DB: circle-test

commands:
  restore_source_code:
    steps:
      - restore_cache:
          name: Restore source code cache
          keys:
            - v1-source-code-{{ .Environment.CIRCLE_SHA1 }}
            - v1-source-code-
  save_source_code:
    steps:
      - save_cache:
          key: v1-source-code-{{ .Environment.CIRCLE_SHA1 }}
          paths:
            - ~/repo
  restore_node_dependencies:
    steps:
      - restore_cache:
          name: Restore node dependencies cache
          keys:
            - v1-node-dependencies-{{ checksum "yarn.lock" }}
            - v1-node-dependencies-
  install_node_dependencies:
    steps:
      - run:
          name: Install node dependencies
          command: yarn install --frozen-lockfile
  save_node_dependencies:
    steps:
      - save_cache:
          name: Save node dependencies cache
          key: v1-node-dependencies-{{ checksum "yarn.lock" }}
          paths:
            - ~/repo/node_modules
  restore_bundle_dependencies:
    steps:
      - restore_cache:
          name: Restore bundle dependencies cache
          keys:
            - v1-bundle-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-bundle-dependencies-
  install_bundler:
    steps:
      - run:
          name: Install bundler
          command: |
            pwd # debug

            BUNDLER_VERSION=$(cat Gemfile.lock | tail -1 | tr -d " ")
            echo $BUNDLER_VERSION # debug
            gem install bundler:$BUNDLER_VERSION
  install_bundle_dependencies:
    steps:
      - run:
          name: Install bundle dependencies
          command: |
            pwd # debug

            bundle install --jobs=4 --retry=3
  save_bundle_dependencies:
    steps:
      - save_cache:
          key: v1-bundle-dependencies-{{ checksum "Gemfile.lock" }}
          paths:
            - ~/repo/vendor/bundle
  wait_for_db_start_up:
    steps:
      - run:
          name: Wait for db start up
          command: dockerize -wait tcp://127.0.0.1:5432 -timeout 1m
  set_up_database:
    steps:
      - run:
          name: Set up database
          command: |
            pwd # debug
            echo $(cat Gemfile.lock | tail -1 | tr -d " ") # debug

            bin/rails db:create --trace
            bin/rails db:migrate --trace
  run_rubocop:
    steps:
      - run:
          name: Run rubocop
          command: bundle exec rubocop
  run_brakeman:
    steps:
      - run:
          name: Run Brakeman
          command: bundle exec brakeman -A -w1
  run_rspec:
    steps:
      - run:
          name: Run rspec
          command: bundle exec rspec --format progress --format RspecJunitFormatter -o test_results/rspec.xml
  run_eslint:
    steps:
      - run:
          name: Run eslint
          command: |
            pwd # debug

            yarn lint --format junit -o test_results/eslint.xml
  run_jest:
    steps:
      - run:
          name: Run jest
          command: yarn test --maxWorkers=2 --testResultsProcessor="jest-junit"
          environment:
            JEST_JUNIT_OUTPUT: ~/test_results/jest.xml

jobs:
  checkout_code:
    executor: default
    steps:
      - checkout
      - save_source_code
  node_dependencies:
    executor: default
    steps:
      - restore_source_code
      - restore_node_dependencies
      - install_node_dependencies
      - save_node_dependencies
  bundle_dependencies:
    executor: default
    steps:
      - restore_source_code
      - restore_bundle_dependencies
      - install_bundler
      - install_bundle_dependencies
      - save_bundle_dependencies
  rubocop_job:
    executor: default
    steps:
      - restore_source_code
      - restore_bundle_dependencies
      - install_bundler
      - run_rubocop
  brakeman_job:
    executor: default
    steps:
      - restore_source_code
      - restore_bundle_dependencies
      - install_bundler
      - run_brakeman
  eslint_job:
    executor: default
    steps:
      - restore_source_code
      - restore_node_dependencies
      - run_eslint
  jest_job:
    executor: default
    steps:
      - restore_source_code
      - restore_node_dependencies
      - run_jest
  rspec_job:
    executor: extended
    steps:
      - restore_source_code
      - restore_bundle_dependencies
      - install_bundler
      - wait_for_db_start_up
      - set_up_database
      - run:
          name: Copy application.yml.sample
          command: cp -p config/application.yml.sample config/application.yml
      - run_rspec
      # see: https://circleci.com/docs/2.0/code-coverage/
      # You can check the code coverage at coverage/index.html on ARTIFACTS of this job on CircleCI.
      - store_artifacts:
          path: coverage
  deploy:
    executor: default
    parameters:
      env:
        type: enum
        enum: ['staging', 'production']
    steps:
      - restore_source_code
      - restore_bundle_dependencies
      - install_bundler
      - add_ssh_keys:
          fingerprints: <your_fingerprint_comes_here>
      - run:
          name: Deploy to << parameters.env >>
          command: bundle exec cap << parameters.env >> deploy
      - slack/status:
          mentions: channel

workflows:
  build_test_and_deploy:
    jobs:
      - checkout_code
      - node_dependencies:
          requires:
            - checkout_code
      - bundle_dependencies:
          requires:
            - checkout_code
      - rubocop_job:
          requires:
            - bundle_dependencies
      - brakeman_job:
          requires:
            - bundle_dependencies
      - eslint_job:
          requires:
            - node_dependencies
      - jest_job:
          requires:
            - node_dependencies
      - rspec_job:
          requires:
            - bundle_dependencies
      - deploy_to_staging:
          name: Deploy to staging
          env: staging
          requires:
            - rubocop_job
            - brakeman_job
            - eslint_job
            - jest_job
            - rspec_job
          filters:
            branches:
              only: develop
      - deploy_to_production:
          name: Deploy to production
          env: production
          requires:
            - rubocop_job
            - brakeman_job            - 
            - eslint_job
            - jest_job
            - rspec_job
          filters:
            branches:
              only: master

ポイント

  • こちらをどうぞ

  • ソースコードもキャッシュしていますが、常にキャッシュした方が速いとは限りません。

    とはいえ、ソースコードのキャッシュの有無によってビルド速度が向上するかどうかは検証した方が良い場合もあります。例えば restore_cache するより git clone を実行する方が高速な場合も多々あります。
    https://circleci.com/docs/ja/2.0/caching/#%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5

  • ベースのDockerイメージ(circleci/ruby:2.6.3-node)で、BUNDLE_PATHが設定されているので、BUNDLE_PATH: vendor/bundleとして上書きしています。

  • gem install bundler時に、Gemfile.lockで使われているbundlerのバージョンを指定してインストール方法として、ここを参考にして、以下の方法を試しましたが、何故か最新のものがインストールされてしまったので、gem install bundler:$BUNDLER_VERSIONとしています。

    echo 'export BUNDLER_VERSION=$(cat Gemfile.lock | tail -1 | tr -d " ")' >> $BASH_ENV
    source $BASH_ENV
    gem install bundler
    

参照先

11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?