関連

ワークフローの.ymlには都度パターンが出現します。使える特殊な文字は以下のようなものがあります。

  • *

  • **

  • !

  • []

  • ?

  • +

*

*はすべてにマッチしますが/は跨ぎません。これは*と設定した時foobarはマッチしますが、foo/foobar/barにはマッチしません。

またあるワードの前後に適当な文字を付けると、適当な文字は必ず含まれるランダムな文字にマッチします。*-barfoo-*foo-barにマッチします。

**

**はすべてにマッチします。こちらは/にもマッチするのでfoo/foobar/barにもマッチします。

こちらもランダムな文字と組み合わせて使えます。**/src/main.jspath/to/src/main.jsa/b/c/src/main.jsなどにマッチします。

また特によく使う表現は拡張子と使う方法です。**.js**.mdなどはpath/to/foo.jspath/to/readme.mdなど、適当なディレクトリにある.js.mdファイルにマッチします。

!

!は直前でマッチしたものの中から一部除外する為のパターンです。例えば**.jsはすべての.jsがマッチしますが。この後に続けて!foo/src/main.jsを置けば、foo/src/main.js以外のすべてのリポジトリに含まれる.jsファイルがマッチします。

[]

[]で囲んだ部分はある1文字についてのパターンを表現します。*ではすべての文字がマッチしていましたが、この表現にするとその文字をある程度絞り込めます。例えば[abc]であればabcのどれかになります。もうひとつ挙げると[0123456789]は数値だけにマッチすることになります。

上記のような0123...というのは続いた値です。このような場合は[0-9]-を挟んで省略できます。これはアルファベットも同じで[a-z][A-Z]と書けます。
また、これらを[0-9a-zA-Z]のように組み合わせることもできます。

?

直前の文字が「あっても無くても良い」ことを表現できます。*.jsx?というパターンの場合*.js*.jsxどちらにもマッチします。

+

直前の文字は必ず1つ以上続くことを表現できます。(無理やり感がありますが)[ademe]+.mdとすればreadme.mdadem.mdなどにマッチします。

他にこれをよく使うのはセマンティック・バージョニングのパターンなどです。このパターンはv[0-9].[0-9]+.[0-9]+のように書きます。こえでv1.23.681のような文字にマッチします。

on.pushイベントを使うと、そのリポジトリにpushした時にそのワークフローを走らせられます。on.pull_requestの場合はプルリクを上げた時だけです。

使うには以下のような一行をワークフローファイルへ置くだけです。どちらかだけを使いたいなら配列じゃなくでも良いです。

on: [push, pull_request]
# 1つだけなら `on: push` 

またこのon.pushon.pull_requestは下に詳細設定を置くことがでます。

ブランチ・タグフィルター

「このブランチ、このタグがpushされた時だけ」のような設定ができます。これはブランチはbranchesまたはbranches-ignore、タグはtagsまたはtags-ignoreで設定できます。-ignoreが付いているものとそうでないものは同時に設定できません。

また一度含ませてから、一部だけ含ませないような書き方も!を頭に付けることで可能です。これは.gitignoreのような設定ファイルと同じです。

ブランチ

ブランチはデフォルトですべてのブランチが実行対象に含まれてます。例えば以下のような設定があるとします。

on:
  push:
    branches-ignore:
      - 'features/**'
      - '!features/*-master'

上記の設定上ではfeatures/**にマッチしない時とfeat/*-masterにマッチする(feat/foo-masterなど)時のみワークフローが走ります。つまりこれはmasterブランチなども対象になります。

!はもちろんbranchesでも効きます。

on:
  push:
    branches:
      - "features/**"
      - "!features/*-test"

タグ

タグの場合はデフォルトではすべて対象外になってます。もしすべてのタグを含めないのであれば、

on:
  push:
    tags:
      - "*"

とすることでできます。セマンティック・バージョニングな形のタグだけにしたいなら、

on:
  push:
    tags:
      - "v[0-9].[0-9]+.[0-9]+"

この場合ではfeatures/*-testにはマッチしないfeatures/**にマッチするブランチでワークフローが走ります。

ファイルフィルター

on.pushの下にはpathspaths-ignoreというキーも置けます。これは「コミットにパターンにマッチするファイルがあった時だけ処理するよ」またはその逆のように設定できます。

on:
  push:
    paths-ignore:
      - '**.md'

上記のような設定にしておけばすべての**.md(リポジトリ上のすべての*.mdファイル)だけのコミットの時にはワークフローを走らせずに済みます。

on:
  push:
    paths:
      - '**.js'

このようにすれば**.jsの時だけ対象にできます。

on.scheduleはある決められた時刻にワークフローを実行する為の仕組みです。このイベントによってワークフローが走る時対象になるブランチは、そのリポジトリのデフォルトブランチになります。またその内容は最新のコミット時のものになります。

設定するにはon.scheduleの下にcronキーを持つ連想配列を配列で置きます。cronPOSIX クーロン構文で書く必要があります。配列ということは同時に複数の値を置くことができます。

on:
  schedule:
    - cron: "*/5 * * * *"
    - cron: "*/7 * * * *"  

このような設定ではワークフローは5分毎と7分毎に実行されます。注意点として2020年1月時点ではcronの最小インターバルは5分と設定されてます。

ジョブ毎にどの環境で処理を実行するか指定するものとしてruns-onがあります。これが取りうる値は2020年1月現在以下の通りです。

  • ubuntu-latest,ubuntu-18.04 Ubuntu 18.04

  • ubuntu-16.04 Ubuntu 16.04

  • windows-latest Windows Server 2019

  • macos-latest macOS Catalina 10.15

例えば以下のようになります。

jobs:
  build:
    runs-on: ubuntu-latest

複数の環境で実行

ビルドマトリクスjobs.<job>.strategy.matrixを用いることで、1つのジョブステップを別々の環境で複数回実行できます。例えば、

jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, ubuntu-16.04]  

のようにするとubuntu-18.04ubuntu-16.04の2環境で検証できます。

runs-onself-hostedを選択すると、自身のマシンでワークフローを実行させれます。その為には先に自身のマシンで環境構築が必要です。
マシンには自分のマシン環境そのままを使っても良いですが、ここではあまり環境を汚したくない為 Docker 上で起動した Ubuntu の上で環境構築します。

とりあえず構築前に以下で Ubuntu を建てます。

docker run -itd \
  --name self-hosted-runner \
  --env WORKING_DIR=/home/runner/actions-runner
  ubuntu

次で ID が表示されれば建てれてます。

docker ps --filter name=self-hosted-runner --quiet

依存インストール

Docker の Ubuntu は最小環境らしくcurlが無いのでインストールします。

docker exec -it self-hosted-runner \
  sh -c 'apt update && apt install curl --yes'

ユーザーを作る

マシンはいつワークフローが実行されても大丈夫なように、(GitHub 提供の).shを実行して待機状態にしておく必要がありますが、この.shはスーパーユーザー権限だと実行できません。

そのため以下でユーザーrunnerを作り、このユーザーでその.shを実行させます。

docker exec -it self-hosted-runner \
  sh -c 'useradd runner -m && echo runner:secret | chpasswd'

最初のユーザーなので恐らく UID が1000runnerが作られたはずです。その為今後は、

docker exec --user 1000 -it self-hosted-runner sh -c '...'

として実行した...runnerユーザーによって行われるコマンドになります。

依存ファイルのインストール

これは適当なリポジトリの

で開くSettingsActionsAdd runnerダイアログに載っているコマンドを叩くだけです。

docker exec --user 1000 -it self-hosted-runner sh -c '
  mkdir -p $WORKING_DIR && \
  cd $WORKING_DIR && \
  curl -O -L https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz && \
  tar xzf ./actions-runner-linux-x64-2.164.0.tar.gz
'

そしてダイアログに示されてる次の./config.shから始まるコマンドを実行すると以下のメッセージが出ます。

Libicu's dependencies is missing for Dotnet Core 3.0
Execute ./bin/installdependencies.sh to install any missing Dotnet Core 3.0 dependencies.

また「欲しいツールが入ってないので、./bin/installdependencies.shを実行してインストールしてね」という感じのことを言ってるので./bin/installdependencies.shスーパーユーザーで叩きます。

docker exec -it self-hosted-runner \
  sh -c 'cd $WORKING_DIR && ./bin/installdependencies.sh'

再度runnerユーザーで./config.sh ...を叩きます。これもコピペで良いです。注意点として、一定時間が経過するとトークンが期限切れになるようなので、その場合は再度ダイアログを開き直して再生成してください。

docker exec --user runner -it self-hosted-runner \
  sh -c 'cd $WORKING_DIR && ./config.sh --url https://github.com// --token '

いくつか聞かれるものがありますが、それもデフォルト値が入ってるので変えたいものがあれば変えます。試しに以下のようにしてみました。

# self-hosted runner の名前
Enter the name of runner: [press Enter for 06332a3d575d] my-runner
# ...
#
# ワークフローを実行する際にいるフォルダ名
Enter name of work folder: [press Enter for _work]
# ...

うまくいくと Settings → Actions の Self-hosted runners へSettingsActionsAdd runnerダイアログこのように runner が追加されます。

待ち受け状態にする

最後のコマンドを実行します。このコマンドで GitHub runner として接続され、ctrl-cを叩くまで実行されます。また、マシンの通信などが切れた場合は終了せず、自動で再接続を試みてくれるようです。

docker exec --user runner -it self-hosted-runner \
  sh -c 'cd $WORKING_DIR && ./run.sh'

すると先程はオフラインだった runner がSettingsActionsAdd runnerダイアログこのようにアイドル状態になります。

最後にこの状態でgit pushなどを行った時に実行状態のターミナル画面にワークフローのログが現れればうまくいってます。こま### Docker 化

以下のようなDockerfileで Image 化すればdocker run -it --rm <image-name>だけで簡単にアイドル状態にできます。

FROM ubuntu:bionic

RUN apt update && apt install language-pack-ja sudo curl --yes

ENV TZ Asia/Tokyo
ENV LANG ja_JP.UTF-8

RUN useradd runner -m && \
  echo runner:secret | chpasswd && \
  usermod -aG sudo runner

USER runner
WORKDIR /home/runner

RUN mkdir actions-runner && cd actions-runner
RUN curl -O -L https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz
RUN tar xzf ./actions-runner-linux-x64-2.164.0.tar.gz

USER root

RUN ./bin/installdependencies.sh

USER runner

RUN ./config.sh --url https://github.com/nju33/github-actions-self-hosted-runner --token ADZKC3TA36BF3Y6VR7K7CPC6FZGMY

CMD [ "./run.sh" ]

トリガーイベントにrepository_dispatchを指定すると、GitHub API からイベントのディスパッチをした時に実行されるワークフローを作れます。

このトリガーを有効にするにはワークフロー.ymlファイルに次を追加するだけです。

on: repository_dispatch

イベント送信

イベントの送信は例えばcurlで以下のように行なえます。

curl -X POST \
--header 'accept: application/vnd.github.v3+json' \
--data '{"event_type": "foo"}' \
https://api.github.com/repos///dispatches

<owner>/<repo>はワークフローを置いている GitHub リポジトリによって変わります。
もしリポジトリがプライベートであれば、repo権限を持ったアクセストークンも送る必要があります。

--header 'authorization: bearer '

値も送ってワークフローで使う

データのclient_payloadに好きなオブジェクトデータを入れて送るとワークフローでその値が使えます。例えば以下で送ると、

curl -X POST \
--header 'accept: application/vnd.github.v3+json' \
--data '{"event_type": "foo", "client_data": {"value": "value"}}' \
https://api.github.com/repos///dispatches

ワークフローではgithubコンテキストを通してそれを取得できます。

jobs:
  build:
    steps:
      - name: client_payload
        run: echo ${{ github.event.client_payload.value }}
        # value 

ちなみにevent_typegithub.event.actionで取得できます。

CI ではプロジェクトの依存パッケージのインストールを走らせることが多いです。 1 からインストールを行うと、規模によっては数分掛かりることもあります。 数回であれば気になりませんが、pushの度複数ワークフローを実行とかになると、準備ステップに時間をあまり掛けたくありません。 (プライベートリポジトリの場合、 ワークフローの合計実行時間の制限もあるので余計に)

cache アクション

キャッシュには公式が管理している actions/cache という action を使います。これを使うと、以前と同じ状態、または近いものがあれば今に復元できます。
実行時間の掛かる処理によって作られたものを復元することで、 1 から再度作るよりも時間を短縮できるはずです。

2020-05-07 だとバージョン 1 が主流です。使うには以下のように記述します。

steps:
  - # ...略
  - uses: actions/cache@v1
    id: something-cache
    with:
      path: ...
      key: ...
      restore-keys: ...

入力値

3 つの値を渡して実行できます。pathkeyそしてrestore-keysです。この中でpathkeyの 2 つの値は必須です。

まず最初にpathにはキャッシュしたいディレクトリやファイルへの絶対パスを渡します。次にkeyにはキャッシュする際のキー( 512 文字以下)を指定します。 ワークフローの最後にそのキーによるキャッシュが行われ、次に実行した際にそのキーによるキャッシュが存在すればそれが復元されます。

restore-keysは指定したキーによるキャッシュが存在しなかった場合に使われるフォールバック的なキーです。キーによって前方一致した最新のキャッシュを復元できるようになります。不完全でも前の結果がある方が処理を早く終えられるようなものに設定するのが良いです。

出力値

cache-hitという値があります。これはキャッシュを復元できた場合のみ、steps.something-cache.outputs.cache-hittrueという文字列が格納されます。

ジョブの例

このジョブはパッケージマネージャ Yarn を用いた JavaScript プロジェクトを想定してます。プロジェクトルートにはpackage.jsonがあり、適用にパッケージが追加されてます。また依存はローカルでインストール済でその解決方法が記載されたyarn.lockもプロジェクトルートに置かれれる状態です。

キャッシュに関するステップは- id: yarn-cache-dirの次の次の行にあります。

jobs:
  build:
    # ubuntu 18.04 環境上で実行
    runs-on: ubuntu-18.04

    # それぞれ以下のNode.jsが使える環境にして実行
    # - node@^10
    # - node@^12
    # - node@^13
    strategy:
      matrix:
        node-version: [10.x, 12.x, 13.x]

    # 実行処理
    steps:
      # Node.js の指定バージョンで使えるようにする
      - uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}

      # コードを持ってくる
      - uses: actions/checkout@v2

      # キャッシュがあれば復元
      - id: yarn-cache-dir
        run: echo "::set-output name=value::$(yarn cache dir)"
      - id: restore-keys
        run: echo "::set-output name=value::node-dependencies-${{ matrix.node-version }}-v1"
      - uses: actions/cache@v1
        id: node-dependencies
        with:
          path: ${{ steps.yarn-cache-dir.outputs.value }}
          key: ${{ steps.restore-keys.outputs.value }}-${{ hashFiles('**/yarn.lock') }}
          restore-keys: ${{ steps.restore-keys.outputs.value }}

      # インストール
      - run: yarn

キャッシュアクション部分を見ていきます。まずpathですが${{ steps.yarn-cache-dir.outputs.value }}を指定してます、この中身は id がyarn-cache-dirのステップのアウトプット値value、つまりはyarn cache dirコマンドを実行時の結果となっています。
yarnはインストール時に各パッケージ結果をキャッシュディレクトリに保存していて、次回のインストール時には可能ならそこからパッケージを解決してくれる為高速化が望めます。

echo "::set-output name=NAME::VALUE"部分ですが、ここは特殊なechoコマンドで、 GitHub Actions ではこのように書くと、steps.step-id.outputs.NAMEVALUEを格納して、後のステップで変数のように使えるようになります。

次にkeyです。これには${{ steps.restore-keys.outputs.value }}-${{ hashFiles('**/yarn.lock') }}-を指定してます。steps.restore-keys.outputs.valueidrestore-keysなステップのアウトプット値valuehashFilesによるハッシュ値、それぞれの文字列を組み合わせたものを指定してます。

hashFilesは GitHub Actions で使える関数の 1 つで指定したファイルパターンからハッシュを計算します。例のように**/yarn.lockのように書くとプロジェクトに含まれるすべてのyarn.lockファイルを対象に計算してくれます。

最後のrestore-keysでは、keyからhashFiles部分を除いたものを指定してます。こうしておくことで、依存に変更が入りyarn.lockの中身が更新され、完全マッチなキャッシュが無くなったとしても、この最新のキャッシュを復元できるようになります。これは上記でも述べた前方一致すればいいキーだからです。

実行してみる

yarn.lockが同じ状態で 2 回ワークフローを走らせてみます。キャッシュがうまく効いていれば 2 回目はより早く完了するはずです。

1 回目

actions/cache1 回目を実行しました。これにより 2 つの事が分かりました。

  1. yarnの実行時間は 17 秒

  2. 終わる前に実行されるキャッシュアクションによるステップのログCache saved successfullyからキャッシュがnode-dependencies-12.x-v1-a135e48fc66de6bb6175d4726583f1b07aefbaac334cc580f8f80e8b70532c3aキーで作られた

2 回目

上記が完了した後 actions/cache1 回目2 回目を実行しました。

  1. yarnの実行時間は 5 秒

  2. Cache hit occurred on the primary key... not saving cache(プリマリキーでキャッシュヒットが発生しました、キャッシュを保存してません)から今度はキャッシュが作られなかった

上記の結果からキャッシュが正常に作られたことで、ワークフローに掛かる時間が短縮できることが分かりました。

actions/cache1 回目2 回目1 回目actions/cache1 回目2 回目1 回目2 回目はリンク先から結果を見ることができます。

キャッシュを復元できた時、作成コマンドの実行がいらない場合

トップの例でキャッシュが復元できた時steps.something-cache.outputs.cache-hittrueになると書きました。もし作成コマンドの実行がいらないなら、この値を使ってステップをスキップできます。
それにはstep.ifを設定します。以下ではsteps.something-cache.outputs.cache-hit != 'true'の時のみrun: ...部分のコマンドが実行されます。

steps:
  - # ...略
  - uses: actions/cache@v1
    id: something-cache
    with:
      path: ...
      key: ...
      restore-keys: ...
	- if: steps.something-cache.outputs.cache-hit != 'true'
	  run: ...

GitHub Actions には少し特殊なワークフローコマンドというものがあります。実態はechoコマンドですが、出力の形式が決められてます。

ワークフローコマンドには以下のような種類があります。

  • set-env

  • set-output

  • add-path

  • error

  • warning

  • debug

  • add-mask

set-env

jobs:
  build:
    steps:
      # `echo nju33`は動的な情報入力のつもり
      - run: echo nju33 | xargs -I{} echo "::set-env name=NAME::{}"
      - run: echo "$NAME"

<job-id>.env<job-id>.steps.<step-id>.env

jobs:
  build:
    env:
      FOO: foo
    steps:
      - run: ...
        env:
          BAR: bar

steps.<step-id>.outputs.<value-name><step-id>id

jobs:
  build:
    steps:
      - id: org
        run: echo "::set-output name=name::nju33-com"
      - run: echo "${{ steps.org.outputs.name }}"

run: echo "${{ steps.org.outputs.name }}"nju33-com

set-output

repository

jobs:
  build:
    steps:
      - id: org
        run: echo "::set-output name=name::nju33-com"
      - run: echo "${{ steps.org.outputs.name }}"
      - id: repo
        run: echo "::set-output name=name::${{ steps.org.outputs.name }}/sandbox-github-actions-workflow-command"
      - uses: actions/checkout@v2
        with:
          repository: ${{ steps.repo.outputs.name }}

repositorynju33-com/sandbox-github-actions-workflow-commandactions/checkout

PATHcowsay

npmyarnnode_modules/.bincowsay
node_modules/.bin/cowsaycowsay

PATH

jobs:
  build:
    steps:
      - run: |
          yarn init -y
          yarn add -D cowsay
      - run: |
          if command -v cowsay > /dev/null; then
            cowsay Moo
          else
            echo cowsay: command not found
          fi
      - run: echo "::add-path::$(pwd)/node_modules/.bin"
      - run: |
          if command -v cowsay > /dev/null; then
            cowsay Moo
          else
            echo cowsay: command not found
          fi

cowsaycowsayPATHMoocowsay: command not found
add-path$(pwd)/node_modules/.bincowsay
Moo

 _____
< Moo >
 -----
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

jobs:
  build:
    steps:
      - run: echo ::error::error-message

filelinecol,

jobs:
  build:
    steps:
      - run: |
          echo ::error file=index.js,line=1,col=1::The first line is bad
          exit 1
      - run: echo foo

error

jobs:
  build:
    runs-on: ubuntu-16.04
    steps:
      - run: echo ::warning::warning-message
      - run: echo ::warning file=.github/workflows/warning.yml,line=10,col=14::ubuntu-18.04 使ってね

ACTIONS_STEP_DEBUGtrueecho 123

##[debug]Evaluating condition for step: 'Run echo 123'
##[debug]Evaluating: success()
##[debug]Evaluating success:
##[debug]=> true
##[debug]Result: true
##[debug]Starting: Run echo 123
##[debug]Loading inputs
##[debug]Loading env
Run echo 123
##[debug]/bin/bash -e /home/runner/work/_temp/b85ddda2-9c07-4f5d-80c2-f0b6b46fc9a8.sh
123
##[debug]Finishing: Run echo 123

debugdebug-message

jobs:
  build:
    steps:
      - run: echo ::debug::debug-message

debugACTIONS_STEP_DEBUGtrue

***

jobs:
  build:
    steps:
      - run: echo ::add-mask::foo

::add-mask::***

終了ステータスが0以外で終了した時デフォルトでは、そのステップのジョブはそこで終了されます。単にジョブの全ステップが正常に実行されて終了することを期待するのであれば、この仕様は直感的で扱いやすいものです。ですが時々、予期していない出来事で最後まで完了しなかった場合だけ後処理を実行したい場合があったりします。

GitHub Actions ではそのような「エラーの時の後処理」を記述できる仕組みが提供されてます。また同じように「正常終了時の後処理」というような仕組みも用意されてます。

関数

GitHub Actions には以下の終了ステータスに関する関数が使えます。

  • success

  • failure

  • cancelled

  • always

これらの関数は真偽値を返すので、主に<step>.ifに渡して使います。

success

successは前回のステップの終了ステータスが0の時trueになる関数です。以下のワークフローではecho successも実行されます。

jobs:
  build:
    - run: echo foo
    - if: success()
      run: echo success

failure

今度は逆に以前のコマンドが0以外の時だけtrueになる関数です。以下のワークフロー以下のワークフローでは最初のステップを終了ステータス1で終わってるので、echo failureのステップも実行されます。

jobs:
  build:
    - run: exit 1
    - if: failure()
      run: echo failure

注意点として、一度終了ステータスが0以外のもので終了したステップがある時点で、そのワークフローは失敗した扱いになるというのがあります。このワークフローはechoで正常に終了してますが、これもワークフローは失敗という扱いになります。
使い所はワークフローが失敗したことに対する後処理を行う為に使うと良いと思います。

ちなみに失敗するかもしれないステップが成功しても失敗してもどちらでも良いような場合continue-on-error: trueを設定することで、終了ステータスが0以外でも成功扱いにすることができます。以下のワークフロー以下のワークフロー以下のワークフローの場合、echo failureは実行されません。

jobs:
  build:
    - continue-on-error: true
      run: exit 1
    - if: failure()
      run: echo failure

cancelled

これはステップの終了ステータスではないですが、ワークフローがキャンセルされた以降にtrueになる関数です。以下のワークフロー以下のワークフロー以下のワークフロー以下のワークフローは、sleep 60で待っている間に以下のワークフロー以下のワークフロー以下のワークフロー以下のワークフローキャンセルボタンをクリックしてキャンセルすることで、echo cancelledのステップを実行できます。

jobs:
  build:
    steps:
      - run: sleep 60
      - if: cancelled()
        run: echo cancelled

always

以前のステップが成功でも失敗でも、ワークフローがキャンセルされていたとしても、とにかく常にtrueになる関数です。以下のワークフロー以下のワークフロー以下のワークフロー以下のワークフローキャンセルボタン以下のワークフローは、最初のステップでわざと失敗させていますが、echo alwaysは実行されます。

jobs:
  build:
    steps:
      - run: exit 1
      - if: always()
        run: echo always
      - if: failure()
        run: echo failure

alwaysfaiureと同じく、一度終了ステータスが0以外で終わったステップがある時点で、そのワークフローは失敗扱いになります。

バージョンタグとはセマンティック バージョニング (SemVer) 2.0.0のような1.2.3というバージョンの頭にvが付いたv1.2.3のようなタグの事をここでは指します。

例えば以下のようなワークフローがあるとします。これがv1.2.3のようなタグをプッシュした時のみ、ジョブが実行されるワークフローです。これはセマンティック バージョニング (SemVer) 2.0.0echo fooと表示されます。

on:
  push:
    branches-ignore:
      - '**'
    tags:
      - 'v*'

jobs:
  build:
    runs-on: ubuntu-18.04
    steps:
      - run: echo foo

これは全体をタグがプッシュした時に実行というフィルターを掛けているような形になっています。ただケースによってはブランチプッシュでもタグプッシュでも共に実行したい共通ステップが出てきてしまったりするかもしれません。
この対応方法にはざっと以下のようなものがあります。

  1. ブランチとタグで別々のワークフローを実行

  2. ブランチとタグで同じワークフローを実行し、バージョンタグのプッシュ時にだけしか実行されないようなステップで対応

どちらが良いのかは規模感によって変わります。個人プロジェクトのような小さなものなら 1 で良いでしょうし、大きいなら 1 だと管理が辛くなるかもしれません。

1. ブランチとタグで別々のワークフローを実行

これは単にワークフローファイルを別々に置くというだけです。

例えば上記のワークフローがバージョンタグのプッシュ時のみというものだったので、ブランチプッシュ時のみ実行されるワークフローは以下のように書き始めます。

on: push

jobs: # 略

pushイベントはbrannchstagsで何もフィルタリングしていない時、すべてのブランチが対象で全てのタグが除外という設定になるので、これだけでブランチ用のワークフローになります。

2. ブランチとタグで同じワークフローを実行し、バージョンタグのプッシュ時にだけしか実行されないようなステップで対応

同じワークフローで実行というのは、<job>.if<step>-ifなどの制御を活用して、バージョンタグをプッシュした時だけに実行される領域をワークフロー内に作ろうという意味です。これにはまず 2 つの要素の理解をします。

1 つ目は参照の名前の存在です。何かしらをプッシュするとgithubコンテキストのref値に参照の名前が入ります。参照の名前というのはセマンティック バージョニング (SemVer) 2.0.0echo fooと表示ブランチのプッシュであればrefs/heads/<branch-name>(refs/heads/masterなど)、セマンティック バージョニング (SemVer) 2.0.0echo fooと表示ブランチのプッシュタグのプッシュであればrefs/tags/<tag-name>(refs/tags/v0.0.2など)のような名前のことです。

2 つ目はワークフロー内ではstartsWithという関数が使えます。これは引数を文字列として 2 つ取り、 1 つ目の文字列が 2 つ目に渡した文字列から始まっていればtrueを返します。

この 2 つの要素を組み合わせます。例えばタグの参照名はrefs/tags/から始まると分かったので、startsWith( github.ref, 'refs/tags/' )のようにすればタグの時だけtrueになりそうです。ただ今回はバージョンダグを扱うので、refs/tags/vとします。

上記から作成したブランチとバージョンタグが共存したワークフローが以下のようなものです。

on:
  push:
    branches:
      - '**'
    tags:
      - 'v*'

jobs:
  build:
    runs-on: ubuntu-18.04
    steps:
      - run: echo ${{ github.ref }}
      - if: startsWith( github.ref, 'refs/tags/v' )
        env:
          REF: ${{ github.ref }}
        run: echo "${REF##*/}"

2 つ目のステップのenv.REFecho "${REF##*/}はちょっとしたテクニックで、シェルが Bash (デフォルト)の時だけですが、このようにするとechov0.0.3のような値が表示(取得)できます。(ちなみにecho "${REF##*/v}とすると0.0.3という値になります)

JavaScript で飯食べたい歴約 9 年、 純( nju33 ) によるノートサイトです。

このサイトではドリンク代や奨学金返済の為、広告などを貼らせて頂いてますがご了承ください。

Change Log