はじめに
GitHub CI/CD実践ガイドを読みました。
こちらの本では、セキュリティや運用コストを意識したGitHub Actionsの設計や、単体テストや静的解析だけでなくDependabotの自動バージョンアップデートなど様々なワークフロー例が紹介されています。
これまで雰囲気でGitHub Actionsを触ってたのですが、GitHub Actionsをもっとよい設計・運用にできることを学べたので、その中で今すぐ取り入れられるプラクティスをいくつか紹介したいと思います。
無駄なコストを削減する/高速化する
自動キャンセル
例えば、プッシュもしくはプルリクエストをトリガーに動くワークフローでは、同じブランチに短期間で連続プッシュした場合、それぞれのコミット単位でワークフローが実行されると思います。このとき、先に実行していたワークフローを自動でキャンセルすることができるので、無駄なワークフローを実行するコストを減らせます。
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
ワークフローとブランチの組み合わせでConcurrencyグループを定義し、同じグループであれば実行中のワークフローをキャンセルする仕組みです。
タイムアウト
GitHub Actionsのデフォルトタイムアウトは360分です。万が一ワークフローが終了せず360分も実行したままだとこれも無駄なコストが発生しますし、終了できずに失敗することがすぐにわかりません。
そこで、ジョブまたはステップのタイムアウトを設定することができます。
jobs:
timeout:
timeout-minutes: <タイムアウトまでの時間(分)>
キャッシュ
公式のアクションには、様々な言語をセットアップするものがありますが、依存関係をキャッシュするオプションがあります。
# Nodeセットアップの例
steps:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
また、自分で自由にパスを指定してキャッシュするアクションもあります。
ファイルのダウンロードに時間がかかるケースは、そのファイルをキャッシュすることで時間の短縮につながります。
steps:
- uses: actions/cache@v4
with:
key: <キャッシュの検索に使うキー>
path: <キャッシュ対象のディレクトリパスまたはファイルパス>
restore-keys: <キャッシュミス時のリストアキー>
例えば自分は、actions/setup-node
では/node_modules
をキャッシュしない(マシン上のグローバルキャッシュ~/.npm
をキャッシュしている)ので、/node_modules
もキャッシュしたい場合とかにactions/cache
を使ったりしています。セットアップアクションの仕様は言語ごとに異なるので、詳しくはドキュメントをご参照ください。
# /node_modulesをキャッシュする例
steps:
- uses: actions/cache@v4
with:
key: ./node_modules
path: node_modules-${{ hashFiles('**/package-lock.json') }}
その他
例えば自動テストなどで並列実行の制御(実行環境のCPU数、ソフトウェアのI/O頻度のチューニングなど)によって高速化を図ることができます。このあたりのチューニングって意外と大事だと思います。他にも。自動テストや静的解析のプラクティスは本で紹介されています。
実行結果を見やすくする
ロギング
ワークフローコマンドを使うと、ワークフローの実行ログを自分で出力したり出力するログをグループ化して見やすくしたりすることができます。
debug
ワークフローコマンドを使うことで、デバッグログを出力できます。
※デバッグログを出力するにはEnable debug logging
の有効化が必要です。
run: echo "::debug::<デバッグ用メッセージ>"
group
endgroup
ワークフローコマンドを使うことで、↓のようにコマンドの中の出力が折りたたまれて表示されます。
run: |
echo "::group::ロググループ"
echo "以降の出力が折りたたまれる"
echo "::endgroup::
レポーティング
GitHub Actionsのページでワークフローの実行結果を見やすくするアノテーションとジョブサマリーを設定することができます。
アノテーションはerror
warning
notice
のワークフローコマンドを使うことで、ジョブの結果を↓のようにError/Warning/Noticeと分かりやすく表示してくれます。
- name: Error annotation
run: echo "::error::<エラーメッセージ>"
- name: Warning annotation
run: echo "::warning::<ワーニングメッセージ>"
- name: Notice annotation
run: echo "::notice::<メッセージ>"
ジョブサマリーではGITHUB_STEP_SUMMARY
環境変数にマークダウンテキスト出力することで、↓のようにアノテーションより多くの情報を出力することができます。
run: |
echo "# ジョブサマリー" >> "${GITHUB_STEP_SUMMARY}"
echo "- マークダウンテキストを書けます" >> "${GITHUB_STEP_SUMMARY}"
echo "- 実行結果が見やすくなります" >> "${GITHUB_STEP_SUMMARY}"
セキュリティに強くする
中間環境変数
github.pullrequests.title
やgithub.pullrequests.body
など、信頼できないコンテキストからスクリプトインジェクションが発生する可能性があります。この場合はそのコンテキストを直接ステップで使うのでなく、中間環境変数を使うことで回避することができます。
# 中間環境変数でプルリクエストのタイトルを使う
env:
PR_TITLE: ${{ github.pullrequests.title }}
steps:
- run: echo "${PR_TITLE}"
その他
その他にもクレデンシャルをマスクするなどのテクニックや、サードパーティのアクションを使うルールを策定するといったセキュリティに関する運用プラクティスが紹介されています。
おわりに
これらのプラクティス以外にもたくさんの事例が紹介されています。もっと詳しく知りたい方はぜひ本を読んでみてください!