🦾

GitHub Actionsを整理するためのパターンを考える

2024/02/28に公開

GitHub Actionsはひとつのyamlファイルから書き始めることができますが, 複数のWorkflowやJobを書こうとすると途端に重複部分が増えてきます.

例えば

  • マージ時とプッシュ時でビルドとテストをするところはまでは同じだが, マージ時にはリリース処理をしたい
  • Jobを分けたいが毎回同じようなセットアップをする必要がある
  • 並列して確認できる部分(例えばtest実行とlint実行)はなるべく並列にしたい. 直列だと片方が失敗したときにもう片方が実行されず結果が出ない

こうした要求を実現しつつ, コードの重複を減らして整理するための自分なりの書き方パターンがあるので, ここではその方法をまとめておきます.

まとめ

  • 単一Workflowと複合Workflowとの2つに役割を分ける
    • 単一Workflowの役割
      • workflow_callからのみ呼び出される
      • ビルドやテスト, リリース処理などの単位でひとつ作成する
    • 複合Workflowの役割
      • on pushやon scheduleなどの外部からのトリガーによって呼び出される
      • 複数の単一WorkflowをJobとして組み合わせて並べる
        • 例: プッシュ時テストではビルド→テスト
        • 例: マージ時リリースではビルド→テスト→リリース
  • キャッシュを利用して成果物を複数のJob間でやり取りする
  • composite action を活用する

サンプル

https://github.com/ykyki/blog/tree/2ddb39e48de7862c2772c114201edd25cfb22b42

composite action

共通のセットアップを行うアクションの例

name: common-setup
description: |
  action for common setup of node/pnpm/bun

inputs:
  NODE_VERSION:
    description: ''
    required: false
    default: '20'
  PNPM_VERSION:
    description: ''
    required: false
    default: '8'
  BUN_VERSION:
    description: ''
    required: false
    default: '1'
  REQUIRE_BUN:
    description: ''
    required: false
    default: 'true'

runs:
  using: 'composite'
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.NODE_VERSION }}
    - uses: pnpm/action-setup@v3
      with:
        version: ${{ inputs.PNPM_VERSION }}
        run_install: false
    - uses: oven-sh/setup-bun@v1
      if: inputs.REQUIRE_BUN == 'true'
      with:
        bun-version: ${{ inputs.BUN_VERSION }}

単一Workflow

name: _install-pnpm-deps

on:
  workflow_call:
    inputs:
      use-pnpm-store-cache:
        type: boolean
        required: false
        default: true

jobs:
  cache-and-install:
    runs-on: ubuntu-latest
    timeout-minutes: 1
    env:
      PNPM_STORE_PATH: ''
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/common-setup
        with:
          REQUIRE_BUN: 'false'
      - uses: ./.github/actions/restore-pnpm-store-cache
        id: restore-pnpm-store-cache
        if: inputs.use-pnpm-store-cache == true
      - run: pnpm install
        if: steps.restore-pnpm-store-cache.outputs.cache-hit != 'true'
      - name: Get pnpm store directory
        run: echo "PNPM_STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
      - name: Save pnpm-store cache
        uses: actions/cache/save@v4
        if: steps.restore-pnpm-store-cache.outputs.cache-hit != 'true'
        with:
          path: ${{ env.PNPM_STORE_PATH }}
          key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}

複合Workflow

name: Build🔨, Verify🧪 on push

on:
  push:

jobs:
  install-pnpm-deps:
    uses: ./.github/workflows/_install-pnpm-deps.yaml

  build-uniproc:
    needs: install-pnpm-deps
    uses: ./.github/workflows/_build-uniproc.yaml

  build-sveltecf:
    needs:
      - install-pnpm-deps
      - build-uniproc
    uses: ./.github/workflows/_build-sveltecf.yaml

  verify-uniproc:
    needs: build-uniproc
    uses: ./.github/workflows/_verify-uniproc.yaml

  verify-sveltecf:
    needs: build-sveltecf
    uses: ./.github/workflows/_verify-sveltecf.yaml

実行結果

失敗を含む場合
GitHub Actions result 2

全件成功の場合
GitHub Actions result

ディレクトリ構成

GitHub Actions directory structure

補足

上記のまとめとサンプルを読んでいただければ大体の雰囲気は把握できるかと思います.

いくつか補足をしておきます.

  • 単一Workflow→複合Workflowの順番で書く. 書き進めていって共通部分が気になってきたらcomposite actionに切り出す
  • composite actionの構文: Metadata syntax for GitHub Actions - GitHub Docs
  • キャッシュを頻繁に使っているためprivate repository等ではGitHub Storageの使用量に注意
    • 1GB/月までは無料で使えるが, 思いのほかキャッシュの容量が大きくなることがある
  • 単一Workflowのファイル名を_で始めることで, 複合Workflowと区別しやすくしている

References

Discussion