🤖

[act]GitHub Actionsをローカルで試して仲良くなる

に公開

概要

こんにちは🙌

GitHub Actionsを用いたCI/CDの実現に向けて、workflowファイルの作成が必要ですが、これにはリポジトリへの繰り返しプッシュやマージ時の動作確認など、手間が伴うことがあります。

そんな時、ローカル環境での動作確認を可能にするnektos/actが役立ちます。このツールは既に広く知られているかもしれませんが、実際に試した記録を残します。

actとは

ユーザーが自分のGitHub Actionsをローカルで実行できるようにするツールです。
これにより、変更をプッシュすることなく、迅速にフィードバックを得てテストを行うことが可能になります。このツールは.github/workflows/からGitHub Actionsを読み取り、Dockerを使用して必要なイメージをビルドまたはプルし、コンテナ内でアクションを実行するプランを作成します。
これによりGitHubの環境を模倣し、ローカルでのアクションテストをスムーズに行うことができます​。

ゴール

  • サンプルコードを使ってローカル環境でworkflowが実行できること
  • actの便利なオプションを試してみる

環境

チップ Apple M2 Pro
OS macOS Sonoma14.1.1
Docker Desktop 4.26.1 (131620)

インストール

act - User Guide - Homebrew
brewでインストールします。

$ brew install act
==> Downloading https://ghcr.io/v2/homebrew/core/act/manifests/0.2.60
############################################################################################################################################################################# 100.0%
==> Fetching act
==> Downloading https://ghcr.io/v2/homebrew/core/act/blobs/sha256:5533293b8efc5d003e2f607626ac2a487c7f92202775f9541a0dc3a9f17bb931
############################################################################################################################################################################# 100.0%
==> Pouring act--0.2.60.arm64_sonoma.bottle.tar.gz
🍺  /opt/homebrew/Cellar/act/0.2.60: 5 files, 21.6MB
==> Running `brew cleanup act`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).

早速使ってみる

actでは、demoのためのサンプルリポジトリが用意されているため、インストールしたら直ぐにそれを使って確認することが出来ます。

# リポジトリをクローンしてくる
$ git clone https://github.com/cplee/github-actions-demo.git
Cloning into 'github-actions-demo'...
remote: Enumerating objects: 110, done.
remote: Counting objects: 100% (49/49), done.
remote: Compressing objects: 100% (34/34), done.
remote: Total 110 (delta 32), reused 15 (delta 15), pack-reused 61
Receiving objects: 100% (110/110), 68.28 KiB | 416.00 KiB/s, done.
Resolving deltas: 100% (39/39), done.

workflowのファイルは以下のように書かれています。

.github/workflows/main.yml
name: CI
on: push

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v1
      - run: npm install
      - run: npm test

とりあえず初回actを実行してみました。
Appleシリコンを使っていると問題があるかもしれないとの警告が表示されていますが、一旦無視して進めます。
デフォルトイメージのサイズの選択が求められます。一般的な用途であれば、Mediumを選択しておけば問題ないかと思います。
約500MBで、アクションをブートストラップするのに必要なツールのみを含んでおり、ほとんどのアクションと互換性があるとのこと。

$ cd github-actions-demo
$ act
INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock'
WARN  ⚠ You are using Apple M-series 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'. ⚠
? Please choose the default image you want to use with act:
  - Large size image: ca. 17GB download + 53.1GB storage, you will need 75GB of free disk space, snapshots of GitHub Hosted Runners without snap and pulled docker images
  - Medium size image: ~500MB, includes only necessary tools to bootstrap actions and aims to be compatible with most 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)  [Use arrows to move, type to filter, ? for more help]
  Large
> Medium
  Micro

イメージサイズが選択されると、.github/workflowが実行されていきます。(出力が長いので、途中を省略して載せています。)ファイル記載の内容の通りとなりますが、
DockerからPullしてイメージの取得をしてUbuntu環境のコンテナを作成、
actions/checkout@v2: GitHubリポジトリをチェックアウト、
actions/setup-node@v1: Node.jsの環境をセットアップ、
npm installnpm testが実行されて、全てのジョブが成功されたことが出力されています。

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
[CI/test] 🚀  Start image=catthehacker/ubuntu:act-latest
INFO[0685] Parallel tasks (0) below minimum, setting to 1
[CI/test]   🐳  docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
[CI/test] using DockerAuthConfig authentication for docker pull
INFO[0706] Parallel tasks (0) below minimum, setting to 1
[CI/test]   🐳  docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[CI/test]   🐳  docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[CI/test]git clone 'https://github.com/actions/setup-node' # ref=v1
[CI/test] ⭐ Run Main actions/checkout@v2
[CI/test]   🐳  docker cp src=/Users/kojitsukamoto/Projects/develop/github-actions-demo/. dst=/Users/kojitsukamoto/Projects/develop/github-actions-demo
[CI/test]   ✅  Success - Main actions/checkout@v2
[CI/test] ⭐ Run Main actions/setup-node@v1
[CI/test]   🐳  docker cp src=/Users/kojitsukamoto/.cache/act/actions-setup-node@v1/ dst=/var/run/act/actions/actions-setup-node@v1/
[CI/test]   🐳  docker exec cmd=[node /var/run/act/actions/actions-setup-node@v1/dist/index.js] user= workdir=
~~~
[CI/test]   🐳  docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/2] user= workdir=
| added 280 packages from 643 contributors and audited 280 packages in 2.515s
|
| 24 packages are looking for funding
|   run `npm fund` for details
|
| found 34 vulnerabilities (17 moderate, 14 high, 3 critical)
|   run `npm audit fix` to fix them, or `npm audit` for details
[CI/test]   ✅  Success - Main npm install
[CI/test] ⭐ Run Main npm test
[CI/test]   🐳  docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/3] user= workdir=
|
| > github-actions-demo@1.0.0 test /Users/kojitsukamoto/Projects/develop/github-actions-demo
| > mocha ./tests --recursive
|
|
|
|   GET /
|     ✓ should respond with hello world
|
|
|   1 passing (11ms)
|
[CI/test]   ✅  Success - Main npm test
[CI/test] Cleaning up container for job test
[CI/test] 🏁  Job succeeded
|

簡単にローカル環境でWorkflowを試すことができました。Dockerがある環境であれば、どこでもローカルマシン上でWorkflowを実行することが分かりました。
これは、例えばローカル環境でGithub Actionsを実行したいというユースケースにも応用することができます。インターネット環境がないようなRaspberry Pi等でも実行することが出来ます。

オプションを試してみる

いくつかオプションについても試してみようと思います。

-P:custom image to use per platform (e.g. -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04)

GitHub Actionsでも提供されているように、Workflow実行環境を複数から選ぶことが出来ます。
参考:GitHub ホステッド ランナーの概要

# 実行例
$ act -P ubuntu-latest=-self-hosted
$ act -P windows-latest=-self-hosted
$ act -P macos-latest=-self-hosted

-n:dryrun mode

ドライランでは、Workflowの正しさだけを確認します。Containerは立ち上がりません。(本来であればact実行中にコンテナが立ち上がり、Workflowが実行されていきます。)
参考:Document what "dryrun" means for this tool?

$ act -n --container-architecture linux/amd64
INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock'
*DRYRUN* [CI/test] 🚀  Start image=catthehacker/ubuntu:act-latest
INFO[0000] Parallel tasks (0) below minimum, setting to 1
*DRYRUN* [CI/test]   🐳  docker pull image=catthehacker/ubuntu:act-latest platform=linux/amd64 username= forcePull=true
INFO[0000] Parallel tasks (0) below minimum, setting to 1
*DRYRUN* [CI/test]   🐳  docker create image=catthehacker/ubuntu:act-latest platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
*DRYRUN* [CI/test]   🐳  docker run image=catthehacker/ubuntu:act-latest platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
*DRYRUN* [CI/test]git clone 'https://github.com/actions/setup-node' # ref=v1
*DRYRUN* [CI/test] ⭐ Run Main actions/checkout@v2
*DRYRUN* [CI/test]   ✅  Success - Main actions/checkout@v2
*DRYRUN* [CI/test] ⭐ Run Main actions/setup-node@v1
*DRYRUN* [CI/test]   ✅  Success - Main actions/setup-node@v1
*DRYRUN* [CI/test] ⭐ Run Main npm install
*DRYRUN* [CI/test]   ✅  Success - Main npm install
*DRYRUN* [CI/test] ⭐ Run Main npm test
*DRYRUN* [CI/test]   ✅  Success - Main npm test
*DRYRUN* [CI/test] Cleaning up container for job test
*DRYRUN* [CI/test] 🏁  Job succeeded

-j:run a specific job ID

デフォルトではpushイベントをトリガーとして全てのWorkflowが実行されることになっていますが、-jオプションを使うことで特定のジョブを実行することが出来ます。
サンプルにジョブを追加して実行しています。

.github/workflows/main.yml
~~~
  sample-job:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Hello, World!"
$ act -j "sample-job" --container-architecture linux/amd64
INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock'
[CI/sample-job] 🚀  Start image=catthehacker/ubuntu:act-latest
INFO[0000] Parallel tasks (0) below minimum, setting to 1
[CI/sample-job]   🐳  docker pull image=catthehacker/ubuntu:act-latest platform=linux/amd64 username= forcePull=true
[CI/sample-job] using DockerAuthConfig authentication for docker pull
INFO[0005] Parallel tasks (0) below minimum, setting to 1
[CI/sample-job]   🐳  docker create image=catthehacker/ubuntu:act-latest platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[CI/sample-job]   🐳  docker run image=catthehacker/ubuntu:act-latest platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[CI/sample-job] ⭐ Run Main echo "Hello, World!"
[CI/sample-job]   🐳  docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/0] user= workdir=
| Hello, World!
[CI/sample-job]   ✅  Success - Main echo "Hello, World!"
[CI/sample-job] Cleaning up container for job sample-job
[CI/sample-job] 🏁  Job succeeded

-s:secret to make available to actions with optional value (e.g. -s mysecret=foo or -s mysecret)

Secretを渡してその値を使って実行することも可能となっています。GitHubアクションにおいてもSecretをリポジトリに設定、もしくはAWS SecretsManager等を用いてWorkflowの中で実行するユースケースは多くあるかと思います。
以下を追加しています。

.github/workflows/main.yml
~~~
  sample-secret:
    runs-on: ubuntu-latest
    steps:
      - name: Hello world action step
        uses: actions/hello-world-javascript-action@v1
        with:
          who-to-greet: ${{ secrets.WHO_TO_GREET }}
$ act -j sample-secret -s WHO_TO_GREET="cozy07"
INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock'
WARN  ⚠ You are using Apple M-series 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'.[CI/sample-secret] 🚀  Start image=catthehacker/ubuntu:act-latest
INFO[0000] Parallel tasks (0) below minimum, setting to 1
[CI/sample-secret]   🐳  docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
[CI/sample-secret] using DockerAuthConfig authentication for docker pull
INFO[0006] Parallel tasks (0) below minimum, setting to 1
[CI/sample-secret]   🐳  docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[CI/sample-secret]   🐳  docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[CI/sample-secret]git clone 'https://github.com/actions/hello-world-javascript-action' # ref=v1
[CI/sample-secret] ⭐ Run Main Hello world action step
[CI/sample-secret]   🐳  docker cp src=/Users/kojitsukamoto/.cache/act/actions-hello-world-javascript-action@v1/ dst=/var/run/act/actions/actions-hello-world-javascript-action@v1/
[CI/sample-secret]   🐳  docker exec cmd=[node /var/run/act/actions/actions-hello-world-javascript-action@v1/index.js] user= workdir=
| Hello ***!
[CI/sample-secret]   ⚙  ::set-output:: time=03:15:53 GMT+0000 (Coordinated Universal Time)
| The event payload: {}
[CI/sample-secret]   ✅  Success - Main Hello world action step
[CI/sample-secret] Cleaning up container for job sample-secret
[CI/sample-secret] 🏁  Job succeeded

おわりに

サンプルコードメインとなりましたが、今回はactを使ってローカル環境でGithub Actionsが実行できることを確認してみました。
actについて調べてみると、正直まだ完全にGithub Actionsをローカル環境で模擬したものであるかと言うと微妙なところもあるらしく、使っていく中でその辺りももっと探れたらと思います。

ありがとうございました🙌🙌

Discussion