Open5

GitHub Actions 小技

ピン留めされたアイテム
mythrnrmythrnr

GitHub Actionsでちょっとしたアレを実現したい的なことを書き連ねる

mythrnrmythrnr

三項演算子もどき

例: name: などの出力を分岐したいとき

jobs:
  job1:
    name: ${{ fromJSON('["FALSE", "TRUE"]')[1 == 1] }}

解説

  • GitHub Actions では配列やオブジェクトがリテラルで使えないので、JSON文字列から生成する
  • 1 == 1 は真偽値だが、 GitHub Actions では 1 になるので、配列にアクセスできる

変数にも使いたい

上の例だと環境変数を分岐したいときには使えないので...

jobs:
  job1:
    name: |
      ${{ env[fromJSON('[
        "VERY_LONG_KEY_CASE_FALSE",
        "VERY_LONG_KEY_CASE_TRUE"
      ]')[1 == 1]] }}

解説

  • オブジェクトのプロパティはインデックス構文( [0], [key] など)と参照外し( . アクセス)が使えるので、上の三項演算子もどきを使ってインデックス構文のキーを切り替える
  • 複数行はダメ元で試したものながら、問題なく動作したので記載
mythrnrmythrnr

ステップを切り出す(Composite run steps actions)

ローカルかつプライベートリポジトリで切り出したいときの方法
(公開リポジトリならアクション用に別リポジトリを作ることが推奨されている)

マニュアル

.github/actions/composite-example/action.yml

公式のものを持ってきただけ

# via https://docs.github.com/en/actions/creating-actions/creating-a-composite-run-steps-action#creating-an-action-metadata-file
name: "Hello World"
description: "Greet someone"
inputs:
  who-to-greet: # id of input
    description: "Who to greet"
    required: true
    default: "World"
outputs:
  random-number:
    description: "Random number"
    value: ${{ steps.random-number-generator.outputs.random-id }}
runs:
  using: "composite"
  steps:
    - run: echo Hello ${{ inputs.who-to-greet }}.
      shell: bash
    - id: random-number-generator
      run: echo "::set-output name=random-id::$(echo $RANDOM)"
      shell: bash
    # - run: ${{ github.action_path }}/goodbye.sh
    #   shell: bash
.github/workflows/composite-run.yml
name: "composite-example"

on:
  workflow_dispatch:

jobs:
  debug:
    runs-on: "ubuntu-latest"
    steps:
      - uses: "actions/checkout@v2"

      - name: "動く"
        uses: "./.github/actions/composite-example"

      - name: "動かない(Unable to resolve action `.github/actions@master`, repository not found)"
        uses: ".github/actions/composite-example"

解説

  • uses: はチェックアウトをした上で、 ./ をつけてパスを指定する。 ./ で始まっていないと、リポジトリを探しにいってしまい、 Not Found になる
mythrnrmythrnr

matrix で実行する組み合わせを制御(妥協あり)

matrix を否定してそう...)

includeexclude だと人間の頭ではしんどい

.github/workflows/example.yml
name: "matrix_array"

on:
  workflow_dispatch:
    inputs:
      target:
        description: "sample input (1 | 2 | 3)"
        required: true
        default: ""

jobs:
  debug_array:
    runs-on: "ubuntu-latest"
    strategy:
      fail-fast: false
      matrix:
        entries:
          - { key: 1, value: "value1" }
          - { key: 2, value: "value2" }
          - { key: 2, value: "value3" }
          - { key: 3, value: "value4" }
          - { key: 3, value: "value5" }
          - { key: 3, value: "value6" }

    name: ${{ fromJSON('["[Skipped] ", ""]')[matrix.entries.key == github.event.inputs.target] }}${{ matrix.entries.key }} => ${{ matrix.entries.value }}

    env:
      RUN: ${{ fromJSON('["", "1"]')[matrix.entries.key == github.event.inputs.target] }}

    steps:
      # env.RUN はこっちでも同じ. わかりやすい方で
      # - if: "matrix.entries.key == github.event.inputs.target"
      #   name: "Set job as run"
      #   run: echo "RUN=1" >> $GITHUB_ENV

      - if: "env.RUN"
        run: "echo ${{ matrix.entries.value }}"

解説

  • matrix.entries の配列を定義して、実行対象かどうかを matrix.entries.key == github.event.inputs.target で判定し、 env.RUN にセット
  • env.RUN の偽の値は空文字か未定義にする。 false0 では jobs.<job_id>.steps[*].if で何故か true になる。文字列で解釈されている?
  • jobs.<job_id>.namematrix が使えるので割と自由が効く

妥協点

steps[*].if で毎回 if: "env.RUN" (もしくは if: "env.RUN && ..." )と記述する必要がある。
ワークフローの解釈の順番の都合だと思っているが、 jobs.<job_id>.if では matrix が使えないので、ここで使えるとベスト

mythrnrmythrnr

プライベートリポジトリのアクションを実行する

actions/checkout を使ってプライベートリポジトリを取得してローカル実行する

name: "composite-private"

on:
  workflow_dispatch:

jobs:
  debug:
    runs-on: "ubuntu-latest"
    steps:
      - uses: "actions/checkout@v2"

      - uses: "actions/checkout@v2"
        with:
          repository: "mythrnr/actions-private-example"
          token: "${{ secrets.SECRET_TOKEN }}"
          path: "./.github/actions/private-example"

      - uses: "./.github/actions/private-example"

解説

  • secrets.GITHUB_TOKEN は別のリポジトリを取得できないので secrets.SECRET_TOKEN を別途作成して使う