Github ActionsのYAMLを静的解析するactionlintというツールがある1。今回はそれを使ってみる。
READMEに従い go install
でインストールする。
go install github.com/rhysd/actionlint/cmd/actionlint@latest
$HOME/go/bin
配下に actionlint
というコマンドが生成される。
$HOME/go/bin
にはパスを通しておく。
export PATH=$HOME/go/bin:$PATH
Usage: actionlint [FLAGS] [FILES...] [-] actionlint is a linter for GitHub Actions workflow files. To check all YAML files in current repository, just run actionlint without arguments. It automatically finds the nearest '.github/workflows' directory: $ actionlint To check specific files, pass the file paths as arguments: $ actionlint file1.yaml file2.yaml To check content which is not saved in file yet (e.g. output from some command), pass - argument. It reads stdin and checks it as workflow file: $ actionlint - To serialize errors into JSON, use -format option. It allows to format error messages flexibly with Go template syntax. $ actionlint -format '{{json .}}' Documents: https://github.com/rhysd/actionlint/tree/main/docs Flags: -color Always enable colorful output. This is useful to force colorful outputs -config-file string File path to config file -debug Enable debug output (for development) -format string Custom template to format error messages in Go template syntax. See https://github.com/rhysd/actionlint/tree/main/docs/usage.md#format -ignore value Regular expression matching to error messages you want to ignore. This flag is repeatable -init-config Generate default config file at .github/actionlint.yaml in current project -no-color Disable colorful output -oneline Use one line per one error. Useful for reading error messages from programs -pyflakes string Command name or file path of "pyflakes" external command. If empty, pyflakes integration will be disabled (default "pyflakes") -shellcheck string Command name or file path of "shellcheck" external command. If empty, shellcheck integration will be disabled (default "shellcheck") -stdin-filename string File name when reading input from stdin -verbose Enable verbose output -version Show version and how this binary was installed
READMEに掲載されている壊れたYAMLを使いコマンドを実行し、どのような出力となるのか確認する。
actionlint broken.yml
broken.yml:3:5: unexpected key "branch" for "push" section. expected one of "branches", "branches-ignore", "paths", "paths-ignore", "tags", "tags-ignore", "types", "workflows" [syntax-check] | 3 | branch: main | ^~~~~~~ broken.yml:5:11: character '\' is invalid for branch and tag names. only special characters [, ?, +, *, \ ! can be escaped with \. see `man git-check-ref-format` for more details. note that regular expression is unavailable. note: filter pattern syntax is explained at https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet [glob] | 5 | - 'v\d+' | ^~~~ broken.yml:10:28: label "linux-latest" is unknown. available labels are "windows-latest", "windows-2022", "windows-2019", "windows-2016", "ubuntu-latest", "ubuntu-22.04", "ubuntu-20.04", "ubuntu-18.04", "macos-latest", "macos-latest-xl", "macos-13-xl", "macos-13", "macos-13.0", "macos-12-xl", "macos-12", "macos-12.0", "macos-11", "macos-11.0", "macos-10.15", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file [runner-label] | 10 | os: [macos-latest, linux-latest] | ^~~~~~~~~~~~~ broken.yml:13:41: "github.event.head_commit.message" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions for more details [expression] | 13 | - run: echo "Checking commit '${{ github.event.head_commit.message }}'" | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ broken.yml:17:11: input "node_version" is not defined in action "actions/setup-node@v3". available inputs are "always-auth", "architecture", "cache", "cache-dependency-path", "check-latest", "node-version", "node-version-file", "registry-url", "scope", "token" [action] | 17 | node_version: 16.x | ^~~~~~~~~~~~~ broken.yml:21:20: property "platform" is not defined in object type {os: string} [expression] | 21 | key: ${{ matrix.platform }}-node-${{ hashFiles('**/package-lock.json') }} | ^~~~~~~~~~~~~~~ broken.yml:22:17: receiver of object dereference "permissions" must be type of object but got "string" [expression] | 22 | if: ${{ github.repository.permissions.admin == true }} | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-no-color
や -oneline
を使う事でプログラム的に扱い易くなりそうだ。
actionlint -no-color -oneline broken.yml
broken.yml:3:5: unexpected key "branch" for "push" section. expected one of "branches", "branches-ignore", "paths", "paths-ignore", "tags", "tags-ignore", "types", "workflows" [syntax-check] broken.yml:5:11: character '\' is invalid for branch and tag names. only special characters [, ?, +, *, \ ! can be escaped with \. see `man git-check-ref-format` for more details. note that regular expression is unavailable. note: filter pattern syntax is explained at https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet [glob] broken.yml:10:28: label "linux-latest" is unknown. available labels are "windows-latest", "windows-2022", "windows-2019", "windows-2016", "ubuntu-latest", "ubuntu-22.04", "ubuntu-20.04", "ubuntu-18.04", "macos-latest", "macos-latest-xl", "macos-13-xl", "macos-13", "macos-13.0", "macos-12-xl", "macos-12", "macos-12.0", "macos-11", "macos-11.0", "macos-10.15", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file [runner-label] broken.yml:13:41: "github.event.head_commit.message" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions for more details [expression] broken.yml:17:11: input "node_version" is not defined in action "actions/setup-node@v3". available inputs are "always-auth", "architecture", "cache", "cache-dependency-path", "check-latest", "node-version", "node-version-file", "registry-url", "scope", "token" [action] broken.yml:21:20: property "platform" is not defined in object type {os: string} [expression] broken.yml:22:17: receiver of object dereference "permissions" must be type of object but got "string" [expression]
これなら自分でflymakeも書けそうだが、flymake用の拡張は既に実装された flymake-actionlint
というものがあるから、まずはそれを使う事にしよう2 。
M-x package-install RET flymake-actionlint RET
.github/workflows
配下のYAMLファイルを開き、 flymake-actionlint-load
を実行すると、以下のようにエラー箇所が赤く表示される。
良さげだ。