31歳未経験からwebエンジニアへの転職を目指している@naota7118と申します。
この記事は『CircleCI Advent Calendar 2020』の16日目の記事です。
はじめに
転職活動用ポートフォリオでCircleCIを使いました。使う前から「CircleCIは難しそう」というイメージを持っていましたが、実際使ってみると、「使うだけならそんなに難しくないが、ちゃんと細かい部分まで理解するのは難しい」というのが正直な感想です。「使うだけならそんなに難しくない」理由は「Rails CircleCI」などで検索すれば、先人の記事がいくつも出てきて、それらを参考にすれば設定できてしまうからです。本記事では、一応使っているけれどほとんど理解できていないCircleCIの設定ファイルの中身を調べてわかる範囲で説明してみたいと思います。
想定する読者
これからポートフォリオでCircleCIを使おうと思っている実務未経験の方
CircleCIとは
CI/CDサービスの1つ。GitHubのリポジトリへプッシュするタイミングで、アプリのビルドやテスト、デプロイなどの処理が実行できます。
CircleCIの設定ファイルに記述しておけば、Rspecを使ったテストやCapistranoを使った自動デプロイが$ git push
したタイミングで自動で実行されます。
CircleCIの設定ファイルのソースのコード
CircleCIの設定は.circleci/config.yml
というYAMLファイルで行います。
version: 2
jobs:
build:
docker:
- image: circleci/ruby:2.5.1-node-browsers
environment:
- BUNDLER_VERSION: 2.1.4
- RAILS_ENV: 'test'
- image: circleci/mysql:5.6
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
- MYSQL_ROOT_HOST: '%'
working_directory: ~/projects/pfc-master
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run:
name: install dependencies
command: |
gem install bundler -v 2.1.4
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
- run: mv config/database.yml.ci config/database.yml
# Database setup
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load
# - run:
# name: run rubocop
# command: bundle exec rubocop --auto-gen-config
# run tests!
- run:
name: run tests
command: |
mkdir /tmp/test-results
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
circleci tests split --split-by=timings)"
sudo gem install bundler
sudo gem install rspec
sudo gem install rspec-core
bundle exec rspec \
--format progress \
--format RspecJunitFormatter \
--out /tmp/test-results/rspec.xml \
--format progress \
$TEST_FILES
# collect reports
- store_test_results:
path: /tmp/test-results
- store_artifacts:
path: /tmp/test-results
destination: test-results
- add_ssh_keys:
fingerprints:
- "90:a0:74:73:63:66:9b:9c:c2:95:9e:54:1a:2c:1d:6a"
# - deploy:
# name: Capistrano deploy
# command: |
# if [ "${CIRCLE_BRANCH}" != "master" ]; then
# exit 0
# fi
# bundle exec cap production deploy
CircleCIの設定ファイルを説明してみる
version
#この設定ファイルがCircleCI 2.0対応と宣言している
version: 2
バージョンは2、2.0、2.1のどれかを指定します。バージョンによって使用できるキーが変わってくるため、バージョンを指定する必要があります。例えばorbs
、commands
、parameters
、executors
はバージョン2.1が必須です。
jobs
jobs: # 実行するジョブを指定する
build: # 「build」という名前のジョブを定義
ジョブとは、ある特定の目的を達成するためのプログラムのかたまりのことです。今回は「build」という名前のジョブを定義しました。
docker
docker: #ジョブのステップの実行環境としてDockerイメージを定義
- image: circleci/ruby:2.5.1-node-browsers #使用するカスタムDockerイメージの名前
environment: #環境変数の名前と値のマップ
- BUNDLER_VERSION: 2.1.4
- RAILS_ENV: 'test'
- image: circleci/mysql:5.6 #使用するカスタムDockerイメージの名前
environment: #環境変数の名前と値のマップ
- MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
- MYSQL_ROOT_HOST: '%'
マップとは、関連づけること。「BUNDLER_VERSION」と「2.1.4」を関連づけている。
working_directory
working_directory: ~/projects/pfc-master #ステップを実行するディレクトリを定義
steps
steps:
- checkout
ステップは、ジョブ中に実行される実行可能なコマンドの集まりです。コードをチェックアウトするには checkout: キーを指定します。また、run: キーを使用すると、複数行にわたる任意のシェルコマンドスクリプトを追加できます。チェックアウトは、アプリケーションのソースコードをcloneすることを意味します。
restore_cache
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
restore_cacheは以前に保存したキャッシュをkeyに基づいて復元します。あらかじめsave_cacheステップを使用して、そのキーでキャッシュを保存しておく必要があります。
キャッシュとは、ウェブページの情報を一時的に保存する仕組みのことです。キャッシュという一時的な保存データがスマホやPC内にあることで、再表示の速度が上がります。
run
- run: #runキーは任意のコマンドの呼び出し
name: install dependencies #CircleCIのUIに表示されるステップのタイトル (デフォルトは command 文字列全体)。
command: | #シェルから実行するコマンド。
gem install bundler -v 2.1.4
bundle install --jobs=4 --retry=3 --path vendor/bundle
それぞれのrun宣言で新しいシェルが立ち上がります。複数行のcommandを指定でき、その場合はすべての行が同じシェルで実行されます。runで任意のコマンドを設定することができ、command: |
という記述方法をとることで、それ以下に複数行を書いても全て同じシェルで実行されます。今回はrunでbundlerのバージョンを指定しbundle installするコマンドの処理を任意で設定しました。
save_cache
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
ドキュメントには「依存関係やソースコードなどのファイルのキャッシュ、または複数のファイルが入ったディレクトリのキャッシュを生成して、オブジェクトストレージに格納します」とありますが、正直全然わかりません。ドキュメントには、キャッシュを説明するページがありました(下記リンク参照)。それによると、キャッシュはデータを再利用することでCircleCIのジョブを高速化してくれるとのことです。キャッシュを使用する目的がわかったので、今回は仕組みの理解は諦めます。
run(2回目)
- run: mv config/database.yml.ci config/database.yml
runは任意のコマンドを設定できるのでした。config/database.yml.ci
の中身をconfig/database.yml
に反映させています。既存のdatabase.yml
を指定した結果エラーが出たため、CircleCI用のデータベース設定を別で作ることにしました。
test:
adapter: mysql2
encoding: utf8
pool: 5
username: 'root'
port: 3306
host: '127.0.0.1'
database: ci_test
# Database setup
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load
rake db:schema:load
はdb/schema.rb
からテーブルを作成します。rake db:schema:load
の部分をrake db:migrate
に変更している例をちらほら見かけたので、2つを使い分ける必要がありそうです。
# - run:
# name: run rubocop
# command: bundle exec rubocop --auto-gen-config
rubocopで落ちた部分の修正を重ねてもsuccessにならないため、今は一時的にコメントアウトしてあります。数日中になんとかしたいと思います。
Rspec
- run:
name: run tests #この名前で表示される
command: | #複数のコマンドの1つのシェルで処理できる
mkdir /tmp/test-results #テスト用のファイルを作成
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
circleci tests split --split-by=timings)"
sudo gem install bundler
sudo gem install rspec #Rspecをインストール
sudo gem install rspec-core
bundle exec rspec \ #Rspecによるテスト実行
--format progress \
--format RspecJunitFormatter \
--out /tmp/test-results/rspec.xml \
--format progress \
$TEST_FILES
store_test_results
- store_test_results:
path: /tmp/test-results
store_test_resultsは、ビルドのテスト結果をアップロードおよび保存するための特別なステップです。 テスト結果は、CircleCI Webアプリケーションで各ビルドの「テストサマリー」セクションに表示されます。 テスト結果を保存すると、テストスイートのタイミング分析に役立ちます。
/tmp/test-results
ここにテスト結果が保存されます。テストスイートという謎ワードが出てきたので調べました。タイミング分析は見つかりませんでしたが、「テストはタイミングも重要なのか」ということにします。
テストケースとは、テストを行うエンジニアがどんなテストをすればいいか、その手順をまとめたものです。ひとつのシナリオが完結するまでのテストケースを集めたものを、テストスイートと呼びます。
引用元:https://qangaroo.jp/info/test-case-plan-do/
store_test_results
- store_artifacts:
path: /tmp/test-results
destination: test-results
store_artifactsは、Webアプリまたは API からアクセスできるアーティファクト (ログ、バイナリなど) を格納するステップです。
アーティファクトとは、ソフトウェアを作る過程で生み出された副産物のことです。文脈によってアーティファクトが示す対象は変わってくるようです。
https://blog.jfrog.co.jp/entry/what-is-an-artifact
今回はいちばんイメージしやすいので、アーティファクト=ログとして、ログを格納するコマンドとして理解することにします。なぜログなどの副産物を格納しておく必要があるのかを調べるのは今後の課題とします。
add_ssh_keys
- add_ssh_keys:
fingerprints:
- "90:a0:74:73:63:66:9b:9c:c2:95:9e:54:1a:2c:1d:6a"
add_ssh_keysはプロジェクトの設定からコンテナにSSH鍵を追加するための特別なステップです。指定する鍵を使用するようにSSHを構成します。
SSH鍵を追加したのは、Capistranoの自動デプロイを実行するためでした。CircleCIでCapistranoの自動デプロイもしたいという方はこの設定が必要になります。私の場合はまだ自動デプロイのエラーが解決できておらず、CI/CDのCDは実現できていません。
まとめ
まだまだ全然理解できていないのですが、ドキュメントが親切なので、ググればだいたいヒットくれます。これからも1つ1つ疑問点をつぶして理解を深めていきたいと思います。