GitHub Actionsワークフローを今よりちょっと最適化してみる〜GitHub CI/CD実践ガイドを読んで〜

# はじめに
GitHub CI/CD実践ガイドを読みました。

https://gihyo.jp/book/2024/978-4-297-14173-8

こちらの本では、セキュリティや運用コストを意識したGitHub Actionsの設計や、単体テストや静的解析だけでなくDependabotの自動バージョンアップデートなど様々なワークフロー例が紹介されています。
これまで雰囲気でGitHub Actionsを触ってたのですが、GitHub Actionsをもっとよい設計・運用にできることを学べたので、その中で今すぐ取り入れられるプラクティスをいくつか紹介したいと思います。

# 無駄なコストを削減する/高速化する

## 自動キャンセル

https://docs.github.com/ja/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs

例えば、プッシュもしくはプルリクエストをトリガーに動くワークフローでは、同じブランチに短期間で連続プッシュした場合、それぞれのコミット単位でワークフローが実行されると思います。このとき、先に実行していたワークフローを自動でキャンセルすることができるので、無駄なワークフローを実行するコストを減らせます。

```yaml
on:
  pull_request:
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
```
ワークフローとブランチの組み合わせでConcurrencyグループを定義し、同じグループであれば実行中のワークフローをキャンセルする仕組みです。

## タイムアウト

GitHub Actionsのデフォルトタイムアウトは360分です。万が一ワークフローが終了せず360分も実行したままだとこれも無駄なコストが発生しますし、終了できずに失敗することがすぐにわかりません。
そこで、ジョブまたはステップのタイムアウトを設定することができます。

```yaml
jobs:
  timeout:
    timeout-minutes: <タイムアウトまでの時間（分）>
```

## キャッシュ

https://docs.github.com/ja/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows

公式のアクションには、様々な言語をセットアップするものがありますが、依存関係をキャッシュするオプションがあります。
```yaml
# Nodeセットアップの例
steps:
  - uses: actions/setup-node@v4
    with:
      node-version: 20
      cache: npm
```

また、自分で自由にパスを指定してキャッシュするアクションもあります。
ファイルのダウンロードに時間がかかるケースは、そのファイルをキャッシュすることで時間の短縮につながります。

```yaml
steps:
  - uses: actions/cache@v4
    with:
      key: <キャッシュの検索に使うキー>
      path: <キャッシュ対象のディレクトリパスまたはファイルパス>
      restore-keys: <キャッシュミス時のリストアキー>
```
例えば自分は、`actions/setup-node`では`/node_modules`をキャッシュしない（マシン上のグローバルキャッシュ`~/.npm`をキャッシュしている）ので、`/node_modules`もキャッシュしたい場合とかに`actions/cache`を使ったりしています。セットアップアクションの仕様は言語ごとに異なるので、詳しくはドキュメントをご参照ください。

```yaml
# /node_modulesをキャッシュする例
steps:
  - uses: actions/cache@v4
    with:
      key: ./node_modules
      path: node_modules-${{ hashFiles('**/package-lock.json') }}
```

## その他
例えば自動テストなどで並列実行の制御（実行環境のCPU数、ソフトウェアのI/O頻度のチューニングなど）によって高速化を図ることができます。このあたりのチューニングって意外と大事だと思います。他にも。自動テストや静的解析のプラクティスは本で紹介されています。

# 実行結果を見やすくする

## ロギング
ワークフローコマンドを使うと、ワークフローの実行ログを自分で出力したり出力するログをグループ化して見やすくしたりすることができます。

https://docs.github.com/ja/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions

`debug`ワークフローコマンドを使うことで、デバッグログを出力できます。
※デバッグログを出力するには`Enable debug logging`の有効化が必要です。
```yaml
run: echo "::debug::<デバッグ用メッセージ>"
```

`group` `endgroup`ワークフローコマンドを使うことで、↓のようにコマンドの中の出力が折りたたまれて表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3735277/a7101b9f-6a9d-874c-2282-41c76cc036bf.png)

```yaml
run: |
  echo "::group::ロググループ"
  echo "以降の出力が折りたたまれる"
  echo "::endgroup::
```

## レポーティング
GitHub Actionsのページでワークフローの実行結果を見やすくするアノテーションとジョブサマリーを設定することができます。

アノテーションは`error` `warning` `notice`のワークフローコマンドを使うことで、ジョブの結果を↓のようにError/Warning/Noticeと分かりやすく表示してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3735277/d986664f-9285-abad-5896-e9e626deab6e.png)

```yaml
- name: Error annotation
  run: echo "::error::<エラーメッセージ>"
- name: Warning annotation
  run: echo "::warning::<ワーニングメッセージ>"
- name: Notice annotation
  run: echo "::notice::<メッセージ>"
```

ジョブサマリーでは`GITHUB_STEP_SUMMARY`環境変数にマークダウンテキスト出力することで、↓のようにアノテーションより多くの情報を出力することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3735277/c7449ffb-f0e6-5828-a02a-56bb02256ef6.png)

```yaml
run: |
  echo "# ジョブサマリー" >> "${GITHUB_STEP_SUMMARY}"
  echo "- マークダウンテキストを書けます" >> "${GITHUB_STEP_SUMMARY}"
  echo "- 実行結果が見やすくなります" >> "${GITHUB_STEP_SUMMARY}"
```

# セキュリティに強くする

## 中間環境変数
`github.pullrequests.title`や`github.pullrequests.body`など、信頼できないコンテキストからスクリプトインジェクションが発生する可能性があります。この場合はそのコンテキストを直接ステップで使うのでなく、中間環境変数を使うことで回避することができます。

```yaml
# 中間環境変数でプルリクエストのタイトルを使う
env:
  PR_TITLE: ${{ github.pullrequests.title }}
  steps:
    - run: echo "${PR_TITLE}"
```

## その他
その他にもクレデンシャルをマスクするなどのテクニックや、サードパーティのアクションを使うルールを策定するといったセキュリティに関する運用プラクティスが紹介されています。

# おわりに
これらのプラクティス以外にもたくさんの事例が紹介されています。もっと詳しく知りたい方はぜひ本を読んでみてください！