[🔰初心者向け] GitHub Actions をローカルマシンで実行する act を使うぞ

色々な記事を調べてみたのだけど、脳死で手順通りにやれば OK という感じの記事がなかったので、自分で作成することにした。

最初の一歩には不要な、めちゃめちゃ大変そうなことが書かれていたりして、読んでいて疲れる。この記事は、必要最低限のことしか書きません。ささっと導入しましょう(\*˘︶˘\*).｡.:*

# 1. act とは

GitHub Actions 上で動作する workflow をローカルマシン上で動作させることができるツール。
`workflow` の動作テストなどを省力化できるのが良いところ。

リポジトリは [nektos/act](https://github.com/nektos/act?tab=readme-ov-file)。

# 2. 想定読者

- GitHub Actions は使ったことがあるが初心者
- そんなに難しいことはしていない (環境変数とか、ACCESS_TOKEN がなくても動くような workflow を作成している)

# 3. 動作環境

項目|バージョンなど
---|---
OS|MacOS
Chip(CPU)|M2

Mac なので、ベースとなる手順はここ参照した。

https://nektosact.com/installation/homebrew.html


# 4. 手順

## 4-0. workflow ファイルの用意

このようなファイルを使って動作を確認した。

```yaml:.github/workflows/ci_lint.yml
name: Lint and Type Check

on:
  push:

jobs:
  mypy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install dependencies
        run: pip install mypy types-requests types-PyYAML

      - name: Run mypy
        run: mypy .

```

## 4-1. インストール

act は以下のようにインストールできる。

```bash
% brew install act
% which act
/opt/homebrew/bin/act
```

## 4-2. 動かせる workflow 一覧の確認

`.github/workflows` 以下にある workflow ファイルのジョブ一覧が表示される。

```bash
% act -l
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'. ⚠  
Stage  Job ID         Job name       Workflow name        Workflow file       Events
0      mypy           mypy           Lint and Type Check  ci_lint.yml         push
0      ruff-format    ruff-format    Lint and Type Check  ci_lint.yml         push
0      ruff-check     ruff-check     Lint and Type Check  ci_lint.yml         push
```

## 4-3. 初回実行時の必須作業

`act -l` で `Job ID` を確認してから実行しよう。
ちなみに、**全部のジョブを一気に実行したい**ときは、 `act` だけ実行すれば良い。

```bash
# job を実行するときは -j で Job ID を指定
$ act -j mypy
WARN[0000] Couldn't get a valid docker connection: no DOCKER_HOST and an invalid container socket '' 
? 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 /root/.config/act/actrc (please refer to https://nektosact.com/usage/index.html?highlight=configur#configuration-file for additional information about file structure)  [Use arrows to move, type to filter, ? for more help]
  Large
> Medium
  Micro

```

この画面では、矢印キー「↑」「↓」でインストールするものを選べる。
私は mypy とか ruff ぐらいしか動かさないので「Micro」にした。


## 4-4. Mac 特有っぽいエラーとその対処

### 4-4-1. エラー

`::error::The version '3.10' with architecture 'x86' was not found for this operating system.` というエラーが出る。

`x86` のところは `arc64` だったり色々する。

```bash
% act -j mypy
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'. ⚠  
[Lint and Type Check/mypy] ⭐ Run Set up job
[Lint and Type Check/mypy] 🚀  Start image=node:16-buster-slim
[Lint and Type Check/mypy]   🐳  docker pull image=node:16-buster-slim platform= username= forcePull=true
[Lint and Type Check/mypy]   🐳  docker create image=node:16-buster-slim platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Lint and Type Check/mypy]   🐳  docker run image=node:16-buster-slim platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Lint and Type Check/mypy]   🐳  docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir=
[Lint and Type Check/mypy]   ✅  Success - Set up job
[Lint and Type Check/mypy]   ☁  git clone 'https://github.com/actions/setup-python' # ref=v5
[Lint and Type Check/mypy] ⭐ Run Main actions/checkout@v4
[Lint and Type Check/mypy]   🐳  docker cp src=/Users/user/src/repo/. dst=/Users/user/src/repo
[Lint and Type Check/mypy]   ✅  Success - Main actions/checkout@v4 [265.517375ms]
[Lint and Type Check/mypy] ⭐ Run Main actions/setup-python@v5
[Lint and Type Check/mypy]   🐳  docker cp src=/Users/user/.cache/act/actions-setup-python@v5/ dst=/var/run/act/actions/actions-setup-python@v5/
[Lint and Type Check/mypy]   🐳  docker exec cmd=[/usr/local/bin/node /var/run/act/actions/actions-setup-python@v5/dist/setup/index.js] user= workdir=
[Lint and Type Check/mypy]   ❓  ::group::Installed versions
| Version 3.10 was not found in the local cache
[Lint and Type Check/mypy]   ❗  ::error::The version '3.10' with architecture 'x86' was not found for this operating system.%0AThe list of all available versions can be found here: https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json
[Lint and Type Check/mypy]   ❌  Failure - Main actions/setup-python@v5 [2.715746833s]
[Lint and Type Check/mypy] exitcode '1': failure
[Lint and Type Check/mypy] ⭐ Run Complete job
[Lint and Type Check/mypy]   ✅  Success - Complete job
[Lint and Type Check/mypy] 🏁  Job failed
Error: Job 'mypy' failed
```

### 4-4-2. 対処法

以下の記事に対処法あり。

https://qiita.com/sokorahen-szk/items/2c2812855b05a61ff173

https://zenn.dev/kawataku/articles/508c0abd50482b

具体的には、`~/.actrc` において、使用する Docker イメージを指定すると良い。

```bash
vim ~/.actrc
```

自分は以下のようにした。

```ini:~/.actrc
-P ubuntu-latest=catthehacker/ubuntu:act-22.04
-P ubuntu-18.04=catthehacker/ubuntu:full-18.04
```

すると、以下のように動かすことができた。

```bash
% act -j mypy 
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'. ⚠  
[Lint and Type Check/mypy] ⭐ Run Set up job
[Lint and Type Check/mypy] 🚀  Start image=catthehacker/ubuntu:act-22.04
[Lint and Type Check/mypy]   🐳  docker pull image=catthehacker/ubuntu:act-22.04 platform= username= forcePull=true
[Lint and Type Check/mypy]   🐳  docker create image=catthehacker/ubuntu:act-22.04 platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Lint and Type Check/mypy]   🐳  docker run image=catthehacker/ubuntu:act-22.04 platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Lint and Type Check/mypy]   🐳  docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir=
[Lint and Type Check/mypy]   ✅  Success - Set up job
[Lint and Type Check/mypy]   ☁  git clone 'https://github.com/actions/setup-python' # ref=v5
[Lint and Type Check/mypy] ⭐ Run Main actions/checkout@v4
[Lint and Type Check/mypy]   🐳  docker cp src=/Users/user/src/repo/. dst=/Users/user/src/repo
[Lint and Type Check/mypy]   ✅  Success - Main actions/checkout@v4 [552.468625ms]
[Lint and Type Check/mypy] ⭐ Run Main actions/setup-python@v5
[Lint and Type Check/mypy]   🐳  docker cp src=/Users/user/.cache/act/actions-setup-python@v5/ dst=/var/run/act/actions/actions-setup-python@v5/
[Lint and Type Check/mypy]   🐳  docker exec cmd=[/opt/acttoolcache/node/18.20.8/arm64/bin/node /var/run/act/actions/actions-setup-python@v5/dist/setup/index.js] user= workdir=
[Lint and Type Check/mypy]   ❓  ::group::Installed versions
| Version 3.10 was not found in the local cache
| Version 3.10 is available for downloading
| Download from "https://github.com/actions/python-versions/releases/download/3.10.18-15433209320/python-3.10.18-linux-22.04-arm64.tar.gz"
| Extract downloaded archive
| [command]/usr/bin/tar xz --warning=no-unknown-keyword --overwrite -C /tmp/4acfe117-40e2-4539-a6e3-901529967d39 -f /tmp/9eb2a8b5-5c02-44c1-b024-f1f15a042e1c
| Execute installation script
| Check if Python hostedtoolcache folder exist...
| Creating Python hostedtoolcache folder...
| Create Python 3.10.18 folder
| Copy Python binaries to hostedtoolcache folder
| Create additional symlinks (Required for the UsePythonVersion Azure Pipelines task and the setup-python GitHub Action)
| Upgrading pip...
| Looking in links: /tmp/tmpt83vuv2j
| Requirement already satisfied: setuptools in /opt/hostedtoolcache/Python/3.10.18/arm64/lib/python3.10/site-packages (65.5.0)
| Requirement already satisfied: pip in /opt/hostedtoolcache/Python/3.10.18/arm64/lib/python3.10/site-packages (23.0.1)
[Lint and Type Check/mypy]   ❗  ::error::WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
| Collecting pip
| Downloading pip-25.1.1-py3-none-any.whl (1.8 MB)
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 11.1 MB/s eta 0:00:00
| 
| Installing collected packages: pip
| Attempting uninstall: pip
| Found existing installation: pip 23.0.1
| Uninstalling pip-23.0.1:
| Successfully uninstalled pip-23.0.1
| Successfully installed pip-25.1.1
| Create complete file
| Successfully set up CPython (3.10.18)
[Lint and Type Check/mypy]   ❓  ::endgroup::
[Lint and Type Check/mypy]   ❓ add-matcher /run/act/actions/actions-setup-python@v5/.github/python.json
[Lint and Type Check/mypy]   ✅  Success - Main actions/setup-python@v5 [17.6543125s]
[Lint and Type Check/mypy]   ⚙  ::set-env:: PKG_CONFIG_PATH=/opt/hostedtoolcache/Python/3.10.18/arm64/lib/pkgconfig
[Lint and Type Check/mypy]   ⚙  ::set-env:: Python_ROOT_DIR=/opt/hostedtoolcache/Python/3.10.18/arm64
[Lint and Type Check/mypy]   ⚙  ::set-env:: Python2_ROOT_DIR=/opt/hostedtoolcache/Python/3.10.18/arm64
[Lint and Type Check/mypy]   ⚙  ::set-env:: Python3_ROOT_DIR=/opt/hostedtoolcache/Python/3.10.18/arm64
[Lint and Type Check/mypy]   ⚙  ::set-env:: LD_LIBRARY_PATH=/opt/hostedtoolcache/Python/3.10.18/arm64/lib
[Lint and Type Check/mypy]   ⚙  ::set-env:: pythonLocation=/opt/hostedtoolcache/Python/3.10.18/arm64
[Lint and Type Check/mypy]   ⚙  ::set-output:: python-version=3.10.18
[Lint and Type Check/mypy]   ⚙  ::set-output:: python-path=/opt/hostedtoolcache/Python/3.10.18/arm64/bin/python
[Lint and Type Check/mypy]   ⚙  ::add-path:: /opt/hostedtoolcache/Python/3.10.18/arm64
[Lint and Type Check/mypy]   ⚙  ::add-path:: /opt/hostedtoolcache/Python/3.10.18/arm64/bin
[Lint and Type Check/mypy] ⭐ Run Main Install dependencies
[Lint and Type Check/mypy]   🐳  docker exec cmd=[bash -e /var/run/act/workflow/2] user= workdir=
| Collecting mypy
|   Downloading mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl.metadata (2.1 kB)
| Collecting typing_extensions>=4.6.0 (from mypy)
|   Downloading typing_extensions-4.14.0-py3-none-any.whl.metadata (3.0 kB)
| Collecting mypy_extensions>=1.0.0 (from mypy)
|   Downloading mypy_extensions-1.1.0-py3-none-any.whl.metadata (1.1 kB)
| Collecting pathspec>=0.9.0 (from mypy)
|   Downloading pathspec-0.12.1-py3-none-any.whl.metadata (21 kB)
| Collecting tomli>=1.1.0 (from mypy)
|   Downloading tomli-2.2.1-py3-none-any.whl.metadata (10 kB)
| Downloading mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl (11.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 11.9/11.9 MB 51.1 MB/s eta 0:00:00
| Downloading mypy_extensions-1.1.0-py3-none-any.whl (5.0 kB)
| Downloading pathspec-0.12.1-py3-none-any.whl (31 kB)
| Downloading tomli-2.2.1-py3-none-any.whl (14 kB)
| Downloading typing_extensions-4.14.0-py3-none-any.whl (43 kB)
| Installing collected packages: typing_extensions, tomli, pathspec, mypy_extensions, mypy
Successfully installed mypy-1.16.0 mypy_extensions-1.1.0 pathspec-0.12.1 tomli-2.2.1 typing_extensions-4.14.0
| WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.
[Lint and Type Check/mypy]   ✅  Success - Main Install dependencies [3.708810375s]
[Lint and Type Check/mypy] ⭐ Run Main Run mypy
[Lint and Type Check/mypy]   🐳  docker exec cmd=[bash -e /var/run/act/workflow/3] user= workdir=
| setup.py:1: error: Library stubs not installed for "setuptools"  [import-untyped]
| setup.py:1: note: Hint: "python3 -m pip install types-setuptools"

# ...略

| Found 37 errors in 13 files (checked 18 source files)
[Lint and Type Check/mypy]   ❌  Failure - Main Run mypy [951.902417ms]
[Lint and Type Check/mypy] exitcode '1': failure
[Lint and Type Check/mypy] ⭐ Run Complete job
[Lint and Type Check/mypy]   ✅  Success - Complete job
[Lint and Type Check/mypy] 🏁  Job failed
Error: Job 'mypy' failed
```

# 5. 補足

最初は Docker コンテナを立てて、その中に act などをインストールしたいと思っていたが、マシンのOS上に直接インストールした。

## 理由

Docker コンテナの中に Docker コンテナを立てることが結構面倒 (Docker Hub へのアクセス権限とか...) だったため。


# 6. Link 集

## 6-1. act の公式 user's ガイド

https://nektosact.com/usage/index.html

## 6-2. GitHub Actions 上で環境変数を利用したい場合

ローカルで実行する GitHub Actions の環境変数設定方法は以下の記事を参照

https://qiita.com/wwalpha/items/6c303dcf04e236238315#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0