何かやってみるブログ

興味をもったことに関して書いています。技術系の記事が多いです。

[Rails] GitHub Actions(CI) を試してみた

概要

以前の記事でも書きましたが、業務ではGitLab CIを使用しています。しかし、個人開発などではGitHubを使いたかったので、今回はGitHub Actionsを試してみました。

www.takayasugiyama.com

GitHub Actionsとは

GitLab CIと同様にbuilt in、つまりGitHubだけでCI/CD環境が構築出来るサービスです。 詳しくはこの記事を見てください。とてもわかり易いです。

試してみる

環境構築

リポジトリを作ります。

$ mkdir ~/sample_pj
$ cd ~/sample_pj
$ touch Dockerfile docker-compose.yml  Gemfile Gemfile.lock

Dockerfiledocker-compose.ymlGemfileGemfile.lockを用意します。

FROM ruby:2.6.5
RUN apt-get update -y && apt-get install curl 
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
RUN apt-get install -y nodejs
RUN npm install -g yarn 
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile  /myapp/Gemfile
COPY Gemfile.lock  /myapp/Gemfile.lock
RUN bundle install
RUN yarn install
COPY . /myapp

docker-compose.ymlでは3つのコンテナを定義しました。 railsなのでwebdbはおなじみですが、今回はSystem Specを扱いたいのでwebdriver_chromeというコンテナを定義しました。

version: "3"
services:
  db:
    image: mysql:5.7
    volumes:
      - db-volume:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password
  web:
    build: .
    command: rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    environment:
      SELENIUM_REMOTE_URL: http://webdriver_chrome:4444/wd/hub
    depends_on:
      - db
      - webdriver_chrome
  webdriver_chrome:
    image: selenium/standalone-chrome
volumes:
  db-volume:
source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rails", '6.0.0'

railsアプリケーションを作成します。

$ docker-compose run web rails new . -f  -d=mysql

config/database.ymlを編集します。

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root   
  password: password   <--  追記
  host: db  <--- 追記

編集したらbuildし直します。

$ docker-compose build

データベースを作成して、サーバーを立ち上げます。

$ docker-compose run web rails db:create
$ docker-compose up -d

http://localhost:3000/にアクセスしていかのようになっていれば環境構築は終了です。

f:id:s-takaya1027:20200428063427p:plain

適当に機能を作成する

scaffoldで簡単なブログ的な機能を実装します。

$ docker-compose exec web /bin/bash
root@c6e2afbc1f47:/myapp# rails g scaffold Post title:string content:text
root@c6e2afbc1f47:/myapp# rails db:migrate

テストを書く準備をする

テストを書くのに必要なgem(factory-bot-railsrspec-rails)をインストールします。

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
   gem 'factory_bot_rails' <-- 追記
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers',  require: !ENV['SELENIUM_REMOTE_URL']  <--- 編集
  gem 'rspec-rails', '~> 4.0.0'  <-- 追記
end
$ docker-compose exec web /bin/bash
root@c6e2afbc1f47:/myapp# bundle
root@c6e2afbc1f47:/myapp# rails g  rspec:install

webdriversrequire: !ENV['SELENIUM_REMOTE_URL']としているのはSystem Spec実行時にFailed to find Chrome binaryというエラーが出るのを防ぐための処理です。

spec/rails_helper.rbにdriverの設定とFactoryBotの設定を追記します。

Capybara.server_host = Socket.ip_address_list.detect{|addr| addr.ipv4_private?}.ip_address
Capybara.server_port = 3000

Capybara.register_driver :selenium_remote do |app|
  url = "http://chrome:4444/wd/hub"
  opts = { desired_capabilities: :chrome, browser: :remote, url: url }
  driver = Capybara::Selenium::Driver.new(app, opts)
end

RSpec.configure do |config|
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.before(:each, type: :system) do
    driven_by :rack_test
  end

  config.before(:each, type: :system, js: true) do
    driven_by :selenium_remote
    host! "http://#{Capybara.server_host}:#{Capybara.server_port}"
  end

  config.include FactoryBot::Syntax::Methods
  ・
  ・
  ・
end

.rspecにformatの設定を追記します

--require spec_helper
--format documentation

FactoryBotでpostのテストデータを作成します。

FactoryBot.define do
  factory :post do
    title { "test" }
    content  { "test" }
  end
end

適当にRSpec(model spec、system spec)を書く

root@c6e2afbc1f47:/myapp# rails g rspec:model Post
root@c6e2afbc1f47:/myapp# rails g rspec:system posts
require 'rails_helper'

RSpec.describe Post, type: :model do
  describe "データ" do
    it("テストデータは有効"){ expect(build(:post)).to be_valid }
  end

  describe "CRUD機能" do
    describe "ポスト作成" do
      let(:post) { build(:post) }
      it { expect{post.save}.to change { Post.count }.by(1)}
    end

    describe "ポスト削除" do
      let!(:post) { create(:post) }
      it { expect{post.destroy}.to change { Post.count }.by(-1) }
    end

    describe "ポスト更新" do
      let(:post) { Post.new(title: "test", content: "test") }
      it "更新に成功する" do
        post.update(title: "updated")
        expect(post.title).to eq "updated"
      end
    end
  end
end
require 'rails_helper'

RSpec.describe "Posts", type: :system do
  before { visit new_post_path }

  describe "create new post" do
    subject do
      fill_in "Title", with: "New Post"
      fill_in "Content", with: "New Post"
      click_on "Create Post"
    end

    context "正しく入力したとき" do
      it "Postの数は1つ増える" do
        expect{ subject }.to change { Post.count }.by(1)
      end
    end

    context "Postの作成に成功したとき" do
      it "詳細ページに飛んでフラッシュメッセージが表示される" do
        subject
        expect(page).to have_current_path post_path(Post.first)
        expect(page).to have_selector "#notice", text: "Post was successfully created."
      end
    end
  end
end
root@c6e2afbc1f47:/myapp# rm -rf test
root@c6e2afbc1f47:/myapp# bundle exec rspec

Post
  データ
    テストデータは有効
  CRUD機能
    ポスト作成
      is expected to change `Post.count` by 1
    ポスト削除
      is expected to change `Post.count` by -1
    ポスト更新
      更新に成功する

Posts
  create new post
    正しく入力したとき
      Postの数は1つ増える
    Postの作成に成功したとき
      詳細ページに飛んでフラッシュメッセージが表示される

Finished in 35.55 seconds (files took 9.6 seconds to load)
6 examples, 0 failures

上記のようにテストがパスすればOKです。

rubocopの設定をする

Gemfileにrubocopを追記してインストールします。

gem 'rubocop', require: false
root@c6e2afbc1f47:/myapp# bundle
root@c6e2afbc1f47:/myapp# bundle exec rubocop --auto-gen-config

今回はrubocopがCIで動くのが確認できればいいので、.rubocop.ymlの中身を.rubocop-todo.ymlのファイルの中身に置き換えます。

root@c6e2afbc1f47:/myapp# bundle exec rubocop 

f:id:s-takaya1027:20200428072752p:plain

上の画像のようにrubocopがパスすればrubocopの準備は終了です。

GitHub Actionの設定をする

設定ファイルを作成します。公式サイトによると設定ファイルは.github/workflows配下に置きます。

root@c6e2afbc1f47:/myapp# mkdir .github && mkdir .github/workflows && touch .github/workflows/.github-ci.yml
name: Test
on: [push]
jobs:
  rspec:
    runs-on: ubuntu-latest
    services:
      db:
        image: mysql:5.7
        env:
          MYSQL_USER: root
          MYSQL_ROOT_PASSWORD: password
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
    container:
      image: ruby:2.6.5
    steps:
      - uses: actions/checkout@v2
      - name: setup bundle
        run: bundle install
      - name: install chorme
        run: |
          curl -sS -L https://dl.google.com/linux/linux_signing_key.pub | apt-key add -
          echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list
          apt-get update -q -y
          apt-get install -y google-chrome-stable
      - name: setup yarn
        run: |
          curl -sL https://deb.nodesource.com/setup_12.x | bash -
          apt-get install -y nodejs
          npm install -g yarn
          yarn install
      - name: setup migrate
        run: |
          rails db:create RAILS_ENV=test
          rails db:migrate RAILS_ENV=test
      - name: run spec
        run: bundle exec rspec
  rubocop:
    runs-on: ubuntu-latest
    container:
      image: ruby:2.6.5
    steps:
      - uses: actions/checkout@v2
      - name: setup bundle
        run: bundle install
      - name: run rubocop
        run: bundle exec rubocop

実行

Githubにリポジトリを作って、pushします。

実行結果

f:id:s-takaya1027:20200428074404p:plain

RSpec、RubocopどちらもPassしました。

今回試したリポジトリはこちらです。

github.com

GitHubに詳しくなりたいので、次はこの本を読んでみたいと思います。

参考にした記事

github.co.jp

knowledge.sakura.ad.jp

qiita.com

github.com