ちょっと話題の記事

Github Actionsの個人的ユースケース備忘録

Github Actionsを使っていて、「これどうやるんだったっけ」と過去に見た資料を探すのが面倒になり、とりあえずテンプレ的にまとめてブログ化しておけば後々楽になりそうだなと思って書いてみました。
2020.05.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Github Actionsで色々なフローの自動化に取り組んで一番大変だと感じているのは、目的とするデータをコンテキストのどこから取れるのか見つけるところです。

公式ドキュメントも正直目的の情報に辿り着きやすいとも言えず、StackOverFlow等のFAQフォーラムも類似した質問が多くありすぎて逆に絞るのが大変となる繰り返しでした。

私自身で頻繁に用いそうなものを中心に、調査の手間を省くために備忘録として書き出してみました。2020年5月26時点のデータとなります。

各コンテキストについて

利用可能なコンテキストは以下の8つです。公式ドキュメントが個人的にやや見難いため、必要なところだけを抜粋しました。

github
workflowの情報にアクセスする用途
env
workflow、job、stepの環境変数にアクセスする用途
job
jobの情報にアクセスする用途
steps
jobで設定しているstepの情報にアクセスする用途
runner
jobを実行しているrunner(セルフホスト含)にアクセスする用途
secrets
リポジトリのsecretsにアクセスする用途
strategy
strategyパラメータにアクセスする用途(fail-fast, job-index, job-total, max-parallel)
matrix
実行中のjobに対して決定したmatrixパラメータにアクセスする用途
needs
実行中のjobが依存しているjobによる出力へアクセスする用途

githubやenvについては利用頻度も高いため覚えやすいかと思われます。

用途別のコンテキスト参照

各コンテキストには沢山の要素がありますが、目的を達成するのに使えるのか、及び使っても問題ないのかは正直分かりにくいところです。

頻度が高かったケースを順に書き出しました。

pull-requestのベースブランチ及び比較ブランチを取得する

ベースブランチ、比較ブランチと書くと分かり難いですが、github上で以下の状態になる組み合わせです。

ベースブランチ(base)
github.base_ref
比較ブランチ(compare)
github.head_ref

ポイントは、workflowを実行したイベントがpull_requestの時しか使えません。

on:
  pull_request:
    branches:
      - master
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: |
        echo ${{github.base_ref}}
        echo ${{github.head_ref}}

pull-requestのmergeが完了した時に動作させる

ifへの条件指定を用います。stepsでも指定可能ですが、jobそのものに指定することで動作時間の節約を見込めます。

on:
  pull_request:
    types: [closed]
jobs:
  build:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    steps:
      - name: merged
        run: echo merged

jobの実行に順番を持たせる

依存関係を持たせます。

jobs:
  build:
  test:
    needs:[build]
  release:
    needs:[build, test]

分けるメリットとしては、buildが失敗したらその時点で終わらせる、といった分岐がifにて環境変数等を使う必要なく可能になります。

複数の異なる条件で動くjobを一つのファイルに収める

互いに関連するjobは一つのファイルにしたほうが把握しやすいケースもあります。

気をつけるべき点として、onで指定された条件がjobに紐づくわけではありません。制約がなければ、いずれのjobもonに該当する場合必ず実行されます。よって、if等で正確に指定しなければ事故の原因に繋がります。

on:
  push:
    branches_ignore:
      - master
  pull_request:
    branches:
      - master
jobs:
  pr:
    if: github.event_name == 'pull_request'
  test:
    if: github.event_name == 'push'

pull-requestのmergeでreleaseを作成しつつrelease-messageにpull-requestの文面を流用する

pull-requestでreviewerによる承認を得て、mergeと同時にreleaseを作成するケースです。pull-requestとreleaseでの文面が一致する前提としています。

release作成時のバージョン指定についてはインクリメントの条件もリポジトリによって異なると思われるため、とりあえず被らない想定で日付指定にしています。

on:
  pull_request:
    branches:
      - master
    types: [closed]
jobs:
  merge:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - name: pull-request merged
        run: |
          echo "::set-env name=release_tag::v$(date '+%Y%m%d_%H%M%S')"
      - name: Create Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ env.release_tag }}
          release_name: Release ${{ env.release_tag }}
          body: "${{github.event.pull_request.body}}"
          draft: false
          prerelease: false

必要に応じてdraftprereleaseを有効にするケースもありかと思います。

あとがき

ifにおける条件指定については、主にコンテキストでアクセスできる内容を使うケースもあると思いますが、ドキュメントでは中々中身がわからない場合にはDumpした方が早いと思いました。

- run: ${{ toJson(github) }}

Dumpした結果についてはそれぞれの精査が必要になってきますが、keyの組み合わせ(github.event.pull_request等)で検索することで用例が見つかることも多々あります。

今回の記事がなにかの参考になれば幸いです。

直接的ではないものの参考にした記事