GitHub Actions: tagでfilter


概要

先日GitHub Actionsの設定ファイルの書き方が変わりました。

これまでは独自のフォーマットだったものが、YAMLで設定するようになりました。

tagでfilterする方法を調べたのでメモします。

やりたいこと

  1. リポジトリにpushされたら
  2. ユニットテストの実行
  3. ユニットテストをパスした かつ vで始まるtagがpushされた場合、リリースする

をやりたい。

普段はpushしたらその都度ユニットテストが実行され、tagもpushした場合のみリリースするということをやりたい

旧GitHub Actions設定ファイル

Node.jsのプロジェクトでnpm publishまでやる例

workflow "Build, Test, and Publish" {
  on = "push"
  resolves = ["Publish"]
}

action "Build" {
  uses = "actions/npm@master"
  args = "install"
}

action "Test" {
  needs = "Build"
  uses = "actions/npm@master"
  args = "test"
}

# Filter for release tag
action "RelaseTag" {
  needs = "Test"
  uses = "actions/bin/filter@master"
  args = "tag v*"
}

action "Publish" {
  needs = "RelaseTag"
  uses = "actions/npm@master"
  args = "publish --access public"
  secrets = ["NPM_AUTH_TOKEN"]
}

もうこの設定は使えなくなるので詳細は割愛しますが、

  1. リポジトリにpushされたら
  2. ユニットテストの実行
  3. ユニットテストをパスした かつ vで始まるtagがpushされた場合、リリースする

という動きをします。

リポジトリにpushされてもtagがなかったらユニットテストの実行だけ動きます。

新GitHub Actions設定ファイル

name: Node CI

on: [push]

jobs:
  test:
    name: Test on node ${{ matrix.node-version }} and ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        node-version: [8.x, 10.x, 12.x]
        os: [ubuntu-latest, windows-latest, macos-latest]
    steps:
    - uses: actions/checkout@v1
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node-version }}
    - name: npm install, build, and test
      run: |
        npm install
        npm run build --if-present
        npm test
      env:
        CI: true

  publish:
    name: npm publish
    runs-on: ubuntu-latest
    needs: [test]
    steps:
    - uses: actions/checkout@master
    - uses: actions/setup-node@v1
      with:
        node-version: '12.x'
        registry-url: 'https://registry.npmjs.org'
    - run: npm install
      if: contains(github.ref, 'tags/v')
    - run: npm publish
      env:
        NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
      if: contains(github.ref, 'tags/v')

旧と比べて 複数のNode.jsのバージョン、複数のOS でのテストが追加されていますがそれ以外はやっていることは同じです。

新GitHub Actions設定ファイルのポイント

on での制御

特定の条件、例えば「特定のtagがpushされた時」をやりたい場合、上部の

on: [push]

でも制御できます。

参考: https://help.github.com/ja/articles/workflow-syntax-for-github-actions#on

ただしその条件が全体に適用されるので、旧での

リポジトリにpushされてもtagがなかったらユニットテストの実行だけ動きます。

この動きが実現できません。

ifneeds

ということで、on では実現できないので別jobを作って対応します。
jobは基本的に並列で動くので、

  1. ユニットテストをパスした かつ vで始まるtagがpushされた場合、リリースする

の「テストをパスした後に」を実現するために needs を使います。

    needs: [test]

この設定を入れるとjobの「publish」は「test」が終わった後に実行されます。

「vで始まるtagがpushされた場合」を実現するためには if を使います

      - run: npm publish
        if: contains(github.ref, 'tags/v')

これでvで始まるtagがpushされたときのみ npm publish が動きます。

参考: https://help.github.com/ja/articles/contexts-and-expression-syntax-for-github-actions

まとめ

「普段はpushしたらその都度ユニットテストが実行され、tagもpushした場合のみリリースする」が実現できました。

on がjobごとに設定できたらもう少しわかりやすくなるかも?

もう少しいい設定方法があったら知りたい

(全体的にまとまりのない文章ですみません)