actionlintは、GitHub Actionsのワークフローファイル(.github/workflows/*.yml)をpushする前に検査する静的解析ツールです。作者はrhysd氏、Go製でMITライセンス、2026年10月時点の最新版はv1.7.12(2026年3月30日リリース)です。YAMLの構文だけでなく、${{ }}式の型、存在しないstep IDやjobへの参照、run:のシェルスクリプト(shellcheck連携)、信頼できない入力によるスクリプトインジェクションまで検出します。この記事では、実際に誤りを仕込んだワークフローに対するv1.7.12の出力と直し方を示したうえで、インストール、警告の除外設定、CIへの組み込みと必須チェック化、ghalint・zizmorとの使い分けを説明します。v1.7.12の後にGitHubが追加した構文(concurrency.queueやbackground steps、ubuntu-26.04ラベルなど)を誤ってエラーにする問題も、実測結果と回避設定をまとめました。
まとめ:actionlintの要点(v1.7.12時点)
- 引数なしの
actionlintで、リポジトリ内の.github/workflows配下を全部検査します。問題があれば終了コード1を返すので、CIの失敗条件にそのまま使えます。 - インストールはmacOSなら
brew install actionlint、Windowsはwinget・Scoop・Chocolatey、CI上では公式のダウンロードスクリプトかDockerイメージrhysd/actionlintが手軽です。 - shellcheckが入っている環境でだけ
run:のシェルスクリプトも検査されます。入っていなくてもエラーにはならず、黙って省略されます。 - 誤検知や不要な警告は、
-ignoreオプション(正規表現)か.github/actionlint.yamlのpaths.<glob>.ignoreで除外します。セルフホストランナーのラベルもこのファイルに登録します。 - v1.7.12は2026年4月以降にGitHubが追加した構文を知りません。
cache-mode・concurrency.queue・background steps・uses: $/・ubuntu-26.04は正しく書いてもエラーになるので、該当ファイルに限ったignoreで逃がします。 - 検査対象はワークフローファイルです。composite actionの
stepsは検査されず、action.ymlを単体で引数に渡すこともできません。権限の最小化やSHA固定といったセキュリティ方針の強制は守備範囲外で、ghalintかzizmorを併用します。
以下、検出内容の実例から見ていきます。
actionlintが検出するエラーの実例
次のワークフローには、よくある誤りを8つ仕込んであります。GitHubにpushしてもYAMLとしては読み込めてしまうものが大半で、実行して初めて失敗するか、失敗すらせず意図と違う動きをします。
name: CI
on:
push:
branch: main # 正しくは branches
pull_request:
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Build
id: build
run: echo "version=1.0" >> $GITHUB_OUTPUT
- name: Comment title
run: echo "${{ github.event.pull_request.title }}"
- name: Show version
run: echo "${{ steps.bulid.outputs.version }}" # build の打ち間違い
- name: Show platform
run: echo "${{ matrix.platform }}" # matrix に platform は無い
deploy:
needs: tset # test の打ち間違い
runs-on: linux-gpu
steps:
- run: echo deploy
actionlint v1.7.12(shellcheck 0.11.0導入済み)で-onelineを付けて実行した結果です。8件すべてが行・列つきで報告され、終了コードは1でした。以下の出力では、長いメッセージの一部を「…」で省略しています。
$ actionlint -oneline
.github/workflows/ci.yaml:4:5: unexpected key "branch" for "push" section. expected one of "branches", "branches-ignore", "paths", "paths-ignore", "tags", "tags-ignore", "types", "workflows" [syntax-check]
.github/workflows/ci.yaml:13:15: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue [action]
.github/workflows/ci.yaml:16:9: shellcheck reported issue in this script: SC2086:info:1:23: Double quote to prevent globbing and word splitting [shellcheck]
.github/workflows/ci.yaml:18:24: "github.event.pull_request.title" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. ... [expression]
.github/workflows/ci.yaml:20:24: property "bulid" is not defined in object type {build: {conclusion: string; outcome: string; outputs: {string => string}}} [expression]
.github/workflows/ci.yaml:22:24: property "platform" is not defined in object type {os: string} [expression]
.github/workflows/ci.yaml:23:3: job "deploy" needs job "tset" which does not exist in this workflow [job-needs]
.github/workflows/ci.yaml:25:14: label "linux-gpu" is unknown. available labels are "windows-latest", ... [runner-label]
行末の角括弧はルール名(kind)です。出力から読み取れる検査の種類を整理すると次のとおりです。
| ルール名 | 検出したもの | 放置した場合 |
|---|---|---|
| syntax-check | 存在しないキー(branch) | フィルタが効かず全ブランチで起動 |
| action | ランタイムが古いアクション | 実行時に非推奨警告・将来停止 |
| shellcheck | 変数の未引用(SC2086) | 空白を含む値で引数が割れる |
| expression | 信頼できない入力の直接埋め込み | PRタイトル経由のコマンド注入 |
| expression | steps・matrixの未定義プロパティ | 空文字に評価され気づけない |
| job-needs | 存在しないjobへのneeds | ワークフロー全体が起動しない |
| runner-label | 未知のランナーラベル | ジョブが割り当て待ちのまま止まる |
特に効くのはsteps.bulidとmatrix.platformの2件です。GitHub Actionsは存在しないプロパティを空文字として評価するため、実行してもエラーにならず、空のバージョン番号がそのまま後続に流れます。actionlintはid: buildの出力やmatrixの定義から型を組み立てて照合するので、この種の「静かな失敗」を実行前に拾えます。
このほか、cron式の妥当性(v1.7.12でtimezoneのIANA名検査が追加)、workflow_dispatchの入力数(v1.7.10で上限を25へ変更)、permissionsの値、Reusable Workflowのinputs・secretsの型、if: trueのような定数条件、YAMLアンカーの未使用なども検査対象です。全ルールはactionlint v1.7.12の公式検査一覧(docs/checks.md)に掲載されています。
8件を直した修正後のワークフロー
上の指摘を1件ずつ直したものが次のワークフローです。PRタイトルはenv:で環境変数に渡してから"$TITLE"で参照し、$GITHUB_OUTPUTは二重引用符で囲みます。linux-gpuはGitHubホステッドランナーのラベルに置き換えました(セルフホストランナーのラベルとして使い続けるなら、後述のactionlint.yamlに登録します)。
name: CI
on:
push:
branches: main
pull_request:
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- name: Build
id: build
run: echo "version=1.0" >> "$GITHUB_OUTPUT"
- name: Comment title
env:
TITLE: ${{ github.event.pull_request.title }}
run: echo "$TITLE"
- name: Show version
run: echo "${{ steps.build.outputs.version }}"
- name: Show platform
run: echo "${{ matrix.os }}"
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- run: echo deploy
同じv1.7.12で再実行すると出力は0行、終了コードは0でした。actions/checkoutの最新メジャーは2026年10月時点でv7(v7.0.1、2026年7月20日リリース)です。
actionlintの検査対象外とローカルアクションの検査範囲
公式ドキュメントに明記されている対象外が2つあります。1つはcomposite actionの中身で、docs/checks.mdに「steps in Composite action’s metadata is not checked at this point」とあります。もう1つはaction.ymlの単体検査で、ワークフローからuses: ./pathで参照されたローカルアクションのメタデータ(name・description・runsの必須キーやruns.usingの値)は検査されますが、「it is not supported to specify action.yml directly via command line arguments」とされています。アクションを自作して配布するリポジトリでは、actionlintだけでは中身を守れません。
また、アクションをタグでなくコミットSHAで固定しているか、jobごとにpermissionsを絞っているかといった運用方針は検査しません。構文と型が正しくても安全とは限らないので、この領域は後述のghalint・zizmorに任せます。
actionlintのインストール方法
公式のインストール手順(docs/install.md)に載っている入手経路をOS別にまとめました。どれで入れても中身は同じ単一バイナリです。
| 環境 | コマンド |
|---|---|
| macOS(Homebrew公式formula) | brew install actionlint |
| Windows(winget) | winget install actionlint |
| Windows(Scoop) | scoop install actionlint |
| Windows(Chocolatey) | choco install actionlint |
| Arch Linux | pacman -S actionlint |
| Nix(NixOS以外) | nix-env -iA nixpkgs.actionlint |
| mise | mise use -g actionlint@latest |
| Docker | docker run --rm rhysd/actionlint:latest -version |
上記のパッケージマネージャーが使えないLinux環境では、リリースページのビルド済みバイナリか、次のダウンロードスクリプトを使います。スクリプトは最新版をカレントディレクトリに置き、第1引数でバージョン、第2引数で配置先を指定できます。
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) latest /usr/local/bin
actionlint -version
Goのツールチェーンがあればgo install github.com/rhysd/actionlint/cmd/actionlint@latestでもビルドできます。サポートされるGoは直近2系列(v1.7.12時点で1.25と1.26)です。
Homebrewのformulaとcaskの違い
brew install actionlintで入るのはHomebrew本体が管理する公式formulaです。rhysd/actionlintリポジトリは別に独自のcaskも提供しており、リリースと同時に自動更新されます。以前あったリポジトリ独自のformulaはv1.7.11で廃止されたので、rhysd/actionlintをtapしてformulaで入れていた場合は、公式formulaかcaskへ切り替えます(公式formulaには影響ありません)。
brew tap "rhysd/actionlint" "https://github.com/rhysd/actionlint"
brew install --cask actionlint
caskのバイナリは署名されていないため、macOSが警告を出してゴミ箱へ移そうとします。使う場合は「システム設定」の「プライバシーとセキュリティ」で実行を許可する必要があり、特にこだわりが無ければ公式formulaの方が手間はかかりません。
ダウンロードしたバイナリの真正性確認
v1.7.11以降のリリース成果物にはArtifact attestationが付いており、GitHub CLIで改ざんされていないかを検証できます。CIでバイナリを取得する構成なら、検証の1行を足しておく価値があります。
gh release download --repo rhysd/actionlint --pattern '*_linux_amd64.tar.gz' v1.7.12
gh attestation verify -R rhysd/actionlint actionlint_1.7.12_linux_amd64.tar.gz
actionlintコマンドの使い方と終了コード
基本は3通りです。引数なしならカレントディレクトリから親をたどってGitリポジトリを探し、その.github/workflowsを全部検査します。ファイルを指定すればそれだけを、-を渡せば標準入力を検査します。
actionlint # リポジトリ内の全ワークフロー
actionlint .github/workflows/ci.yaml # ファイル指定
cat ci.yaml | actionlint -stdin-filename ci.yaml - # 標準入力
Gitリポジトリの外で引数なしで実行すると、no project was found in any parent directoriesと出て終了コード3で止まります。git init前のディレクトリで試すときはファイルを明示してください。終了コードの意味は次のとおりです。
| 終了コード | 意味 |
|---|---|
| 0 | 実行成功・問題なし |
| 1 | 実行成功・問題あり |
| 2 | コマンドラインオプションが不正 |
| 3 | その他の致命的エラー |
よく使うコマンドラインオプション
v1.7.12のactionlint -hに並ぶフラグは13個です。日常的に使うものを用途別に絞りました。
| オプション | 用途 |
|---|---|
-oneline |
1エラー1行で出力(grepやCIログ向け) |
-format '{{json .}}' |
JSONなどGoテンプレートで整形 |
-ignore <正規表現> |
一致するエラーを除外(複数指定可) |
-config-file <パス> |
既定以外の場所の設定ファイルを読む |
-init-config |
.github/actionlint.yamlの雛形を生成 |
-shellcheck= |
shellcheck連携を止める(空文字) |
-pyflakes= |
pyflakes連携を止める(空文字) |
-color/-no-color |
色付けの強制・無効化 |
-verbose/-debug |
読み込んだ設定や処理過程を表示 |
-format '{{json .}}'はJSON配列でmessage・filepath・line・column・kind・snippetを返します。公式リポジトリのtestdata/format/sarif_template.txtの内容を-format "$(cat sarif_template.txt)"のように渡せばSARIF形式にもできるので、GitHubのCode scanningへ結果を上げる構成も組めます。
警告を除外する方法:-ignoreとactionlint.yaml
actionlintには「この行だけ無視する」ためのコメント記法がありません。除外はエラーメッセージに対する正規表現で行います。手段は目的別に3つです。
-ignoreオプションによる一時的な警告除外
-ignoreはRE2構文の正規表現で、エラーメッセージに一致したものを落とします。繰り返し指定できます。
actionlint -ignore 'SC2086:' -ignore 'label ".+" is unknown'
冒頭の例にこの2つを付けると、shellcheckのSC2086とランナーラベルの2件が消えて6件になりました。CIのコマンドに書き足すだけで済む一方、ローカル実行やエディタ連携には効かないので、恒常的な除外は次の設定ファイルへ移します。
.github/actionlint.yamlによる恒常的な警告除外
設定ファイルは.github/actionlint.yaml(または.yml)に置き、actionlint -init-configで雛形を生成できます。v1.7.12で設定できるのは3項目だけです。
self-hosted-runner:
labels:
- linux-gpu # セルフホストランナーに付けた独自ラベル
config-variables: # 定義済みの vars.* を列挙すると未定義参照を検出
- DEPLOY_ENV
paths:
.github/workflows/**/*.{yml,yaml}:
ignore:
- 'SC2086:'
.github/workflows/release.yaml:
ignore:
- 'the runner of ".+" action is too old to run on GitHub Actions'
self-hosted-runner.labelsはセルフホストランナーを使うリポジトリでほぼ必須です。登録しないと、独自ラベルがすべてlabel "..." is unknownとして報告されます(globパターンも書けます)。config-variablesは既定のnullだと検査なし、配列を書くとそこに無いvars.*をエラーにし、空配列ならvarsの使用自体を禁止します。pathsのキーはリポジトリルートからの相対パスに対するglobで、区切りは常に/です。
shellcheckの警告抑制と外部ツール連携の無効化
shellcheck由来の指摘は、run:のスクリプト内に# shellcheck disable=SC2086と書くとそのスクリプトだけ抑えられます(v1.7.12とshellcheck 0.11.0で確認)。全体で特定ルールを外すなら環境変数SHELLCHECK_OPTS='--exclude=SC2086'を使います。shellcheck連携そのものを止めるには-shellcheck=(空文字)を渡します。外部プロセスを大量に起動しなくなるため、実行時間も短くなります。pyflakes連携(shell: pythonのスクリプト検査)も同様に-pyflakes=で止められます。
v1.7.12が未対応のGitHub Actions新構文と誤検知の回避
actionlintはワークフローのキーやランナーラベルの一覧を本体に埋め込んでいるため、GitHub側の追加にはリリースで追随します。ところがv1.7.12(2026年3月30日)の後、2026年10月17日時点まで新しいリリースは出ていません。その間にGitHubのワークフロー構文リファレンスには次の構文が加わり、正しく書いてもv1.7.12はエラーを返します。公式の記述どおりに書いたワークフローを、v1.7.12と、mainブランチをgo install ...@mainでビルドした版(v1.7.13-0.20260419144658-011a6d15e749)の両方で検査しました。
| 新しい構文 | 公式に載った時期 | v1.7.12 | mainビルド |
|---|---|---|---|
services.<id>.entrypoint・command |
2026年4月 | unexpected key | エラーなし |
concurrency.queue |
2026年5月 | unexpected key | unexpected key |
background・wait・wait-all・cancel・parallel |
2026年6月 | unexpected key 等 | 同左 |
uses: $/...(自リポジトリ参照) |
2026年7月 | ref is missing | ref is missing |
cache-mode(workflow・job) |
2026年9月 | unexpected key | unexpected key |
runs-on: ubuntu-26.04・-arm |
2026年9月17日GA | label is unknown | label is unknown |
mainで直っているのはservicesのentrypoint・command(PR #645)だけです。queueはPR #654、$/はPR #732が2026年10月17日時点で審査中のまま、ubuntu-26.04はissue #682が未対応です。background系の4キーワード(wait・wait-all・cancel・parallel)はstep must run script with "run" section or run action with "uses" sectionとして、background: trueはunexpected key "background"として報告されます。
この中で影響が大きいのはランナーラベルです。GitHubは2026年9月17日のChangelogでUbuntu 26.04ランナーの一般提供と、ubuntu-latestを2026年10月19日から11月19日にかけて段階的に26.04へ切り替えると告知しました。ubuntu-latestのままなら actionlint は困りませんが、版を固定するためにubuntu-26.04と書いた時点でCIが落ちます。なおubuntu-latestの切り替えでランナーのshellcheckも0.9.0(Ubuntu 24.04イメージ)から0.11.0(26.04イメージ)に上がるため、CI上のactionlintの検出内容が変わることがあります(actions/runner-imagesのUbuntu 26.04イメージ一覧で確認)。
新構文の誤検知をファイル単位で除外する設定
上の表の構文をすべて使ったdeploy.yamlは、v1.7.12で13件のエラーになりました。次の設定を置くと、その13件はすべて消えます。ランナーラベルはself-hosted-runner.labelsに足すのが正規の逃がし方です。
self-hosted-runner:
labels:
- ubuntu-26.04
- ubuntu-26.04-arm
paths:
.github/workflows/deploy.yaml: # 新構文を使っているファイルだけに限定する
ignore:
- 'unexpected key "(cache-mode|queue|background|entrypoint|command)"'
- 'step must run script with "run" section'
- 'specifying action "\$/'
pathsのキーを**/*.{yml,yaml}にして全ファイルへ広げるのは避けてください。step must run script with "run" sectionは、runもusesも書き忘れた本物のミスと同じメッセージです。同じリポジトリにrunを書き忘れたステップを持つ別ファイルを置いて検査すると、そちらのエラーだけが残り、終了コードは1でした。除外対象外のファイルでは本物のミスを引き続き検出できます。一方、除外対象のdeploy.yaml内では、runやusesの書き忘れも同じ正規表現に一致して見逃されるため、該当ステップの目視確認が必要です。新しいリリースが出たら、このignoreを消して再実行し、残るエラーが無いかを確かめます。
見慣れないunexpected keyが出たら、除外する前にGitHubのワークフロー構文リファレンスにそのキーがあるかを確かめてください。リファレンスで見つからない場合は、GitHub公式Changelogや対象環境の対応状況も確認し、正式な構文と確認できるまでは除外しないでください。過去の版でも、v1.7.11でcase()関数とmacos-26-large・windows-2025-vs2026ラベル、v1.7.12でmacos-26-intelに対応したように、GitHub側の追加はリリース単位で取り込まれてきました。逆にmacOS 13ランナーのように提供が終わったラベルは、v1.7.10からエラー扱いです。
GitHub ActionsのCIでactionlintを動かす方法
CIへの組み込み方は4通りあり、違いはバイナリの入手方法と結果の見せ方です。
| 方法 | 結果の表示 | 向いている場面 |
|---|---|---|
| 公式ダウンロードスクリプト | ログ(Problem Matcher併用で注釈) | 依存を増やしたくない |
| Dockerイメージ | ログ | shellcheck・pyflakesも同梱で使いたい |
| reviewdog/action-actionlint | 既定はChecks、設定でPR行コメント | レビューで指摘を見せたい |
| raven-actions/actionlint | PRの注釈・ジョブ出力 | キャッシュやWindows・macOSでも動かしたい |
公式スクリプトによるactionlintの取得とCI実行
公式ドキュメントが推奨する形です。スクリプトはダウンロードした実行ファイルの絶対パスをexecutable出力に書くので、次のステップから参照できます。
name: Lint GitHub Actions workflows
on: [push, pull_request]
jobs:
actionlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Download actionlint
id: get_actionlint
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
shell: bash
- name: Check workflow files
run: ${{ steps.get_actionlint.outputs.executable }} -color
shell: bash
shell: bashを明示しているのは、Windowsランナーの既定シェルがpwshで、<( )のプロセス置換が通らないためです。shellcheckはUbuntuランナーにプリインストールされているので、ubuntu-latestならshellcheck連携も有効になります。結果をファイル差分上に注釈として出したい場合は、公式のactionlint-matcher.jsonを.github/に置き、実行前にecho "::add-matcher::.github/actionlint-matcher.json"を1行入れます。
この例はmainブランチのスクリプトを毎回取りに行くので、再現性を重視するなら、ダウンロードURLのmainを確認済みのコミットSHAに置き換え、さらに第1引数へ1.7.12を渡して取得するバイナリの版も固定します。actions/checkoutなどの参照をタグでなくコミットSHAで固定する方法はSHA Pinningの解説にまとめています。
公式DockerイメージによるactionlintのCI実行
公式イメージrhysd/actionlintにはshellcheckとpyflakesが同梱されています。uses: docker://で直接呼べます。
- name: Check workflow files
uses: docker://rhysd/actionlint:1.7.12
with:
args: -color
タグはlatestとバージョン番号(1.7.12など)が用意されています。Dockerコンテナのアクションが動くのはLinuxランナーだけなので、macOS・Windowsランナーのjobでは使えません。
reviewdog・raven-actionsによる検査結果の表示
reviewdog/action-actionlintは公式ドキュメントにも統合例として載っており、on: [pull_request]のジョブにuses: reviewdog/action-actionlint@v1を追加すると、既定のreporter(github-pr-check)でChecksに結果が出ます。reviewdog/action-actionlintのREADMEは、必要な権限としてジョブのpermissions:にcontents: read・checks: write・issues: write・pull-requests: writeの4つを挙げています。PRの該当行にコメントとして付けるには、with:でreporter: github-pr-reviewを指定します。forkからのPRではトークンの書き込み権限が制限されるため、同じ投稿動作にはなりません。検出時にジョブも失敗させる場合はfail_level: errorを指定します。
raven-actions/actionlintはランナーのOS上でネイティブに動くアクションで、actionlintのバイナリをActionsのキャッシュに保存し、Problem Matcherでの注釈も既定で有効です。flags入力で-ignoreなどを渡せ、fail-on-error: falseにすると失敗させずにtotal-errorsなどの出力だけ受け取れます。
- name: actionlint
uses: raven-actions/actionlint@v2
with:
flags: "-ignore SC2086"
どちらもサードパーティ製アクションなので、組織の方針でMarketplaceアクションの利用を絞っている場合は、公式スクリプトかDockerイメージを選ぶ方が審査を通しやすくなります。ワークフローの基本構文から確認したい場合はGitHub Actionsの概要と使い方が入口になります。
actionlintをマージの必須チェックにする設定
CIで動かしただけでは、actionlintが失敗してもPRはマージできてしまいます。止めるには、リポジトリの「Settings」→「Rules」→「Rulesets」でブランチ用ルールセットを作り、対象ブランチを指定してEnforcement statusをActiveに設定し、「Require status checks to pass before merging」にactionlintのジョブ名(上の例ならactionlint)を追加します。GitHubのルールセットの説明どおり、有効にすると必須チェックの条件を満たすまでマージが制限されます。ただし、チェックの結論がskippedやneutralでも条件を満たすため、検査が実際に実行されたことまで保証する設定ではありません。
ここでon.pull_request.pathsに.github/workflows/**を指定して「ワークフローを変えたPRだけ検査する」形にすると、ワークフローに触れないPRが永久に止まります。GitHubの必須チェックのトラブルシューティングには、パスやブランチのフィルタでワークフローごとスキップされた場合、必須チェックは「Pending」のままマージを妨げると書かれています。actionlintは数秒で終わるので、必須にするジョブにはパスの絞り込みを付けない方が運用は単純です。CI/CD全体の設計から相談したい場合は、DevOps・CI/CD導入支援も用意しています。
pre-commitとエディタによるコミット前の検査
CIで落ちてから直すより、コミット前に止める方が往復が減ります。pre-commitには公式フックがあり、.pre-commit-config.yamlに次を書きます。
repos:
- repo: https://github.com/rhysd/actionlint
rev: v1.7.12
hooks:
- id: actionlint
設定ファイルを保存したら、pre-commit導入済みの環境でpre-commit installを実行し、Gitのコミットフックを登録します。既存ファイルをまとめて検査するにはpre-commit run --all-filesを実行します。
フックIDは3種類あり、actionlintの入手方法が違います。
| フックID | actionlintの入手方法 |
|---|---|
| actionlint | Goツールチェーンで隔離環境にビルド |
| actionlint-docker | 公式Dockerイメージを取得 |
| actionlint-system | 手動でインストール済みのものを使用 |
既定のactionlintフックはpre-commit 3.0.0以降ならGo未導入でも自動取得して動作します。入手方法をチームで明示的に統一する場合はactionlint-dockerか、各自がbrew等で入れる前提のactionlint-systemが扱いやすいです。
エディタでは、VS Code拡張(vscode-actionlint)が.github/workflowsを検出して編集中にエラーを表示します。Neovimはnvim-lint、VimとNeovimはALE、EmacsはFlycheck・Flymake用のパッケージが対応しています。インストールせずに試すだけなら、ブラウザ上のプレイグラウンド(rhysd.github.io/actionlint/)が使えます。WebAssemblyで動き、入力内容は外部へ送信されません。
ghalint・zizmorとの違いと併用の判断
「github actions lint」で比較されるツールは3つありますが、検査している層が違うので、どれか1つを選ぶものではありません。
| ツール | 主な対象 | 代表的な検査 | 最新版 |
|---|---|---|---|
| actionlint | 構文・式の型・シェル | 未定義プロパティ、needs誤り | v1.7.12 |
| ghalint | 組織のセキュリティ方針 | job単位のpermissions必須、SHA固定 | v1.5.6 |
| zizmor | 攻撃経路の監査 | template-injection、cache-poisoning | v1.31.1 |
ghalint(作者はsuzuki-shunsuke氏)は「全jobにpermissionsを書く」「read-all・write-allを使わない」「secrets: inheritを使わない」「アクションの参照はフル長のコミットSHAにする」「timeout-minutesを必須にする」「actions/checkoutのpersist-credentialsはfalseにする」といったポリシーを強制します。ghalint run-actionでaction.ymlも検査でき、actionlintが見ない領域を補えます。zizmorはtemplate-injection・dangerous-triggers・excessive-permissions・unpinned-uses・cache-poisoningなど、v1.31.1(2026年10月15日)の監査項目一覧で41項目を持つセキュリティ特化の解析ツールです。
actionlintは、GitHub Actionsのワークフローを管理するリポジトリの基本検査として導入を勧めます。設定ファイルなしで導入でき、この記事の例のように未定義プロパティやjob参照の誤りを実行前に検出できるからです。そのうえで、パブリックリポジトリやpull_request_targetを使うリポジトリにはzizmorを、社内で「permissionsとSHA固定を全リポジトリに強制したい」という方針があるならghalintを足します。逆に、actionlintのスクリプトインジェクション検査だけで供給網攻撃への備えができたと考えるのは誤りです。actionlintのインジェクション検査は、PRタイトルなどの信頼できない入力をrun:やactions/github-scriptのscript入力へ埋め込むパターンを対象としますが、アクションの参照先が書き換えられるリスクやトークン権限の過剰は対象外です。
なお、複数のリンターを束ねるsuper-linterもactionlintを内蔵しています。super-linter経由で動かす場合、actionlint.yamlの置き場所はsuper-linter側の規約に従うため、.github/に置いても読まれないことがあります。
actionlint導入時の誤検知・未検出への対処
shellcheckの未検出時に確認する導入状況
actionlintは実行環境にshellcheckコマンドがあるときだけ連携します。見つからなくてもエラーや警告は出ないため、ローカルでは指摘が出ずCI(Ubuntuランナーはプリインストール済み)では出る、という差が生まれます。brew install shellcheckなどで開発機にも入れるか、同梱済みのDockerイメージで実行をそろえます。
macOSでactionlintが終了しない場合のshellcheck連携の停止
Apple Silicon(darwin/arm64)のMacで、actionlintがCPUを使い続けたまま終わらないことがあります。issue #650によると、run:のステップが2つ以上あり、スクリプトの合計が1KB前後を超えると、shellcheckへスクリプトを渡す処理がデッドロックします。修正PR #651の説明では、公式バイナリのv1.7.10・v1.7.11・v1.7.12で再現しています。修正は2026年4月19日にmainへ取り込まれましたが、リリースはまだです。当面は-shellcheck=でshellcheck連携を止めるか、Dockerイメージ(Linux上で動く)で実行するか、go install github.com/rhysd/actionlint/cmd/actionlint@mainで修正入りの版をビルドします。
Windows・Linux混在matrixでのpwsh誤検知とシェル指定
runs-on: ${{ matrix.os }}のようにOSを静的に決められないjobでは、actionlintはシェルを楽観的にbashとみなします。if: matrix.os == 'windows-latest'で守ったPowerShellのスクリプトもshellcheckにかけられ、パス区切りなどで誤検知が出ます。そのステップにshell: pwshを明示すれば、shellcheckの対象から外れます。shellcheckがかかるのはbashとshだけです。
Reusable Workflowのローカル参照と外部参照の検査範囲
workflow_callで呼び出すワークフローがuses: ./.github/workflows/xxx.ymlのようなローカル参照なら、actionlintは呼び出し側のwith・secretsと呼び出される側のinputs・secrets・outputsの定義を突き合わせます。公式ドキュメントにあるとおり、この照合は./で始まるローカル参照でしか働かず、別リポジトリの再利用ワークフローは中身を読めません。Reusable Workflowsの設計を外部リポジトリに切り出す場合は、呼び出される側のリポジトリにもactionlintを入れておきます。
actionlintに関するよくある質問
actionlintとは何ですか?
GitHub Actionsのワークフローファイルを実行前に検査する、Go製・MITライセンスの静的解析ツールです。YAMLの構文、${{ }}式の型、job間の依存、ランナーラベル、shellcheckによるシェルスクリプト検査、スクリプトインジェクションの検出を1コマンドで行います。
actionlintをbrewでインストールするには?
brew install actionlintでHomebrewの公式formulaが入ります。rhysd/actionlintのcaskもbrew install --cask actionlintで入りますが、署名されていないバイナリのためmacOSのセキュリティ設定で実行を許可する必要があります。
actionlintで特定の警告を無視するには?
エラーメッセージに一致する正規表現を-ignoreオプションで渡すか、.github/actionlint.yamlのpaths.<glob>.ignoreに書きます。行単位で無視するコメント記法はありません。shellcheck由来の指摘は、スクリプト内の# shellcheck disable=SCxxxxでも抑えられます。
actionlintとghalintの違いは何ですか?
actionlintは構文・型・参照の誤りを見つけるツールで、ghalintは「全jobにpermissionsを書く」「アクションはコミットSHAで固定する」といったセキュリティ方針を強制するツールです。構文検査に加えて権限やSHA固定の方針も強制したい場合に、併用します。
actionlintでaction.ymlやcomposite actionは検査できますか?
ワークフローからuses: ./pathで参照されたローカルアクションのaction.ymlは、必須キーやruns.usingの値が検査されます。ただしaction.ymlを引数で直接渡すことはできず、composite actionのstepsの中身も検査対象外です。