ローカルPC内でGithub Actionsを実行する – ‘nekos/act’
はじめに
Github Actions の設定ファイルを記述した後、正しく動作するかを確認するために毎回リモートリポジトリにプッシュするのが面倒だなと思ったので良い方法が無いかと調べてみました。
すると、ローカルで Github Actions の実行が可能なツールが見つかったので、検証してみました。
検証環境
$ 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
を呼ぶことで回避できると思いますが公式ページのメリットとして書かれていました )
- (この点については、 Github Actions から
nektos/act
って何をしてくれるに?
act
コマンドが提供されます。
act
コマンドを実行すると、 .github/workflows/
ディレクトリから設定ファイルを読み込み、実行すべきアクションのセットを決定します。
実行されるアクションを決定するためには「イベント」 ( push
、branch
作成など ) を指定しなければいけませんが、明示しなければ push
イベントが発生したものとして解釈されます。
ワークフローファイルで定義されているように、Docker API を使用して必要なイメージをプルまたはビルドし、定義された依存関係に基づいて実行パスを最終的に決定します。
インストール
macOS なら、 Homebrew を使うのが最もかんたんです。
brew install act
上記以外の環境の場合は、公式ページを参照するのが良いでしょう。
少し変わったインストール方法として、 Github CLI の拡張としてインストールする方法もあります。
$ gh extension install https://github.com/nektos/gh-act
試してみる前に ( .github/workflow/
内の設定ファイルの用意 )
自分で Github Actions の学習のために作成した Public リポジトリで試してみることにしました。
まずはリポジトリを用意し、移動します。
$ git clone ssh://git@github.com/genzouw/learning-github-actions
$ cd learning-github-actions
初回実行時の挙動
act
コマンドを初めて実行すると、デフォルトイメージとしてどのイメージを利用するかを問われます。
ここで選択した結果は ~/.actrc
ファイルに保存され、以降はこのファイルの設定が参照されます。
あまり読んでもわからなかったのですが、 Large だと 20GB と随分巨大なイメージを使うことに加えて、 「only ubuntu-18.04 platform」と書かれていたため、 Medium を選択することにしました。
$ 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
を実行すると、設定されているすべてのジョブの一覧が表示されます。
$ 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
オプションを指定することでワーニングを抑制できます。
$ 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 設定をしておくのが良さそうです。
alias act='act --container-architecture linux/amd64'
ワークフローを実行 : act --workflows <YAML>
--workflows
オプションと YAML 設定ファイルを指定して、ワークフローを実行することができます。
以下のような YAML ファイルを指定し実行してみます。
$ 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
という文言が出力されました。
とりあえず動いた模様です。
$ 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
今度はもう少しだけ複雑なワークフローを実行してみます。
$ 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 イメージを拝借するようにしたほうが早いと思いました。
$ 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 で十分でしょう。
$ 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
オプションで環境変数を指定できます。
$ 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
ファイルが存在している場合はこちらから環境変数が充当されます。
$ 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.
プレイックスで始まるイベント情報も取得できます。
$ 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
ひとこと
シークレットを利用する事もできそうですし、もう少し使ってみたいと思います。
ディスカッション
コメント一覧
まだ、コメントがありません