以下の内容はhttps://tech.guitarrapc.com/entry/2021/08/01/194007より取得しました。


GitHub ActionsのローカルComposite Actionで歯がゆいこと

GitHub ActionsのComposite Action (複合ステップアクション) は便利なのですが、制約や歯がゆいことが多く悩ましいです。

では何が難しいと感じているのか、その対処をどうしているのかメモしておきます。

概要

  • Composite Actionはrunのみ使える。usesは使えないからあきらめて
  • Composite Actionはrun.ifが使えないのでbash ifで分岐しよう
  • Composite Actionでスクリプト使うならコンテナ実行時にパス狂うから気を付けて
  • Compoiste Actionの全runステップはGrouping log linesを使おう、絶対だ

Composite Actions とは

GitHub Actionsは、Jobで実際にやる処理1つ1つをstepとして記述できます。 このstepでrun:を使っていろいろな処理を書いたりuses:を使ってアクションを呼び出したりしていることでしょう。

さて、プロジェクトでいろいろなworkflowを用意していくと、似通ったrun stepを記述していてまとめ上げたくならないでしょうか。 TypeScriptやDockerアクションにするというわけではなく、単純にrun stepのYAMLを分離して呼び出すことで共通化したい。

こんな時に便利なのがComposite Actionです。

https://docs.github.com/en/actions/creating-actions/creating-a-composite-run-steps-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::

https://docs.github.com/en/actions/reference/workflow-commands-for-GitHub-actions#grouping-log-lines

先ほどのサンプルはやってませんね、ダメな奴です。 アレに適用して次のようにすると、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でできるようになりましたが、パイプライン的な観点がないので、今後はそっちがどうなるのか気になりますね。




以上の内容はhttps://tech.guitarrapc.com/entry/2021/08/01/194007より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14