1.この記事の立ち位置
自分がいつも調べていること、忘れがちな Tips や小ネタを列挙していく。そのため、網羅性は重視しない。
というのも、なにか調べていていろいろ読み漁った挙げ句、1周回って行き着くところは GitHub Actions の公式ドキュメントであり、たとえば Workflow の書き方は以下のページをよく開いている。
Workflow syntax for GitHub Actions - GitHub Docs
それでも、公式ドキュメントで参照したい箇所を引っ張るための用語を知るまでに苦労することが往々にあり、この記事が、公式ドキュメントで参照したい箇所を導くための助けとなればと思い、書いていく。
2.Step と Job と Workflowの違いアレコレ
2-1.Step と Job と Workflow の違いの一行まとめ
Step < Job < Workflow
2-2.Step と Job と Workflow の違いの三行まとめ
Step
はGitHub Actions Workflow の最小単位であり、run
やuses
で指定するものJob
はコンテナや VM のことを指し、runs-on
で指定するものであり、ひとつないしは複数のStep
を抱えているWorkflow
はひとつ以上のJob
を束ねている
これらについて端的にまとめた図が公式ドキュメントで掲載されているのでそちらもぜひ。
Understanding GitHub Actions - GitHub Docs
2-3.Step と Job と Workflow それぞれの分け方・分割の基準
Step
を分けるときは、run
、uses
、name
が異なるとき- 以下のように書けば、ひとつの
Step
に複数のコマンドを始めとする処理を書くことができる
- 以下のように書けば、ひとつの
- name: Install Dependencies
run: |
npm ci
npm run build
- name: Run test
run: npm run test
Job
を分けるときはruns-on
が異なるときや、needs
でJob
単位で実行制御をしたいときWorkflow
を分けるときは、トリガーが違うとき- e.g.
push
,pull_request
,workflow_dispatch
- cf. Events that trigger workflows - GitHub Docs
- e.g.
3.Step の実行制御や Step の参照方法
以下のような成功するときもあれば失敗するときもある、不安定な Step
があるとしよう。そして、このような不安定な Step
の結果に応じて後続の Step
を実行するかしないか決めたいとする。
steps:
# 略
- name: Generate 0 or 1
id: generate_number
run: echo "::set-output name=random_number::$(($RANDOM % 2))"
- name: Pass or fail # 不安定な Step
run: >
if [[ ${{ steps.generate_number.outputs.random_number }} == 0 ]]; then
exit 0; else exit 1; fi
# 以下に実行したい Step が続く
- run: echo "Hello, World!"
上記のような特に何もしていない場合、「Pass or fial」で失敗した(exit 1 となった)際には、後続の “Hello, World!” を出力する Step
は実行されない。
3-1.後続の Step をお手軽に実行するか決めたい
後続の Step
をお手軽に実行するか決めたい場合、真っ先に挙げられるのが if:always()
, if:success()
, if:failure()
だろう。
※ なお、各 step
では if:success()
が初期値として扱われているために、コケた step
の後続の step
の処理はおこなわれない こととなっている。
A default status check of success() is applied
unless you include one of these functions.
※ したがって、以下のように 成否を無視してよい step
の直後の step
に if:always()
さえ書いておけば、後続の全ての step
は実行されることとなる。もちろん、if:always()
を適用した step
がコケた場合、それ以降の step
は実行されない。
steps:
# 略
- name: Generate 0 or 1
id: generate_number
run: echo "::set-output name=random_number::$(($RANDOM % 2))"
- name: Pass or fail # 不安定な Step
run: >
if [[ ${{ steps.generate_number.outputs.random_number }} == 0 ]]; then
exit 0; else exit 1; fi
# 以下に実行したい Step が続く
- run: echo "Hello, World!"
if: always()
#if: success() 直前の「Pass or fail」が成功したときのみ実行。なお、success()は初期値。
#if: failure() 直前の「Pass or fail」が失敗したときのみ実行
3-2.Step の出力結果に応じて決めたい
Step
の出力結果に応じて、後続の Step
を実行するか決めたいときもある。そういうときは steps.<step_id>.outputs.<output_name>
がおすすめ!
※ if:always()
, if:success()
, if:failure()
では直前の成否でしか実行制御できないが、steps.<step_id>.outputs.<output_name>
は 同じ Job 内の任意の Step の成否 で実行制御ができる。
steps:
# 略
- name: Generate 0 or 1
id: generate_number
run: echo "::set-output name=random_number::$(($RANDOM % 2))"
- run: echo "OK"
if: ${{ steps.generate_number.outputs.random_number == 0 }}
- run: echo "Failed"
if: ${{ steps.generate_number.outputs.random_number == 1 }}
3-3.不安定な Step の成否問わず後続の Step を処理したい
ここまでは、if:always()
や outputs
など「後続の Step 」で実行制御するようにしてきた。
steps:
# 略
- name: Pass or fail # 不安定な Step
run: >
if [[ ${{ steps.generate_number.outputs.random_number }} == 0 ]]; then
exit 0; else exit 1; fi
# 以下に実行したい Step が続く
- run: echo "Alice"
if: always()
- run: echo "Bob"
- run: echo "Charlie"
別案として、不安定な step
に continue-on-error: true
を指定しても実行した Step の成否問わず、後続の Step を処理することができる。
こちらのほうが失敗を許容する step
をハッキリコード上で表現できると思うので、continue-on-error: true
を推したい。
steps:
# 略
- name: Generate 0 or 1
id: generate_number
run: echo "::set-output name=random_number::$(($RANDOM % 2))"
- name: Pass or fail # 不安定な Step
continue-on-error: true
run: >
if [[ ${{ steps.generate_number.outputs.random_number }} == 0 ]]; then
exit 0; else exit 1; fi
# 以下に実行したい Step が続く
- run: echo "Alice"
- run: echo "Bob"
- run: echo "Charlie"
3-4.不安定な Step の後続の Step で不安定な Step の成否に応じて条件分岐させたい
さて、continue-on-error: true
を指定すると指定された Step
は失敗していたとしても後続の Step は一様に処理されることになる。これはこれでいいのだが、continue-on-error: true
が指定された Step
の本来の成否に応じて、後続の Step
から実行するものを決めることはできるのだろうか?
GitHub Actions の steps.<step_id>.outcome
を使うとそれができる
steps:
# 略
- name: Generate 0 or 1
id: generate_number
run: echo "::set-output name=random_number::$(($RANDOM % 2))"
- name: Pass or fail # 不安定な Step
id: generate_number_outputs
continue-on-error: true
run: |
if [[ ${{ steps.generate_number.outputs.random_number }} == 0 ]]; then
exit 0; else exit 1; fi
- name: "steps.generate_number_outputs.outcome の値確認"
run: echo "${{ steps.generate_number_outputs.outcome }}"
- name: "success のときだけ"
if: ${{ steps.generate_number_outputs.outcome == 'success' }}
run: echo "OK"
- name: "failure のときだけ"
if: ${{ steps.generate_number_outputs.outcome == 'failure' }}
run: echo "Failed"
3-5.steps.<step_id>.outcome とsteps.<step_id>.conclusion の違い
3-5-1.ポイント
continue-on-error:true
の適用前と後、どちらのstepの実行結果を知りたいか?
steps.<step_id>.outcome
はcontinue-on-error:true
の適用前のStep
の実行結果。continue-on-error: true
を適用しているStep
の本来の実行結果
steps.<step_id>.conclusion
は適用後のStep
の実行結果。- またどちらも最終的な結果は
success
となるので、後続のStep
の実行が妨げられることはない。
3-5-2.steps.<step_id>.outcome とsteps.<step_id>.conclusion の違いに着目したサンプルコード
steps:
# 略
- name: Generate 0 or 1
id: generate_number
run: echo "::set-output name=random_number::$(($RANDOM % 2))"
- name: Pass or fail # 不安定な Step
id: generate_number_outputs
continue-on-error: true
run: |
if [[ ${{ steps.generate_number.outputs.random_number }} == 0 ]]; then
exit 0; else exit 1; fi
- name: "steps.generate_number_outputs.outcome の値確認"
run: echo "${{ steps.generate_number_outputs.outcome }}"
- name: "steps.generate_number_outputs.conclusion の値確認"
run: echo "${{ steps.generate_number_outputs.conclusion }}"
- name: "success のときだけ"
if: ${{ steps.generate_number_outputs.outcome == 'success' }}
run: echo "OK"
- name: "failure のときだけ"
if: ${{ steps.generate_number_outputs.outcome == 'failure' }}
run: echo "Failed"
参考:Actions の実行ログ 参考:Contexts - GitHub Docs
4.Job の実行制御や Job をまたいだ Step の参照方法
4-1.Job をまたいで Step の実行を制御したり出力結果を参照したい
いよいよ、Job をまたいで Step の実行を制御したり出力結果を参照するという、これもまたありがちなケースを取り上げたい。
結論としては、Step
の実行結果を、別の Job
で参照するにはは jobs.<job_id>.outputs
を使うと出来る。
このとき <job_id>
は 参照する側の Job
で needs: <参照したい Step を抱える job_id>
を指定する必要がある。
4-1-1.サンプルコード
以下のサンプルコードをみてほしい。Step
の実行結果を参照したい Job
は random-number-output
であり、その Step
が実行される Job
を randomly-failing-job
とする。
jobs:
randomly-failing-job:
runs-on: ubuntu-latest
outputs:
steps:
# 略
- name: Generate 0 or 1
id: generate_number
run: echo "::set-output name=random_number::$(($RANDOM % 2))"
- name: Pass or fail # 不安定な Step
id: generate_number_outputs
continue-on-error: true
run: |
if [[ ${{ steps.generate_number.outputs.random_number }} == 0 ]]; then
exit 0; else exit 1; fi
random-number-output:
runs-on: ubuntu-latest
needs: randomly-failing-job
steps:
- name: Echo zero
if: ${{ needs.randomly-failing-job.outputs.random-number-output == 0}}
run: |
echo "AWESOME!!"
echo ${{ needs.randomly-failing-job.outputs.random-number-output }}
- name: Echo one
if: ${{ needs.randomly-failing-job.outputs.random-number-output == 1}}
run: |
echo "One more time!"
echo ${{ needs.randomly-failing-job.outputs.random-number-output }}
4-1-2.Step の実行結果を別の Job で参照するための条件
Step
の実行結果を別の Job
で参照するための条件は、参照したい Step
を含む Job
が完走するまで待機することである。
このような「ある Job
の完走が終わるまで、別の Job
の実行はせず、待機させる」ためにうってつけなのが、needs:<参照したい job_id>
となっている。
X.他
X-1.Workflow Dispatch の実行場所
以下の Workflows の画面から on: workflow_dispatch
と Workflow Dispatch
をトリガーとして使うように指定しているワークフローを選択すると、実行するブランチが表示される。
https://github.com/<アカウント名>/<リポジトリ名>/actions
注意点
注意点は、Manually running a workflow - GitHub Docs で書いてあるとおり、そのリポジトリのデフォルトブランチ(mainなど)で Workflow Dispatch
をトリガーとして使うようにする必要があるということ。
To run a workflow manually, the workflow must be configured to run on the workflow_dispatch event. To trigger the workflow_dispatch event, your workflow must be
in the default branch
.
つまり、デフォルトブランチの Workflow に以下のように書かれていなければ、workflow_dispatch
をトリガーとして Workflow を実行することが出来ないということだ。
on:
workflow_dispatch:
X-2.Workflow のログはどこにある?
Actions タブからログを見たい Workflow
、Job
、Step
を順番に選択する。Step
を選択する際には、>
をクリックする必要がある。
X-3. run: に書くコマンドが長いので改行したい
バックスラッシュで改行を入れるとエラーになってしまう。
run:
if [[ ${{ steps.generate_number.outputs.random_number }} == 0 ]]; then \
exit 0; else exit 1; fi
>
を使えばok! >
は Folded Style
という名前があることを書きながら調べて知った、、。
run: >
if [[ ${{ steps.generate_number.outputs.random_number }} == 0 ]]; then
exit 0; else exit 1; fi
参考:813-folded-style | YAML Ain’t Markup Language (YAML™) revision 1.2.2
X-4.ひとつの run: に複数のコマンドを書きたい
こんどはひとつの run:
に複数のコマンドを書きたい場合だ。
こちらは、|
であり、Literal Style
と言うらしい。
- name: Install Dependencies
run: |
npm ci
npm run build
参考:#812-literal-style | YAML Ain’t Markup Language (YAML™) revision 1.2.2
X-5.GITHUB_TOKEN のデフォルトの権限・スコープを確認したい
Permissions for the GITHUB_TOKEN | Automatic token authentication - GitHub Docs の表の Default access(permissive)
という列に列挙されている。
あるいは、Actions のログのページから、権限・スコープを確認したい Jobの名前, Set up job
, GITHUB_TOKEN Permission
の順番にクリックしても確認できる。
X-6.permissions で権限 / scope を指定すると、デフォルトの権限に追加されることになる?
permissions
で権限 / scope を指定するとデフォルトの権限、Default access(permissive)
を上書きされる。permissions
で指定していない権限については metadata
のみ Read
権限が付与されることを除いて no access
(何も権限が付与されない)となる。
When the permissions key is used,
all unspecified permissions are set to no access, with the exception of the metadata scope, which always gets read access.
参考:Permissions for the GITHUB_TOKEN | Automatic token authentication - GitHub Docs
※ そもそも GITHUB_TOKEN
とは、ワークフローを実行すると ${{ secrets.GITHUB_TOKEN }}
という書式でワークフローのなかで使うことが出来るシークレット。Personal Access Token (PAT)
と違い、シークレットの管理をする必要がない。
参考:Automatic token authentication - GitHub Docs
X-7. permissions ってどこに書けばいいの?
特定の job
のみ、あるいはグローバル、どちらでも書くことが出来るが、原則としては、permissions は必要な job に「最小限の権限のみ」 払い出すべきだろう。
また、特定の step
にのみ権限を払い出すことはできなかった。エラーメッセージは Unexpected value 'permissions'
だった。
# 全べての job に適用する必要がなければ、ココに書くことは避けたほうがいい
#permissions:
# contents: write
# pull-requests: write
jobs:
randomly-failing-job:
#略
random-number-output:
runs-on: ubuntu-latest
needs: randomly-failing-job
# permissions は必要な job に「最小限の権限のみ」払い出すのべき!
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v3
#略
- name: Create Pull Request
id: cpr
# 特定の step にのみ権限を払い出すことはできなかった
# Unexpected value 'permissions'
#permissions:
# contents: write
# pull-requests: write
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "mod: output.txt"
title: "Generate 0 or 1? ${{ needs.randomly-failing-job.outputs.random-number-output }}"
body: "Generate 0 or 1? ${{ needs.randomly-failing-job.outputs.random-number-output }}"
払い出す権限の探し方はいろいろあると思うが、一例としては、使う Action の input
などの項目を確認することだ。
たとえば、上で書いているサンプルコードで使われている peter-evans/create-pull-request@v4
を例に挙げると、ドキュメントの Action inputs
に token
に、必要な権限が書かれている。
token
GITHUB_TOKEN (permissions contents: write and pull-requests: write)
or a repo scoped Personal Access Token (PAT).
GITHUB_TOKEN
と Personal Access Token (PAT)
どちらを使うべきか?という点については、シークレットの管理の煩わしさを踏まえると、GITHUB_TOKEN
に軍配があがるのでは?と考える。
X-8.公開されている Actions のいいカンジの探し方
awesome actions
などでググるとよさそう。以下はその一例。
sdras/awesome-actions: A curated list of awesome actions to use on GitHub
Y.自分のブログから参考になりそうなものをご紹介
Actions 上でマニフェストのイメージタグを更新したい
[addnab/docker-run-action]GitHub Actionsでyqのコンテナイメージを使ってマニフェストを更新する | gkzz.dev
repository_dispatchとworkflow_dispatchの使い分けや書き方の違い
[GitHub Actions]repository_dispatchとworkflow_dispatchの使い分けや書き方の違いをまとめてみた | gkzz.dev
schedule event がうまく起動しない、発火しないときに確認したい cron の書き方
Github Actions の schedule で日時と曜日を指定することができなかったけどなんとかした | gkzz.dev