GithubActionsをローカルで検証可能にする


# 概要
この記事は朝日新聞社 Advent Calendar 2022 の20日目の記事です。
朝日新聞デジタルの開発現場ではCI（継続的インテグレーション）ツールとしてGithub Actionsが利用されています。

エンタープライズ版で1ヶ月5,000分、無料版でも1ヶ月2,000分間は追加料金なしで利用できてとても便利ですが、テスト項目増加やデプロイフローが複雑化してくると上限時間を超過してしまうことがよくあります。（パブリックリポジトリは無制限です！！）

また、ちょっとしたGithubActionのデプロイフロー変更でもプルリクエスト（PR）をGithubにあげなくてはならないので非効率だと感じることがありました。

そこで、今回はGithubActionsを自身のローカル環境で試せるようにしたのでその方法をご共有しようと思います。

# 利用したライブラリ
https://github.com/nektos/act

# 成果物
Githubでサンプルコードを公開しております。 
https://github.com/nakamura-k30/github-actions-demo
公式のサンプルリポジトリをForkして動作確認で試したコードを追加しています。
本記事のコマンド例もこちらのサンプルコードに対応してます。

# 実行環境
OS： macOS Big Sur 11.6
Docker Desktop for Mac： v4.14.1 
Macで動作確認しておりますが、Windows/Linuxでも利用可能です。

# 導入手順
### 事前準備
dockerのインストールが必要です。
[Docker Desktop for Mac](https://docs.docker.com/desktop/install/mac-install/)、[Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/)、[Docker Engine for Linux](https://docs.docker.com/engine/install/)

### actインストール
macOS/Linux
```
brew install act
```

これだけで準備OKです！

# 利用方法
GithubActionsのワークフローが設定してあるリポジトリのルートで`act -l`を実行して実行可能なワークフローを表示してみます。
以降の作業はすべてリポジトリのルートで実行しています。

```
$ act -l
Stage  Job ID  Job name  Workflow name  Workflow file  Events           
0      test    test      CI             main.yml       push,pull_request
0      lint    lint      CI             main.yml       push,pull_request
0      test    test      Reminder       reminder.yml   push       
1      deploy  deploy    CI             main.yml       push,pull_request
```
`act -l`で各ジョブが何のワークフローに属しているかの一覧が表示されました。
Stageはジョブの実行の順序（前提条件があるか）、Eventsは実行に必要なGithub eventになります。

続いて`act -g`を実行するとワークフローをグラフ化できます。
```
$ act -g
 ╭──────╮ ╭──────╮ ╭──────╮
 │ test │ │ lint │ │ test │
 ╰──────╯ ╰──────╯ ╰──────╯
             ⬇
         ╭────────╮
         │ deploy │
         ╰────────╯
```
サッと全体像が掴めるので便利ですね。
ただ、`./.github/workflows/`配下のワークフローが全て出ているので少しわかりづらいです。
そのような場合は`-W`フラグで確認したワークフローファイルを指定できます。
```
 $ act -g -W "./.github/workflows/main.yml" 
 ╭──────╮ ╭──────╮
 │ test │ │ lint │
 ╰──────╯ ╰──────╯
        ⬇
    ╭────────╮
    │ deploy │
    ╰────────╯
```

それでは本題のジョブを実行してみます。
`act`とだけ実行すると全てのワークフローが動き出すので`act -j test -W "./.github/workflows/main.yml"`としてmain.ymlのtestジョブのみを実行します。
初回実行時だけ実行のために必要なimageサイズを聞かれるのでMediumを選択します。

```
$ act -j test -W "./.github/workflows/main.yml"
? Please choose the default image you want to use with act:

  - Large size image: +20GB Docker image, includes almost all tools used on GitHub Actions (IMPORTANT: currently only ubuntu-18.04 platform is available)
  - Medium size image: ~500MB, includes only necessary tools to bootstrap actions and aims to be compatible with all actions
  - Micro size image: <200MB, contains only NodeJS required to bootstrap actions, doesn't work with all actions

Default image and other options can be changed manually in ~/.actrc (please refer to https://github.com/nektos/act#configuration for additional information about file structure)  [Use arrows to move, type to filter, ? for more help]
  Large
> Medium
  Micro
...（中略）
[CI/test]   ✅  Success - Main test
[CI/test] ⭐ Run Post Setup Node
[CI/test]   🐳  docker exec cmd=[node /var/run/act/actions/actions-setup-node@v3/dist/cache-save/index.js] user= workdir=
[CI/test]   💬  ::debug::Caching for '' is not supported
[CI/test]   ✅  Success - Post Setup Node
[CI/test] 🏁  Job succeeded
```
Dockerイメージがダウンロードされてtestジョブの実行が成功しました。

これだけではGithubActionsのローカル検証ができるとはまだ言えないので、少し発展的な条件指定方法を紹介します。
- イベント指定
- アクター指定
- シークレット情報の指定
- github コンテキストの指定

## イベント指定
GithubActionsワークフロー内で起動条件は`on`で記載されています。
コマンドからイベントを指定する場合は、`act [<event>] [options]`とします。
eventには`pull_request`や`push`、`pull_request_target`など利用可能です。
何も指定しない場合は`push`になります。

ワークフローの記載例を示します。
```yml
name: Reminder
on:
  pull_request:
    types: [opened, synchronize]
```
この場合は`pull_request`でジョブを起動したいのでpull_requestと記載した場合のみ起動します。
```
実行可能
$ act pull_request -W "./.github/workflows/reminder.yml" 

実行されない（-vオプションで詳細表示）
$ act push -W "./.github/workflows/reminder.yml" -v 
```

起動条件で`on.pull_request.types`や`on.push.branches`を指定することもあると思いますが、actでは未対応となっているようでした。

詳細は[公式Wiki](https://github.com/nektos/act/wiki/GitHub-Actions-compatibility#workflow)のactでサポートしているオプションリストを参照して下さい。
個人的には`job.<job_id>.concurrency`　が利用できない点も残念でした。

## Githubアクターの指定
actコマンドでGithubアクターを指定するには`-a`オプションを利用します。

```
実行可能
$ act -a "nakamura-k30" -j lint
実行されない
$ act -a "dependabot[bot]" -j lint
```

## シークレット情報の指定
actコマンドでワークフロー内で利用されるシークレット情報を指定するには`-s`オプションを利用します。

```yml
    steps:
      - uses: actions/checkout@v3
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }}
          aws-region: ap-northeast-1
```
上のワークフローで利用されるシークレット情報を指定する場合は以下のコマンドになります。
```
$ act push -s AWS_IAM_ROLE_ARN=XXX
```
実行ログにもシークレット情報は`***`とマスクされた状態で出力されます。

## github コンテキストの指定
最後に、`github.*`でワークフロー内で取得可能なgithub コンテキストを指定する方法を紹介します。
github コンテキストには、ワークフローの実行とその実行をトリガーしたイベントの情報が含まれます。
 
各イベント（push、pull_requestなど）ごとに付与できるペイロードは決まっています。
actコマンドでは各イベントのペイロードを用意しておけば実行時に指定することが可能になります。

pushとpull_requestの場合の例になります。

[pushイベントのペイロード](https://docs.github.com/ja/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#push)
ペイロード例
```json:push-payload.json
{
  "ref": "refs/heads/master"
}
```
この値は、ワークフロー内で`github.ref`として参照可能になります。

[pull_requestイベントのペイロード](https://docs.github.com/ja/developers/webhooks-and-events/webhooks/webhook-events-and-payloads?actionType=assigned#pull_request)
ペイロード例
```json:pull-request-payload.json
{
  "action": "opened",
  "pull_request": {
    "head": {
      "ref": "sample-head-ref"
    },
    "base": {
      "ref": "sample-base-ref"
    }
  },
  "number": 123
}
```
この値は、ワークフロー内で`github.base_ref`、`github.head_ref`、`github.head_ref`、`github.number`として参照可能になります。

以下のような`github.ref`がmasterブランチに向いているときにのみ動き出すワークフローでgithub コンテキストを指定します。
```yml
deploy:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/master' && github.actor != 'dependabot[bot]' && success()
    needs: [lint, test]
```

`-e`オプションで作成したイベントのペイロードへのパスを記載します。
```
$ act push -e push-payload.json -a "nakamura-k30" -s AWS_IAM_ROLE_ARN=XXX
```


# 終わりに
当たり前のことですがローカルにシークレット情報を持ってきて検証する場合は十分に注意が必要ですのでローカルでの管理はお気をつけください。
actコマンドは色々なオプションがあり便利でしたが一部対応していない機能もあるので研修やデプロイフローの理解のために役立てれると良いなと思いました。
今後も良いデプロイフローを構築できるように試行錯誤してみたいと思います！


# 参考
https://github.com/nektos/act
https://github.com/cplee/github-actions-demo
https://github.co.jp/features/actions
https://docs.github.com/ja/developers/webhooks-and-events/events/github-event-types
https://github.com/nektos/act/wiki/GitHub-Actions-compatibility#workflow