パターン

ワークフローの.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のような文字にマッチします。

Trigger Events ∋ push & pull_request

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の時だけ対象にできます。

Trigger Events ∋ schedule

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

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

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

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

どの環境で実行するか: runs-on

ジョブ毎にどの環境で処理を実行するか指定するものとして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環境で検証できます。

ローカル Docker 環境 で runs-on: self-hosted

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ユーザーによって行われるコマンドになります。

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

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

  1. Settings
  2. Actions
  3. Add 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/<owner>/<repo> --token <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 へこのように runner が追加されます。

待ち受け状態にする

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

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

すると先程はオフラインだった 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" ]

外部のイベントから実行: Trigger Events ∋ repository_dispatch

トリガーイベントに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/<owner>/<repo>/dispatches

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

--header 'authorization: bearer <private-access-token>'

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

データの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/<owner>/<repo>/dispatches

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

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

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