GitHub Actions を用いた自動テストと自動マージ

## はじめに
本記事は個人開発でGitHub Actionsを用いた自動テスト環境を構築する際に調べた内容をまとめました。
個人開発でもチーム開発でも関係なく知っておくべき内容だと思うので、忘備録として書きました。

## ゴール
最終的に達成したい内容は下記です。(mainブランチへの直pushは禁止している前提です)
1. GitHub Actionsを用いてソースコードを`push`した際にテスト(Lintチェック, 型チェック)を実行する。
2. テストが通らなければマージブロックする。
3. マージブロックが解消されたらマージする。

## GitHub Actionsとは
今回利用するGitHub ActionsとはCI/CDのプラットフォームです。

https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions

言葉だけでは何もわからないので実際にコードを書き、それぞれの用語について解説していきます。

### ワークフローの作成
特定のイベント(特定のアクティビティが実行された時、スケジュールした時間)で実行したい内容を記述します。
今回は`push`された時にテストを走らせることが目的です。
ワークフローは以下の手順で作成します。
1. 自身のリポジトリで、`ワークフロー`ファイルを格納するための`.github/workflows/`ディレクトリをルート直下に作成します。
2. `.github/workflows/`ディレクトリで、`任意のファイル名.yml`という名前の新しいファイルを作成し、コードを追加します。
3. これらの変更をコミットして、GitHubリポジトリにプッシュします。 

コードの内容は以下のようになります。
![スクリーンショット 2023-05-10 17.38.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/2f409faa-9e09-2314-e686-190f768124ce.png)

それぞれ用語を解説していきます。


### 用語
1. `name`について (1行目)
そのまま単純にこのワークフローの名前を表しています。
GitHubリポジトリのActionタブのワークフロー実行の一覧に表示されます。
![スクリーンショット 2023-05-10 16.06.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/a0e47344-2090-5228-a6ec-6d63886a251b.png)
2. `on`について (2行目から5行目)
このワークフローのトリガーを指定しています。
今回は`push`イベントを使用しており、かつ`paths`で`src/**`と指定しています。
具体的に何をしているかと言うと「srcディレクトリ以下の変更内容がpushされた時」トリガーされます。

3. `jobs`について (7行目から12行目)
このワークフローで実行される全てのジョブをグループ化します。
今回は`build`という名前のジョブが実行されます。
実行環境は、`runs-on`で指定することができ、最新のUbuntu環境を指定しています。
また、複数のマシンで実行する場合は`strategy`を指定する必要があり、10行目から12行目では`node-version`で指定した`16.x`と`18.x`の各バージョンで実行することを意味しています。

4. `steps`について (14行目から23行目)
このワークフローで実行される一連のタスクを指定しています。
`uses: actions/checkout@v2`はリポジトリをチェックアウトするためのアクションで、事前定義されているものを使用しています。(v2はメジャーバージョンを表しています)
同じように`uses: actions/setup-node@v2`ではNode.jsの実行環境を作成しています。
`run`はコマンドラインで実行する内容を表しており、今回は`yarn tsc`と`yarn lint`を実施しています。
(各`name`は各アクションの名前です)

## GitHub Actionsの挙動確認
上記のコードを実際に試します。
mainブランチに作成したワークフローがマージされていることを確認し、新しくブランチを切ってpushしてみます。
(`paths`でフィルターをかけているので`src`ディレクトリ以下の変更内容でないとワークフローが実行されません)

`test`ブランチを作成し、`push`してPRを作成しました。
![スクリーンショット 2023-05-10 19.29.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/33307599-b0ef-8f23-a7a3-e15a562bf0ec.png)
スクショの`Some checks haven't completed yet`でテストが行われていることがわかります。
`Checks`タブを開いてみます。
![スクリーンショット 2023-05-10 19.35.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/4df63635-3c98-665d-8b5e-e047caa74ede.png)
先ほど`jobs`で設定した`build`という名前で、Node.jsのバージョン[16.x 18.x]それぞれの環境で実行されていることがわかります。

これでGitHub Actionsの自動テストが正しく行われていることが確認できました。
ですが、テストが通らずエラーとなっていても**マージできてしまう**状態です。

![スクリーンショット 2023-05-12 12.25.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/b0714aa9-3cd0-f9ce-d7fc-0963b70365fd.png)

テストが通らなかった場合はマージできないように、**マージブロック**を設定します。

## マージブロックの設定
まずはリポジトリの`Settings`タブを開きます。
サイドメニューの`Branches`を選択し、`Add branch protection rule`を開きます。
![スクリーンショット 2023-05-12 12.19.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/c8bc758c-e3de-95c3-8e7e-f84931d1184d.png)


`Branch name pattern` を入力し、`Require status checks to pass before merging`にチェックを入れます。
`Require status checks to pass before merging`は「マージ前にステータスチェックに合格する必要がある」ことを意味しているので、どのステータスチェックに合格する必要があるのかを選択します。

:::note warn
ここでステータスチェックの検索にかけるワードは先ほど記述したワークフローの`jobs`の名前です。
(今回は`build`と設定しているので`build`で検索)
:::

![スクリーンショット 2023-05-12 12.04.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/5c7307c4-d79d-66ca-cc34-e94d4f545e9f.png)
`build(16.x)`と`build(18.x)`がヒットしたので両方選択し、一番下の`Create`ボタンで作成します。

サイドバーの`Branches`を選択すると、新たにルールが作成されていることが確認できます。
![スクリーンショット 2023-05-12 12.16.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/831ca536-9db0-5387-0805-4aeaf30a5a7c.png)
ルールが作成されたことを確認できたら、先ほどのPRを見てみます。
先ほどとは異なり、マージができないようになっています。
![スクリーンショット 2023-05-12 12.31.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/d2b40daf-de1a-f10e-273a-f5282f5b3768.png)
テストのエラーが解消された段階でマージが可能な状態になります。
これでマージブロックの設定が完了しました。

:::note info
チーム開発であればテストの合否だけでなく、レビューをしてもらわないとマージできないようにも設定できます。
:::

次は個人開発をしているときに重宝されるであろう(知らんけど)**自動マージ**について解説していきます。

## 自動マージの設定
テストでこけた(マージブロック)PRで自動マージを設定していきます。

:::note warn
自動マージの設定はリポジトリ単位ではなくPR単位になります。
:::

まずは今までと同様にリポジトリの`Setting`タブを開きます。
中盤から下の方にある、`Pull Requests`セクションの`Allow auto-merge`にチェックします。
![スクリーンショット 2023-05-12 12.48.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/53f060dc-60e0-65e7-78e5-de8936d699c6.png)
これで先ほどマージブロックされたPRを見てみます。
`Enable auto-merge`と表示されているのがわかります。
![スクリーンショット 2023-05-12 13.00.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/889ad1e3-609a-da2f-3c28-f6f372a67c5d.png)
`Enable auto-merge`をクリックするとマージする時と同じような画面が出るので必要なコメントを残し`Confirm auto-merge`をクリックします。
これで自動マージの準備は完了しました。(`Disable auto-merge`からいつでも設定解除できます。)
![スクリーンショット 2023-05-12 13.06.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/c7eb1883-1daf-28bf-2534-19f62b767675.png)
この状態でマージブロックを解消してやると自動でマージされます。
![スクリーンショット 2023-05-12 13.08.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/152fde3e-e4cd-e0e5-596b-a32c67653a1c.png)

## まとめ
今回記事を書いていく中で、GitHub Actionsのワークフローに関してはまだまだ理解が足りないなと実感しました。(できることが多すぎる)
ですが、自動テストに関してはこれから転職を目指す初心者の方にとっても知っていて損はないので実際に試していただきたいところです。
自動マージに関しましては、PRごとに設定する必要があり、必ず自動化の恩恵が受けられるとは思わないので必要に応じて使いこなしていければいいかなと思いました。
書けば書くほど雑になりましたが、最後にブランチ削除の設定だけ解説して終わりにしたいと思います。
興味のある方だけ読んでみてください。

### オマケ
ブランチの自動削除の設定を行います。
基本的にマージされたブランチは必要ないものなので削除すると思うのですが、この操作も自動化してしまいます。(めちゃ簡単です)
先ほど自動マージの設定と同様にリポジトリの`Setting`を開きます。
そして先ほどチェックを入れた`Allow auto-merge`の下の`Automatically delete head branches`にチェックを入れます。
![スクリーンショット 2023-05-12 13.25.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2645945/bacbe6ad-1af5-8c3f-1b6e-d309bdab3dfb.png)
終わりです。
これでマージされたタイミングで自動でブランチが削除されるようになりました。


自動化できるところはどんどん自動化して開発スピードを上げていきましょう！

# 参考
- https://docs.github.com/ja/actions/using-workflows/about-workflows
- https://docs.github.com/ja/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/automatically-merging-a-pull-request