Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

GitHub Actionsのworkflow_runイベントでテストを回すときの要点

Dependabotの作ったPRがSecretsにアクセスできないためにことごとく失敗していたのを修正しました。

github.com

SecretsにアクセスできないのはKeeping your GitHub Actions and workflows secure: Preventing pwn requestsで説明されているようにセキュリティ向上のためです。workflow_run イベントでCheckを回すとワークフロー定義は常にデフォルトブランチのものが使われるため、PRでワークフローファイルが悪意を持って変更されてもマージしなければ悪影響を受けません。ので今後、基本的にはSecretsを必要とするワークフローはworkflow_runイベントで回すことになります。

上記securitylab.github.comの記事で色々説明されていますが、わりと限定的なユースケースについて述べているので、ここでは自分のケースで必要だった知見についてまとめます。

actions/checkout ではrefプロパティを指定する

ソースコードをチェックアウトする場合、どのrefをチェックアウトするのかを明示的に示す必要があります。何も指定しない状態ではデフォルトブランチをチェックアウトしてしまうため、PRで変更する内容をテストできません。

on:
  workflow_run:
    workflows:
      - build
    types:
      - completed
jobs:
  integration-test:
    runs-on: ubuntu-latest
    if: github.event.workflow_run.conclusion == 'success'
    steps:
      - uses: actions/checkout@v2
        with:
          ref: ${{ github.event.workflow_run.head_sha }}

Pull RequestのChecksを登録する際もshaを指定する

この「何もしなければデフォルトブランチの最新refが対象になる」のが曲者です。例えばChecksに結果を反映する際も、明示的にshaを指定する必要があります。

      - uses: LouisBrunner/checks-action@v1.1.2
        if: always()
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          name: integration-test
          sha: ${{ github.event.workflow_run.head_sha }}
          conclusion: ${{ job.status }}
          details_url : https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}

Secretsなしでもテストできるようにする

すべてのテストをworkflow_runの方に寄せても良いですが、Secretsなしでも実行できる単体テストや静的解析はpull_requestで実行してしまったほうが良いかもしれません。 その場合は、./gradlew buildnpm run all がSecretsなしでも正常ステータスコードで完了する必要があります。

JUnitならassumptionsでTokenがあるか確認して無ければテスト実行をスキップすればいいでしょう。Jestではうまい方法が見つからなかったので、Tokenの有無でtesttest.skipを呼び分けることにしました。

const token = process.env.GITHUB_TOKEN || ''
const integrationTest = token ? test : test.skip

integrationTest('fork() does nothing if the forked repo already exists', async () => {
  ...
})