Open17

GitHub Actions のベストプラクティス

概要

GitHub Actions のワークフローを書くときにこうした方がいいかな?と思ったことをメモっていくスクラップ。一般論ではなく個人的なもの。
提案や質問や相談も受け付けます。

1 フロー 1 ワークフロー

一連のフローがある場合は 1 つのワークフローにまとめる。

  • トリガーしたイベントの JSON が使える
  • needs での制御がしやすい
  • 全体を追える
  • グラフが表示される

ファイルを分割したい

ファイルを分割したい理由として以下が挙げられると思います。

  • 行数が増えて読みづらい
  • 処理を共通化したい

複合実行ステップアクションworkflow_run トリガー を使うことになると思いますが、基本的には一連のフロー制御はメインのファイルに書いてその下を複合実行ステップアクションで外部ファイルへ分離するのが良さそう。

デバッグ

ログを増やす

デバッグロギングの有効化 - GitHub Docs

ローカルで動かす

nektos/act: Run your GitHub Actions locally 🚀

SSH する

GitHub Actions で SSH デバッグ! (debugging-with-tmate)

動いている最中の処理を中断してインスタンスへ SSH 接続することができます。
GitHub からすると意図しない使い方と思われるので怒られることはあるかもしれません。自己責任でお願いします。

試行錯誤用リポジトリを作る

サンドボックスとして利用できる試行錯誤用のリポジトリを作っておくと便利。
public なリポジトリだと無料。

ペイロードを出力しておく

$GITHUB_EVENT_PATH または ${{ github.event_path }} にペイロードが保存されているので出力しておくと使いたいプロパティを確認できます。

- run: cat $GITHUB_EVENT_PATH

追加のトリガーを設定する

イベントによって github.event に送られてくる情報が異なるので使えるケースは限られますが追加で以下のイベントを設定しておくと便利。

workflow_dispatch イベント

手動実行イベント on.workflow_dispatch を設定しておきます。

push イベント

ワークフローファイル自身の更新イベントを登録しておくと修正後すぐに走ってくれる。

.github/workflows/example.yml
on:
  push:
    paths:
      - .github/workflows/example.yml

学習

公式ドキュメントにチュートリアルがあります。
GitHub Actions について学ぶ - GitHub Docs

書籍やブログなども参考になるとは思いますが情報がすぐ古くなってしまうので初心者にはおすすめしません。
公式ドキュメントでほとんどの情報は網羅されています。

注意

公式ドキュメントも頻繁に更新されています。
特に日本語ドキュメントはページ内リンクが切れていることが多いのでページ内の特定の項目へリンクしたい場合は英語のページへリンクしておいた方が無難です。
リンク先が英語ドキュメントだった場合はページ上部で言語を切り替えられます。

翻訳が追いついていなかったりおかしかったりするページもあります。
翻訳修正の Pull request は受け付けていないようなのでページ下部の Contact support から報告してあげてください。

その他情報

個人的に役に立ちそうな記事などをまとめてます。

https://github.com/SnowCait/git-notes/blob/master/GitHubActions.md

skip ci

公式機能が実装されたのでまとめました。

https://zenn.dev/snowcait/articles/ef60401313a3fc

以下の情報は古いです。

多くの CI サービスにはコミットメッセージに [skip ci][ci skip] と入れておくと CI 実行をスキップしてくれる機能があります。
複数のコミットを同時にプッシュした場合はどれか 1 つに含まれていたらスキップされます。
GitHub Actions では公式にこの機能がありませんので自前実装が必要です。

自前実装の前に

GitHub Actions ではイベントをフィルタすることができます。
まずはこちらで実現できないか検討しましょう。
branchespaths などがよく使われます。

GitHub Actionsのワークフロー構文 - GitHub Docs

以下は未検証。参考にする場合は要検証です。

アクションを使って実装

アクション 備考
Skip CI action HEAD のみ
CI-SKIP-ACTION
Skip Workflow

自前実装

イベントによって異なる。
コミットメッセージを見る方法は on.push では使えるが、 on.pull_request では使えない。

コミットメッセージ(HEAD のみ)

jobs:
  build:
    runs-on: ubuntu-latest
    if: !contains(github.event.head_commit.message, '[skip ci]')

コミットメッセージ(全てのコミット)

jobs:
  build:
    runs-on: ubuntu-latest
    if: !contains(github.event.commits.*.message, '[skip ci]')

Pull request タイトル

jobs:
  build:
    runs-on: ubuntu-latest
    if: !contains(github.event.pull_request.title, '[skip ci]')

Pull request 本文

jobs:
  build:
    runs-on: ubuntu-latest
    if: !contains(github.event.pull_request.body, '[skip ci]')

Pull request ラベル

jobs:
  build:
    runs-on: ubuntu-latest
    if: !contains(github.event.pull_request.labels.*.name, 'skip ci')

結論

HEAD だけを見たいか、全てのメッセージを見たいか、 Pull request 単位で見たいか、あるいは対象としたいメッセージはケースバイケースなので自前実装して on.push はコミットメッセージを、 on.pull_request はタイトルや本文を見るのがいいのではないかと思います。

cron として使いたい

GitHub ホストランナーでやるのはやめましょう。
サーバーが混みあっているときはかなり遅延します。
落ちていることもあります。
不安定でも問題ないものは可。
使う場合は UTC なので注意。

セルフホストランナーでも遅延するかは要検証。

セキュリティ

GitHub Actions はサードパーティのアクションを作れる/使えるのが魅力ですが、セキュリティが気になることもあります。

使用するアクションを制限

使用を許可するアクションをリポジトリの [Settings] > [Actions] で制御できます。

バージョンの指定

通常は snow-actions/tweet@v1.0.0snow-actions/tweet@main のように指定すると思いますが、タグやブランチは指しているコミットを変更可能なのでいつの間にか悪意あるコードに書き換わっているかもしれません。
そういったことを気にする場合はコミットハッシュで指定することができます。
snow-actions/tweet@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
こうしておけばまず書き換わることはありません。

ハッシュはセキュリティの観点から省略できないようになりました。
GitHub Actions: Short SHA deprecation - GitHub Changelog

サードパーティアクションを使う場合には公開されているコードを読んでコミットハッシュでバージョンを指定しておけば安全に利用できます。

https://docs.github.com/ja/actions/learn-github-actions/security-hardening-for-github-actions
https://blog.ryotak.me/post/github-actions-supplychain/

入力値のインジェクション

run に直接 ${{ }} を書くと先に展開されてしまう。
入力値にコマンドが含まれる場合実行されてしまうので env を経由すること。
Issue や PR のタイトルはもちろん、ブランチ名も含まれるので注意。

https://securitylab.github.com/research/github-actions-untrusted-input/

GitHub ホストランナー or セルフホストランナー

GitHub ホストランナーの仕様 - GitHub Docs
セルフホストランナーについて - GitHub Docs

  • GitHub ホストランナーのスペックでは足りない
  • 環境 が GitHub の都合で更新されて動かなくなる
  • 障害のコントロール
  • 事前にソフトウェアのインストールをしたい
    • ライセンス
    • インストールに時間がかかる、コマンドからできない
  • 無料枠を大幅に超えるので課金するよりセルフホストした方が安い
  • 実行時間が GitHub ホストランナーのタイムアウトを超える
  • 並列実行数を増やしたい
  • リポジトリが大きく毎回 clone すると遅いのでインスタンスを使いまわす
  • Docker イメージを使いまわす
  • GitHub Enterprise Server を使っている
  • セキュリティ
    • アクセスキーを発行したくない

アクションの作成

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

種類

Docker コンテナアクション、 JavaScript アクション、複合実行ステップアクションの3種類がある。
1から作るなら JavaScript アクション。
Docker 資産があるなら Docker コンテナアクション。
複数のステップをまとめたいなら複合実行ステップアクション。

Docker コンテナ JavaScript 複合実行ステップ
OS Linux Linux, MacOS, Windows Linux, MacOS, Windows
速度 遅い 速い -
備考 uses は使えない
uses も使えるようになりました

API アクセス

GitHub Enterprise Server との互換性のため GITHUB_API_URL, GITHUB_GRAPHQL_URL 環境変数を使う。

リリース

ユーザーがアクションを利用する際に @ でバージョンを指定できる。
ここにはタグ、ブランチ、コミットの SHA が利用できる。
ブランチやメジャーバージョンのみのタグでの運用はバグでユーザーの環境を壊しかねない。
基本的には セマンティック バージョニング されたタグ指定かコミットの SHA を推奨。

steps:
    - uses: actions/javascript-action@v1.0.0 # タグ
    - uses: actions/javascript-action@v1-beta # ブランチ
    - uses: actions/javascript-action@172239021f7ba04fe7327647b213799853a9eb89 # SHA

複合実行ステップアクションでusesは今はもう使えるようになってるみたいです。

ありがとうございます、情報を更新しました。

GitHub ホストランナーでの OS 選択

runs-on の指定は ubuntu-20.04ubuntu-18.04 を推奨。
コストは Ubuntu < Windows <<<<< Mac なので特にこだわりがなければ Ubuntu 一択。
*-latest はバージョンが変わってワークフローが動かなくなることがあるのでバージョンまで指定しましょう。

OS 分の倍率
Linux 1
Windows 2
macOS 10

https://docs.github.com/ja/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions

複数の Docker コンテナを組み合わせたテスト

docker-compose.yml に定義した方が良さそう。
ローカルでも実行できます。

containerservices を使って組むこともできますがローカルで実行できないのでデバッグが大変。

ロジックを書きたい

Jenkins だと Groovy を使ってロジックが書けたりします。
しかし GitHub Actions は YAML で書くため最低限の関数や if くらいしか用意されていません。
ロジックを書くためには actions/github-script や自作アクションを作る(リポジトリ内/外)必要があります。

ですが、YAML にロジックを前に考えてほしいのがそのロジックは GitHub Actions でしか実行できないということです。
例えばテストを docker-compose を使ってコマンド1つで実行できるようにしておけば YAML で素直に書けますしローカルでも実行ができます。
この 「ローカルで実行できる」 というのは大きなメリットです。デバッグも容易にできます。
YAML にロジックを書き始める前にそれは本当に必要か再考してみてください。

ローカルでも実行したい

テスト(デプロイスクリプトも?)のようにローカルと CI の両方で実行したいものに関しては Docker や Gradle などマルチ OS で動くものを使ってラップしておく。

ログインするとコメントできます