環境
- Circle CI 2.1
- Rails 5 + PostgresSQL 9.6 + Yarn
- 主にCircle CIの設定のため、上記環境には大きく依存していません。
やっていること
- テストの実行(
rspec
、jest
)-
simplecov を使って、rspec 実行結果の code coverage を CircleCI 上の
ARTIFACTS
に残しています。
-
simplecov を使って、rspec 実行結果の code coverage を CircleCI 上の
- Linterの実行(
rubocop
、eslint
) - セキュリティチェック(
brakeman
) - デプロイ
-
develop
やmaster
ブランチへマージされた時に、capistrano
を使って各staging、production環境にデプロイ。 - 結果を
slack
に通知。 -
capistrano
の設定ファイル(config/deploy/staging.rb
等)で、set :branch, ENV['BRANCH'] || 'develop'
等として、ブランチの設定がされている必要があります。
-
- 上記に付随する処理(ソースコード、ライブラリのインストール及びキャッシング)
出来ていないこと
-
fingerprint
を環境変数で設定すべき
設定例
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