概要
今回は、現在開発中のWebアプリにGithub ActionsによるCIを実装した際、Rubocop, Erb Lint, RSpecの導入に2日ほど費やしてしまったため、今後のために備忘録としてまとめたものになります。
初学者の身であり、Github Actionsへの理解も甘いため、間違って認識している点もあるかと思いますので、ご指摘いただけると幸いです。
Github ActionsによるRuboCop, Erb LintのCI設定
作成したファイル
- .github/workflows/ci.yml
- .erb_lint.yml
- .rubocop.yml
- .rubocop_todo.yml
使用したGem
- rubocop-rails-omakase
- rubocop
- rubocop-performance
- rubocop-rails
- rubocop-rspec
- erb_lint
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: RuboCop
run: bundle exec rubocop --fail-level W
- name: ERB Lint
run: bundle exec erb_lint --lint-all
ci.ymlはRails7以上であればrails newで自動生成されるかと思います。
ここでは、lint
というjob
の一つのstep
としてRuboCop
とERB Lint
というテストを実行するよう設定しています。
- name: RuboCop
run: bundle exec rubocop --fail-level W
RuboCopを実行するよう指示しています。
--fail-level W
:これはテスト実行後にfailを判定する基準を指定しています。ここではW(Worning)を指定しているので、 Worning
以上の違反がある場合のみテストが通らないようになっています。(W未満の違反でも検知はしてくれるので、適宜確認して修正することは可能です)
- name: ERB Lint
run: bundle exec erb_lint --lint-all
Erb Lint(erbファイルのリントチェック)の実行を指示しています。
erblint --lint-all
のように_
がなくても実行はできますが、今は警告が出てしまうみたいなのでerb_lint
としました。
---
EnableDefaultLinters: true
glob: "**/*.{html,text,js}{+*,}.erb"
exclude:
- '**/vendor/**/*'
- '**/node_modules/**/*'
linters:
ErbSafety:
enabled: true
Rubocop:
enabled: true
rubocop_config:
inherit_from:
- .rubocop.yml
Style/FrozenStringLiteralComment:
Enabled: false
Layout/InitialIndentation:
Enabled: false
Erb Lintの設定ファイルになります。
Rubocop:
以下の記述があることで、RuboCopによる解析と連動させることができます。
Style/FrozenStringLiteralComment:
Enabled: false
Layout/InitialIndentation:
Enabled: false
Layout/InitialIndentation:
Enabled: false
この部分ではRuboCopのルール設定をしています。これに関しては、私自身まだ何が必要で何が不要なのかの判断はつかないため、各々の環境に合わせて設定していただくのがいいと思います。
おすすめの設定があれば教えていただきたいです。
inherit_from: .rubocop_todo.yml
inherit_gem: { rubocop-rails-omakase: rubocop.yml }
RuboCopの設定ファイルになります。bundle exec rubocop --auto-gen-config
を実行すると自動で生成されます。
ここに必要なルールを設定していくことで現場にあったテストを実行できるようになります。
.rubocop_todo.yml
は空のままで大丈夫です。
以上でGihub ActionsにRuboCop、Erb Lintを導入するためのCI設定は完了です。
続いてRSpecを導入していきますが、まずはローカル(docker)環境でRSpecを実行するための環境づくりから見ていきましょう。
RSpecの導入
ここではまず、ローカルでRSpecを実行するための環境づくりから始めていきます。
作成したファイル
- rails_helper.rb
- spec_helper.rb
- .rspec
- capybara.rb
- database.ci.yml
追加したGem
- deveopment, test環境
- factory_bot_rails
- database_cleaner"
- faker
- rspec_rails
- development環境
- spec_rails
- test環境
- capybara
- selenium_webdrivers
- webdrivers
RSpecの導入
bundle install
で必要なgemをインストールしたら、下記のコマンドを実行しましょう。
bundle exec rails g rspec:install
これによりRSpecの設定に必要な以下のファイルが自動で生成されます。
.rspec
spec/spec_helper.rb
spec/rails_helper.rb
まずはDockerの設定にRSpec SystemでCapybaraを使用するための設定を追加していきます。
web:
container_name: rails_app
build:
context: .
dockerfile: Dockerfile.prod
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
tty: true
stdin_open: true
volumes:
- .:/myapp
- bundle_data:/usr/local/bundle:cached
- node_modules:/myapp/node_modules
environment:
TZ: Asia/Tokyo
SELENIUM_DRIVER_URL: http://chrome:4444/wd/hub # この行を追加
ports:
- "3000:3000"
depends_on:
db:
condition: service_healthy
chrome: # このセクションを追加
image: seleniarm/standalone-chromium:latest
ports:
- 4444:4444
docker-compose.ymlにSELENIUM_DRIVER_URL:
の一行とchrome:
のセクションを追加します。この設定によりDocker環境でCapybaraを使用したブラウザ操作を自動化したテストが可能になります。
では続いて、生成されたrails_helper.rb
.rspec
に設定を追加していきます。
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
# この行はコメントアウトを解除するだけで大丈夫です
...
RSpec.configure do |config|
...
config.include FactoryBot::Syntax::Methods # この行を追加
...
config.before(:each, type: :system) do # このブロックを追加
if ENV['SELENIUM_DRIVER_URL']
driven_by :remote_chrome
Capybara.server_host = IPSocket.getaddress(Socket.gethostname)
Capybara.server_port = 4444
Capybara.app_host = "http://#{Capybara.server_host}:#{Capybara.server_port}"
Capybara.ignore_hidden_elements = false
else
driven_by :selenium_chrome_headless
end
end
end
config.include FactoryBot::Syntax::Methods
この設定により、FactoryBotをspec内で簡単に使用できるようになります。
例)設定なしの場合
user = FactoryBot.create(:user)
例)設定ありの場合
user = create(:user)
config.before(:each, type: :system) do
このブロックでは、Capybaraによるブラウザ操作を行う際のホスト、ポートなどを指定しています。if ENV['SELENIUM_DRIVER_URL']
の条件により、環境変数が指定されている場合、つまりDockerコンテナ内でRSpecを実行する場合にはSelenium(remote_chrome)を使用し(ローカルでのDocker開発環境でRSpecを実行する場合ならこっち)、Dockerコンテナを使わないで実行する場合には、その環境にインストールされているChromeを使用するように分岐させています。今回、Github ActionsではDockerコンテナを立ち上げずに実行しているのでSeleniumの方を使用することになります。
https://qiita.com/ryouzi/items/fe0f583dcc93e06b6e64
--format documentation # この行を追加
この設定により、RSpecのテスト結果の出力形式を「ドキュメンテーション形式」に設定し、テストの実行結果がより読みやすくなります。
require 'capybara/rspec'
require 'selenium-webdriver'
Capybara.register_driver :remote_chrome do |app|
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument('no-sandbox')
options.add_argument('headless')
options.add_argument('disable-gpu')
options.add_argument('window-size=1680,1050')
Capybara::Selenium::Driver.new(app, browser: :remote, url: ENV['SELENIUM_DRIVER_URL'], capabilities: options)
end
Capybara.javascript_driver = :selenium_chrome_headless
CapybaraがリモートのChromeドライバーを使うために必要なオプションを指定しています。
url: ENV['SELENIUM_DRIVER_URL']
により、docker-compose.ymlで指定した環境変数SELENIUM_DRIVER_URL
をサーバーのurlとして指定しています。
config.hosts << "許可したいホストのIP"
Railsサーバーに特定のホストからのリクエストを許可するための記述になります。RSpec Systemの実行時にホストが拒否される場合に追加してください。
以上で、ローカルでRSpec Model, Systemを実行するための環境は整いました。実際のテストコードについては、ここでは割愛します。
では続いて、Github ActionsにRSpecを導入していきましょう。
Gihub ActionsにRSpecを導入するためのCI設定
私はここで一番時間を取られました。
まずはci.yml
の設定を追加・変更していきます。
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres
env:
POSTGRES_USER: # ユーザー名を指定
POSTGRES_PASSWORD: # パスワードを指定
ports:
- 5432:5432
options: --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3
timeout 5s --health-retries 5
env:
RAILS_ENV: test # この行を追加
DATABASE_URL: postgres://postgres:postgres@localhost:5432/myapp_test
steps:
- name: Install packages
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable curl libjemalloc2 libvips postgresql-client
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Database create and migrate # このセクションを追加
run: |
cp config/database.ci.yml config/database.yml
bundle exec rails assets:precompile #ここで詰まった
bundle exec rails db:create
bundle exec rails db:migrate
- name: Run rspec # このセクションを追加
run: bundle exec rspec
env:
の部分でこれ以下の処理をテスト環境で実行するように指定しています。
name: Database create and migrate
のセクションではRSpec Systemを実行するために一時的にデータベースを作成しています。
test:
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: db
username: # ユーザー名
password: # パスワード
database: myapp_test
ここでテスト用のデータベースを設定しています。
私が時間を取られたポイント
アセットのプリコンパイルができていなかった
私は初め、bundle exec rails assets:precompile
が抜けていたために、ActionView::Template::Error:The asset 'application.js' was not found in the load path.
というエラーが出てしまい、プリコンパイルができていないということに気づくまでに時間を取られました。
entorypoint.sh
にも同様のコードを書いていますが、Github ActionsではDockerコンテナを起動していないため、entorypoint.sh
も実行されていなかった、ということだと解釈しました。
ChromeとChromeDriverのバージョンの不一致
gem "webdrivers"
はインストールされているChromeのバージョンに適するChromeDriverを見つけてくれるらしいのですが、それがうまく見つからないのか、
Webdrivers::NetworkError: # Net::HTTPClientException: 404 "Not Found" with https://chromedriver.storage.googleapis.com/LATEST_RELEASE_131.0.6778
というエラーが出てしまい、テストが実行できませんでした。
Gemfile.lockを確認すると、webdrivers (5.2.0)
がインストールされていたので、最新版のwebdrivers (5.3.1)
をバージョンを指定してインストールしてみたところ、エラーが解消されました。
終わりに
本記事はほとんど自分用として今回取り組んだことをまとめたものになりますので、色々と至らない点があるかと思いますがご了承ください。
間違っている点などありましたらご指摘いただけると幸いです。
参考にさせていただいた記事