GitHub Actions の Composite Action (複合ステップアクション) は便利なのですが、制約や歯がゆいことが多く悩ましいものがあります。
では何が難しいと感じているのか、その対処をどうしているのかメモしておきます。
tl;dr;
- Composite Action は run のみ使える。uses は使えないからあきらめて
- Composite Action は run.if が使えないので bash if で分岐しよう
- Composite Action でスクリプト使うならコンテナ実行時にパス狂うから気を付けて
- Compoiste Action の全 run ステップは Grouping log lines を使おう、絶対だ
Composite Actions とは
GitHub Actions は、Jobで実際にやる処理1つ一つを step として記述できます。
この step で run:
を使っていろいろな処理を書いたり uses:
を使ってアクションを呼び出したりしていることでしょう。
さて、プロジェクトでいろいろな workflow を用意していくと、似通った run step を記述していてまとめ上げたくならないでしょうか。 TypeScriptやDockerアクションにするというわけではなく、単純に run step のYAMLを分離して呼び出すことで共通化したい。
こんな時に便利なのが Composite Action です。
Composite Actions の利用例
例えば、次のように jobA, jobB, jobC それぞれで dotnet build / publish をしているときに、このdotnet 処理を別のYAMLに記述して呼び出せれば便利、みたいな感じです。(これを分離するのに価値があるかはおいておいて、まとめ上げられるというのに注目)
jobs: jobA: runs-on: ubuntu-latest steps: - run: dotnet restore - run: dotnet build -c Debug - run: dotnet publish -c Debug - run: nanika yaru jobB: runs-on: ubuntu-latest steps: - run: dotnet restore - run: dotnet build -c Debug - run: dotnet publish -c Debug - run: betsu no nanika yaru jobC: runs-on: ubuntu-latest steps: - run: dotnet restore - run: dotnet build -c Release - run: dotnet publish -c Release - run: tondemo naikoto yaru
Composite Actions を使うようにしてみましょう。
やることは単純です。ローカルAction として、.github/actions/dotnet_build/actions.yaml
を定義して、dotnet build の記述を移します。
外から実行に値を受けるなら、inputs
で指定するのは workflow_dispatch
などと同じで一貫性が取れています。
name: .NET Build description: | .NET Build inputs: build-config: description: "dotnet build config. Debug|Release" default: "Debug" required: false runs: using: "composite" steps: - run: dotnet restore shell: bash - run: dotnet build -c ${{ inputs.build-config }} shell: bash - run: dotnet publish -c ${{ inputs.build-config }} shell: bash
あとは、元の workflow で呼び出すだけです。簡単ですね。
jobs: jobA: runs-on: ubuntu-latest steps: - name: .NET Build uses: ./.github/actions/dotnet_build - run: nanika yaru jobB: runs-on: ubuntu-latest steps: - name: .NET Build uses: ./.github/actions/dotnet_build - run: betsu no nanika yaru jobC: runs-on: ubuntu-latest steps: - name: .NET Build uses: ./.github/actions/dotnet_build with: build-config: Release - run: tondemo naikoto yaru
Composite Action 利用時の注意
一見すると簡単で便利、最高って感じですが、Composite Actions は微妙に歯がゆいことがいくつかあります。 ということで、使うときはこれだけ気を付けておくといいです。(順次改善されて行ってほしい)
1. 使えるのは run:
のみ (制約->改善済み)
感想: uses: 使えるようになってほしいけど無理そう2021/8/26 に uses が使えるようになりました。 GitHub Actions: Reduce duplication with action composition | GitHub Changelog
Composite Action で使えるのは、 run:
のみで uses:
は使えません。
そのため、外部 Actions の呼び出しや別の composite action の呼び出しができません。
これが地味につらいところです。 たいがいは uses をいくつか使っているので、結果そのジョブを丸っと Composite Action に移して実行するというのはたいがいできません。
ほぼ毎回、runs:
部分をより分けてどれを composite action にするか検討することになるでしょう。
ただ分離したいだけなのに、というわけにはいかないのです。
2. run.if
は使えない (制約)
感想: これはできるようになっていいのでは
run step は、実行するかどうかを決定する if
コンディションがありますが、Composite Action の run で if: <expression>
は使えません。
このため、元の run が if を使っていた場合、run:
処理の中で bash if を使って分岐することになったりします。なるほどねー。
# これはだめ - if: ${{ env.HOGE == 'hoge' }} run: do something shell: bash # こうなる - run: | if [[ "${{ env.HOGE }}" == "hoge" ]]; then do something fi shell: bash
if 分岐を多用していると地味にめんどくさいので、ちまちま bash if にするか shell script に処理を書いてまとめたりします。
3. container で実行すると github.action_path
パスが狂う (歯がゆい)
感想: 地味に罠なのでなおして~
Composite Actions の今のパスは github.action_path
でとれます。このため、Composite Action で使うスクリプトは同じパスに置いておく、とかできます。
${{ github.action_path }}/prepare_env.sh
しかしコンテナで実行するときは狂うので、仕方ないので ${{ job.container.id }}
でコンテナ環境か判定して、${{ github.workspace }}
と ${{ github.action_path }}
で修正してあげましょう。
これやらずパス参照で書けばいいやと思うと、actions のフォルダ名を変えるたびに毎回YAMLを修正しないと行けなくてつらいので。
やっておくのオススメです。
4. 1ステップで実行されるのでログの区切りがつかない (歯がゆい)
感想: すべての Composite Cction でやらないとつらいので大変めんどくさい
Composite Actions は、端的に言うと 呼び出し側の1 step で 呼び出した run がすべて実行されます。 つまり、1 step ログに、呼び出したすべての処理の標準出力がでるので、どの処理がどの出力か区別がつきません。
このため、Grouping log lines を使って処理ごとにログ出力をグループ化しましょう。絶対やりましょう。
::group::{title} ::endgroup::
先ほどのサンプルはやってませんね、ダメな奴です。 アレに適用して次のようにすると、dotnet restore / dotnet build / dotnet publish がそれぞれグループ化されます。(こうなると、name もつけたくなるのでつけてます)
name: .NET Build description: | .NET Build inputs: build-config: description: "dotnet build config. Debug|Release" default: "Debug" required: false runs: using: "composite" steps: - name: restore packages run: | ::group::Restore packages dotnet restore ::endgroup:: shell: bash - name: build run: | ::group::Build dotnet build -c ${{ inputs.build-config }} ::endgroup:: shell: bash - name: publish binaries run: | ::group::Publish binaries dotnet publish -c ${{ inputs.build-config }} ::endgroup:: shell: bash
個人的には、name で自動的にグループ化してほしい気もありますが、ユーザーの好きなようにコントロールさせるために何もしていない気もします。
まとめ
Composite Actions は素朴でいいのですが実際使うときはアレってなるので、これらだけ注意すると便利です。
GitHub Actions に本当に欲しいのは、Template 機能な気もするけど ローカルアクションは便利なのでいいものです。 公開されている GitHub Actions を GHE で使うときに GitHub と Connect せずにローカルに展開することもできますし。
だいたいのことは GitHub Actions でできるようになりましたが、パイプライン的な観点がないので、今後はそっちがどうなるのか気になりますね。