はじめに
Github Actions の設定ファイルを記述した後、正しく動作するかを確認するために毎回リモートリポジトリにプッシュするのが面倒だなと思ったので良い方法が無いかと調べてみました。
すると、ローカルで Github Actions の実行が可能なツールが見つかったので、検証してみました。
検証環境
1
2
3
4
5
| $ uname -moi
arm64 unknown Darwin
$ bash -version | head -n 1
GNU bash, version 5.2.15(1)-release (aarch64-apple-darwin22.1.0)
|
見つけたツール : nektos/act
利用するツールは以下になります。
概要を見る限りは自分の目的にフィットしています。
nektos/act ってどんなツール?
Github Actions をローカルで実行するためのツールとのことです。
このツールを導入するメリットは以下となります。
.github/workflows ディレクトリ配下に設定ファイルを作成、変更した後、 commit/push しなくてもテストできるため開発効率が上がるMakefile でタスクを管理することもできますが、 .github/worflows とタスクが重複し DRY ではなくなります。
ローカルで実行できれば Makefile が不要となります。- (この点については、 Github Actions から
make を呼ぶことで回避できると思いますが公式ページのメリットとして書かれていました )
nektos/act って何をしてくれるに?
act コマンドが提供されます。
act コマンドを実行すると、 .github/workflows/ ディレクトリから設定ファイルを読み込み、実行すべきアクションのセットを決定します。
実行されるアクションを決定するためには「イベント」 ( push、branch作成など ) を指定しなければいけませんが、明示しなければ push イベントが発生したものとして解釈されます。
ワークフローファイルで定義されているように、Docker API を使用して必要なイメージをプルまたはビルドし、定義された依存関係に基づいて実行パスを最終的に決定します。
インストール
macOS なら、 Homebrew を使うのが最もかんたんです。
上記以外の環境の場合は、公式ページを参照するのが良いでしょう。
少し変わったインストール方法として、 Github CLI の拡張としてインストールする方法もあります。
1
| $ gh extension install https://github.com/nektos/gh-act
|
試してみる前に ( .github/workflow/ 内の設定ファイルの用意 )
自分で Github Actions の学習のために作成した Public リポジトリで試してみることにしました。
まずはリポジトリを用意し、移動します。
1
2
3
| $ git clone ssh://git@github.com/genzouw/learning-github-actions
$ cd learning-github-actions
|
初回実行時の挙動
act コマンドを初めて実行すると、デフォルトイメージとしてどのイメージを利用するかを問われます。
ここで選択した結果は ~/.actrc ファイルに保存され、以降はこのファイルの設定が参照されます。
あまり読んでもわからなかったのですが、 Large だと 20GB と随分巨大なイメージを使うことに加えて、 「only ubuntu-18.04 platform」と書かれていたため、 Medium を選択することにしました。
1
2
3
4
5
6
7
8
| $ act
? Please choose the default image you want to use with act:
- Large size image: +20GB Docker image, includes almost all tools used on GitHub Actions (IMPORTANT: currently only ubuntu-18.04 platform is available)
- Medium size image: ~500MB, includes only necessary tools to bootstrap actions and aims to be compatible with all actions
- Micro size image: <200MB, contains only NodeJS required to bootstrap actions, doesn't work with all actions
Default image and other options can be changed manually in ~/.actrc (please refer to https://github.com/nektos/act#configuration for additional information about file structure) Medium
|
すべてのジョブを一覧表示 : act --list
act --list を実行すると、設定されているすべてのジョブの一覧が表示されます。
1
2
3
4
5
6
7
8
9
| $ act --list
WARN ⚠ You are using Apple M1 chip and you have not specified container architecture, you might encounter issues while running act. If so, try running it with '--container-architecture linux/amd64'. ⚠
Stage Job ID Job name Workflow name Workflow file Events
0 go-run go-run go-run go-run.yaml push
0 hello hello hello hello.yaml push
0 if-example if-example if-example if-example.yaml push
0 check-bats-version check-bats-version learn-github-actions learn-github-actions.yaml push
0 upload-artifacts upload-artifacts upload-artifacts upload-artifacts.yaml push
0 use-environment-variables use-environment-variables use-environment-variables use-environment-variables.yaml push
|
Apple M1 チップの PC ではワーニングが出ます。
メッセージの通り --container-architecture linux/amd64 オプションを指定することでワーニングを抑制できます。
1
2
3
4
5
6
7
8
| $ act --container-architecture linux/amd64 --list
Stage Job ID Job name Workflow name Workflow file Events
0 go-run go-run go-run go-run.yaml push
0 hello hello hello hello.yaml push
0 if-example if-example if-example if-example.yaml push
0 check-bats-version check-bats-version learn-github-actions learn-github-actions.yaml push
0 upload-artifacts upload-artifacts upload-artifacts upload-artifacts.yaml push
0 use-environment-variables use-environment-variables use-environment-variables use-environment-variables.yaml push
|
以下の alias 設定をしておくのが良さそうです。
1
| alias act='act --container-architecture linux/amd64'
|
ワークフローを実行 : act --workflows <YAML>
--workflows オプションと YAML 設定ファイルを指定して、ワークフローを実行することができます。
以下のような YAML ファイルを指定し実行してみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| $ cat .github/workflows/if-example.yaml
---
name: if-example
run-name: if-example
on:
- push
jobs:
if-example:
runs-on: ubuntu-22.04
steps:
- run: echo "hello false"
if: ${{ false }}
- run: echo "hello true"
if: ${{ true }}
- run: echo "hello $MY_ENV_VAR"
env:
MY_ENV_VAR: ${{ 'development' }}
|
実行結果、 hello true と hello develop という文言が出力されました。
とりあえず動いた模様です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| $ act --workflows .github/workflows/if-example.yaml
[if-example/if-example] ? Start image=node:16-bullseye-slim
[if-example/if-example] ? docker pull image=node:16-bullseye-slim platform=linux/amd64 username= forcePull=true
[if-example/if-example] ? docker create image=node:16-bullseye-slim platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[if-example/if-example] ? docker run image=node:16-bullseye-slim platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[if-example/if-example] ⭐ Run Main echo "hello true"
[if-example/if-example] ? docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/1] user= workdir=
| hello true
[if-example/if-example] ✅ Success - Main echo "hello true"
[if-example/if-example] ⭐ Run Main echo "hello $MY_ENV_VAR"
[if-example/if-example] ? docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/2] user= workdir=
| hello development
[if-example/if-example] ✅ Success - Main echo "hello $MY_ENV_VAR"
[if-example/if-example] ? Job succeeded
|
今度はもう少しだけ複雑なワークフローを実行してみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| $ cat .github/workflows/go-run.yaml
---
name: go-run
run-name: go-run
on:
- push
jobs:
go-run:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Setup Go environment
uses: actions/setup-go@v4.0.0
with:
go-version: "1.20.3"
- run: go run main.go
|
実行した結果、 Go 実行環境のセットアップと実行が成功しました。
たです、実行してみてわかるのですがだいぶ遅いです。
既存の Go Docker イメージを拝借するようにしたほうが早いと思いました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| $ act --workflows .github/workflows/go-run.yaml
[go-run/go-run] ? Start image=node:16-bullseye-slim
[go-run/go-run] ? docker pull image=node:16-bullseye-slim platform=linux/amd64 username= forcePull=true
[go-run/go-run] ? docker create image=node:16-bullseye-slim platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[go-run/go-run] ? docker run image=node:16-bullseye-slim platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[go-run/go-run] ☁ git clone 'https://github.com/actions/setup-go' # ref=v4.0.0
[go-run/go-run] ⭐ Run Main actions/checkout@v3
[go-run/go-run] ? docker cp src=/Users/genzouw/.ghq/github.com/genzouw/learning-github-actions/. dst=/Users/genzouw/.ghq/github.com/genzouw/learning-github-actions
[go-run/go-run] ✅ Success - Main actions/checkout@v3
[go-run/go-run] ⭐ Run Main Setup Go environment
...(省略)...
[go-run/go-run] ⭐ Run Main go run main.go
[go-run/go-run] ? docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/2] user= workdir=
| Hello Github Actions !
[go-run/go-run] ✅ Success - Main go run main.go
[go-run/go-run] ⭐ Run Post Setup Go environment
...(省略)...
[go-run/go-run] ? Job succeeded
|
Dry Run : act --dryrun
設定ファイルの検証を行うだけなら、Dry Run で十分でしょう。
1
2
3
4
5
6
7
8
9
10
| $ act --dryrun --workflows .github/workflows/hello.yaml
*DRYRUN* [hello/hello] ? Start image=node:16-bullseye-slim
*DRYRUN* [hello/hello] ? docker pull image=node:16-bullseye-slim platform=linux/amd64 username= forcePull=true
*DRYRUN* [hello/hello] ? docker create image=node:16-bullseye-slim platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
*DRYRUN* [hello/hello] ? docker run image=node:16-bullseye-slim platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
*DRYRUN* [hello/hello] ⭐ Run Main actions/checkout@v3
*DRYRUN* [hello/hello] ✅ Success - Main actions/checkout@v3
*DRYRUN* [hello/hello] ⭐ Run Main ./.github/actions/say-hello
*DRYRUN* [hello/hello] ✅ Success - Main ./.github/actions/say-hello
*DRYRUN* [hello/hello] ? Job succeeded
|
特に artifacts に成果物をアップロードするような処理を記述していたり、Github インフラ特有の機能を利用していたりすると、ローカルでは当然動かないわけで、そういうときには --dryrun だけは通しておくと安心できます。
実行時に環境変数を渡す : --env
act コマンド実行時に --env オプションで環境変数を指定できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| $ cat .github/workflows/use-environment-variables.yaml
---
name: use-environment-variables
run-name: use-environment-variables
on:
- push
jobs:
use-environment-variables:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- run: node .github/scripts/use_environment_variables.js
$ cat .github/scripts/use_environment_variables.js
#!/usr/bin/env node
console.log("START------");
console.log(process.env.GITHUB_ACTOR);
console.log(process.env.POSTGRES_HOST);
console.log(process.env.POSTGRES_PORT);
console.log("END------");
$ act --env POSTGRES_HOST=hello --env POSTGRES_PORT=9999 --workflows .github/workflows/use-environment-variables.yaml
[use-environment-variables/use-environment-variables] ? Start image=catthehacker/ubuntu:act-22.04
[use-environment-variables/use-environment-variables] ? docker pull image=catthehacker/ubuntu:act-22.04 platform=linux/amd64 username= forcePull=true
[use-environment-variables/use-environment-variables] ? docker create image=catthehacker/ubuntu:act-22.04 platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[use-environment-variables/use-environment-variables] ? docker run image=catthehacker/ubuntu:act-22.04 platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[use-environment-variables/use-environment-variables] ⭐ Run Main actions/checkout@v3
[use-environment-variables/use-environment-variables] ? docker cp src=/Users/genzouw/.ghq/github.com/genzouw/learning-github-actions/. dst=/Users/genzouw/.ghq/github.com/genzouw/learning-github-actions
[use-environment-variables/use-environment-variables] ✅ Success - Main actions/checkout@v3
[use-environment-variables/use-environment-variables] ⭐ Run Main node .github/scripts/use_environment_variables.js
[use-environment-variables/use-environment-variables] ? docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/1] user= workdir=
| START------
| nektos/act
| hello
| 9999
| END------
[use-environment-variables/use-environment-variables] ✅ Success - Main node .github/scripts/use_environment_variables.js
[use-environment-variables/use-environment-variables] ? Job succeeded
|
あるいは、 .env ファイルが存在している場合はこちらから環境変数が充当されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| $ cat .env
POSTGRES_HOST=foo
POSTGRES_PORT=8765
$ act --workflows .github/workflows/use-environment-variables.yaml
[use-environment-variables/use-environment-variables] ? Start image=catthehacker/ubuntu:act-22.04
[use-environment-variables/use-environment-variables] ? docker pull image=catthehacker/ubuntu:act-22.04 platform=linux/amd64 username= forcePull=true
[use-environment-variables/use-environment-variables] ? docker create image=catthehacker/ubuntu:act-22.04 platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[use-environment-variables/use-environment-variables] ? docker run image=catthehacker/ubuntu:act-22.04 platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[use-environment-variables/use-environment-variables] ⭐ Run Main actions/checkout@v3
[use-environment-variables/use-environment-variables] ? docker cp src=/Users/genzouw/.ghq/github.com/genzouw/learning-github-actions/. dst=/Users/genzouw/.ghq/github.com/genzouw/learning-github-actions
[use-environment-variables/use-environment-variables] ✅ Success - Main actions/checkout@v3
[use-environment-variables/use-environment-variables] ⭐ Run Main node .github/scripts/use_environment_variables.js
[use-environment-variables/use-environment-variables] ? docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/1] user= workdir=
| START------
| nektos/act
| foo
| 8765
| END------
[use-environment-variables/use-environment-variables] ✅ Success - Main node .github/scripts/use_environment_variables.js
[use-environment-variables/use-environment-variables] ? Job succeeded
|
イベント情報の取得
github. プレイックスで始まるイベント情報も取得できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| $ cat .github/workflows/print-variables.yaml
---
name: Print github contexts
run-name: Print github contexts
on:
- push
jobs:
print_github_contexts:
runs-on: ubuntu-22.04
env:
TZ: "Asia/Tokyo"
steps:
- name: Print Github Contexts
run: |
echo "github.action ${{github.action}}"
echo "github.action_path ${{github.action_path}}"
echo "github.actor ${{github.actor}}"
echo "github.base_ref ${{github.base_ref}}"
echo "github.event ${{github.event}}"
echo "github.event_name ${{github.event_name}}"
echo "github.event_path ${{github.event_path}}"
echo "github.head_ref ${{github.head_ref}}"
echo "github.job ${{github.job}}"
echo "github.ref ${{github.ref}}"
echo "github.repository ${{github.repository}}"
echo "github.repository_owner ${{github.repository_owner}}"
echo "github.run_id ${{github.run_id}}"
echo "github.run_number ${{github.run_number}}"
echo "github.sha ${{github.sha}}"
echo "github.token ${{github.token}}"
echo "github.workflow ${{github.workflow}}"
echo "github.workspace ${{github.workspace}}"
$ act --workflows .github/workflows/print-variables.yaml
[Print github contexts/print_github_contexts] ? Start image=catthehacker/ubuntu:act-22.04
[Print github contexts/print_github_contexts] ? docker pull image=catthehacker/ubuntu:act-22.04 platform=linux/amd64 username= forcePull=true
[Print github contexts/print_github_contexts] ? docker create image=catthehacker/ubuntu:act-22.04 platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[Print github contexts/print_github_contexts] ? docker run image=catthehacker/ubuntu:act-22.04 platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[Print github contexts/print_github_contexts] ⭐ Run Main Print Github Contexts
[Print github contexts/print_github_contexts] ? docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/0] user= workdir=
| github.action 0
| github.action_path
| github.actor nektos/act
| github.base_ref
| github.event Object
| github.event_name push
| github.event_path /var/run/act/workflow/event.json
| github.head_ref
| github.job print_github_contexts
| github.ref refs/tags/v1.0.0
| github.repository genzouw/learning-github-actions
| github.repository_owner genzouw
| github.run_id 1
| github.run_number 1
| github.sha e0ddc159d2e4dc5047108ed13d88e5c68966e279
| github.token
| github.workflow Print github contexts
| github.workspace /Users/genzouw/.ghq/github.com/genzouw/learning-github-actions
[Print github contexts/print_github_contexts] ✅ Success - Main Print Github Contexts
[Print github contexts/print_github_contexts] ? Job succeeded
|
ひとこと
シークレットを利用する事もできそうですし、もう少し使ってみたいと思います。