Github Actions実装

# はじめに
私は現在、通っているプログラミングスクールの卒業制作としてアプリを作成しています。
その実装の一環でGithub Actionsを用いたHerokuへの自動デプロイとCIテスト機能を実装しました。

私自身が初学者であり、かつ初めての技術記事を投稿するので、拙い箇所や間違っているところがあるかもしれません。もし指摘等ありましたらご連絡ください。
# 環境
Ruby 3.3.6
Ruby on Rails7.2.2
Docker/docker-compose
GitHub Actions
MySQL
Heroku/Cloudflare

# Github Actionsとは
- 公式

https://docs.github.com/ja/actions

ここでは簡単に解説すると、開発中の本番環境へのデプロイやRubocop・Rspecなどのテストといった作業を自動で実行してくれる機能です。
これらはGitHubのリポジトリに特定のアクションを登録しておくことで実行できます。
私は今回、以下の２つの機能を実装しようと思っています。
- プルリクエストを作成した段階でRubocop・rspecのテストを実行
- mainブランチにプッシュした際、本番環境（Heroku）へ自動プッシュを実行

以下に、実装までの手順やコード解説、つまづいた箇所の解決までの経緯を記述します。
# ワークフロー
ワークフローとは、複数のジョブからなる設定可能な自動化プロセスのことで、これを用いることで様々な動作を実装します。
- 簡単な解説
  - name:ワークフロー名
  - on: トリガーイベント
  - job: 実行されるジョブ一連の流れをひとまとめにしたもの
  - steps: ジョブ内での流れをステップごとにまとめたもの

etc...


・・・正直、この辺りはたくさんありすぎて書ききれないので、残りはこちらの公式や他の方の記事などで確認してください。

https://docs.github.com/ja/actions/writing-workflows/workflow-syntax-for-github-actions

# 実装手順
### GitHub上でのセットアップ


1. 対象のリポジトリの[Settings]にアクセス
|
2. 左サイドバーのSecurityという項目内にある[Secrets and variables]から、[Actions]を選択
|
3. [New repository secret]ボタンをクリックし、HEROKU_API_KEYとHEROKU_EMAILを設定


この時登録するHEROKU_API_KEYとHEROKU_EMAILは以下の手順で確認できます。
1. Herokuにログインして[Account Settings]→[API Key]セクションまでスクロール→[Reveal]ボタンをクリックすると表示されるものを取得
1. 上記の流れで[Account Settings]のprofileにあるEmail Addressを取得

### ファイル作成
.github/workflows/の配下に作成します。

ci.ymlはコード品質を保つためのテストを実行する機能。デプロイする機能は新たにdeploy.ymlを作成します。

### Herokuへ自動デプロイ

```deploy.yml

name: Deploy to heroku

# mainブランチにpushされた時に実行
on:
  push:
    branches:
      - main

# 実行されるジョブ
jobs:
  deploy:
    # テスト環境を最新バージョンのUbuntu(Linux)に設定
    runs-on: ubuntu-latest

    steps:
      # リポジトリのコードをチェックアウト
      # ワークフローがリポジトリのコードにアクセスできる
      - uses: actions/checkout@v4

      # Heroku CLIをインストール
      # 後述のマイグレーションファイルで使用
      - name: Install Heroku CLI
        run: curl https://cli-assets.heroku.com/install.sh | sh
        
      # Herokuへのデプロイを実行
      # 認証コードをGithubのSecretsから取得
      - uses: akhileshns/heroku-deploy@v3.13.15
        with:
          heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
          heroku_app_name: "stockmate"
          heroku_email: ${{ secrets.HEROKU_EMAIL }}

      # デプロイ後にマイグレーションを実行
      # Heroku CLIを利用してRailsのマイグレーションコマンドを実行
      - name: Run database migrations
        env:
          HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
        run: heroku run rails db:migrate -a stockmate
```

### CIチェック
```ci.yml
name: CI

# プルリクエストが作成された時、またはmainブランチにpushされた時に実行
on:
  pull_request:
  push:
    branches: [ main ]

jobs:
# Gem brakemanを使用してセキュリティの脆弱性をスキャン
  scan_ruby:
    runs-on: ubuntu-latest

    steps:
      # コードをチェックアウト
      - name: Checkout code
        uses: actions/checkout@v4

      # Rubyをセットアップ
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: .ruby-version
          bundler-cache: true

      # brakemanでスキャン実行
      - name: Scan for common Rails security vulnerabilities using static analysis
        run: bin/brakeman --no-pager

  # Rubocopを使用してコードの一貫性をチェック
  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

      # Rubocopによるチェックを実行
      - name: Lint code for consistent style
        run: bin/rubocop -f github

  # アプリケーションのテストを実行
  test:
    runs-on: ubuntu-latest

    # テスト環境を定義
    services:
      mysql:
        image: mysql:8.0　# 使用するデータベース
        # 環境変数・パスワード設定・ポート番号
        # Dockerfile.devやdatabase.ymlとの関係性に注意する
        env: 
          MYSQL_DATABASE: myapp_test # データベース名
          MYSQL_PASSWORD: password # パスワード
          MYSQL_ROOT_PASSWORD: password # MySQLのセキュリティ用ルートパスワード
        ports: # 仮想マシン内のポート番号
               # postgreSQLは5432になる
          - 3306:3306
        # オプション設定
        options: --health-cmd="mysqladmin ping"  # Mysqlが起動しているかチェック
                 --health-interval=10s # 10秒ごとにチェック
                 --health-timeout=5s # 5秒以内に完了しなければ失敗
                 --health-retries=3 # 最大3回はチェック

    # テストを実行
    steps:
      # 必要なパッケージをインストール
      - name: Install packages
        run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable curl default-mysql-client libjemalloc2 libvips

      - 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: Run tests
        env:
          RAILS_ENV: test
          DATABASE_URL: mysql2://root:password@127.0.0.1:3306/myapp_test
        run: |
      # 空のデータベースを作成し、そこに開発環境のスキーマを取得してテスト実行
          bin/rails db:test:prepare
          bundle exec rspec

      # 失敗時のスクリーンショットを保存
      - name: Keep screenshots from failed system tests
        uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: screenshots
          path: ${{ github.workspace }}/tmp/screenshots
          if-no-files-found: ignore

```
# 解説

```ci.yml
bin/rails db:test:prepare
```
このコードは、以下の処理を行っています。
1. 必要な場合にテストデータベースを作成
1. development環境のスキーマ（db/schema.rb）をテストデータベースにコピー

これをさらに細かく分解します。
1. 古いテストデータベースがあればを削除
1. 新しいデータベースを作成
1. テストデータベースを空の状態にする
1. 開発環境のスキーマ情報を取得
1. 取得したスキーマをテストデータベースに適応

この場におけるスキーマとは、データベースの構造のことです。

これをset up databaseというnameでstep内に定義することもできます。
```ci.yml
      - name: Set up Database
        env:
          RAILS_ENV: test
          DATABASE_URL: mysql2://root:password@127.0.0.1:3306/myapp_test
        run: |
          bin/rails db:drop || true
          bin/rails db:create
          bin/rails db:schema:load
          bin/rails db:migrate

```
こちらも上記と同様の流れの処理を実行しています。
set up databaseのメリットはエラーが発生した場合、どの段階でエラーが発生したかを明確に判断できるということです。

また、まだRspecの準備ができていない場合は、以下のようにRailsのシステムテストを実行するようにしておき、実装後に修正するということもできます。
```ci.yml
     - name: Run tests
        env:
          RAILS_ENV: test
          DATABASE_URL: mysql2://root:password@127.0.0.1:3306/myapp_test # データベースの接続URL
        run: |
          bin/rails db:test:prepare
          bin/rails test test:system # Railsのシステムテストを実行
```

ただし、この場合注意して欲しいことがあります。
このシステムテストはアプリ内の全てのファイルを読み取るため、不要なファイルなどが残っているとそれが原因でエラーとなります。

私の失敗談として、以下のようなエラーが出て解決に苦労しました。
```
Error:
StaticPagesControllerTest#test_should_get_top:
ActiveRecord::StatementInvalid: Mysql2::Error: Table 'myapp_test.installs' doesn't exist
```
installsというカラムが存在しないというエラーなのですが、当然そのようなカラムに覚えがありません。

メンターの方に相談したところ指摘されたのが、test/fixtures/installs.ymlというファイルが存在していて、それがエラーの原因になっていルのではとのことでした。

このように、予想外のところからエラーが飛び出してくるので気をつけましょう。
# まとめ
今回の実装は公式や他の方の記事、Githubのコードを参照しながら作成しました。
失敗も多かったですが学びもあり、とても有意義なものだったと思います。

自身がメンターの方に助けてもらったように、この記事が誰かの助けになればさいわいです。
ここまで読んでくださり、ありがとうございました。

# 参考記事

https://qiita.com/s3i7h/items/b50ceb0008edc3c0312e

https://zenn.dev/shin_shin_01/articles/53009a81728e21

https://wa3.i-3-i.info/word13663.html