actionlint
Линтер workflow GitHub Actions — синтаксис, выражения ${{ }}, needs, метки раннеров, shellcheck для run
actionlint проверяет файлы .github/workflows/*.yml до пуша: неизвестные ключи и события, опечатки в needs, ошибки в выражениях ${{ }}, устаревшие версии экшенов, опасную подстановку недоверенных данных в скрипты. Скрипты из run: он дополнительно прогоняет через shellcheck (и Python-скрипты — через pyflakes), если те установлены.
Синтаксис
actionlint [опции] # все workflow в ближайшем .github/workflows
actionlint [опции] файл… # конкретные файлы
actionlint [опции] - # workflow из stdin
Без файлов actionlint ищет корень репозитория (каталог с .git) вверх от текущего и проверяет все *.yml/*.yaml в его .github/workflows.
Опции
| Опция | Что делает |
|---|---|
-shellcheck=путь | какую команду shellcheck использовать; -shellcheck= (пусто) — отключить проверку run: через shellcheck |
-pyflakes=путь | то же для pyflakes (шаги с shell: python); -pyflakes= — отключить |
-ignore 'regex' | не показывать ошибки, текст которых совпал с регулярным выражением; можно повторять |
-oneline | одна строка на ошибку, без фрагмента кода — удобно для grep и скриптов |
-format 'шаблон' | свой формат вывода (Go-шаблон); '{{json .}}' — JSON |
-no-color / -color | выключить / принудительно включить цвет |
-config-file файл | путь к конфигу (по умолчанию .github/actionlint.yaml) |
-init-config | создать заготовку .github/actionlint.yaml |
-stdin-filename имя | имя файла в сообщениях при чтении из stdin |
-verbose / -debug | подробности: какие правила включены, что найдено |
-version | версия |
Коды выхода
| Код | Значение |
|---|---|
0 | ошибок нет |
1 | найдены ошибки в workflow |
2 | неверные опции командной строки |
3 | фатальная ошибка: не найден репозиторий, неверный шаблон -format |
Как читать вывод
.github/workflows/ci.yml:10:3: job "test" needs job "biuld" which does not exist in this workflow [job-needs]
|
10 | test:
| ^~~~~
файл:строка:столбец: сообщение [правило], ниже — фрагмент с указателем. Частые правила:
| Правило | О чём |
|---|---|
syntax-check | неизвестный или пропущенный ключ (step вместо steps), YAML не разбирается |
events | неизвестное событие в on: (pull вместо pull_request), неверный фильтр |
job-needs | needs на несуществующую задачу или циклическая зависимость |
expression | ошибка в ${{ }}: несуществующее свойство (matrix.os без матрицы, steps.x без шага с таким id), неверный тип, недоверенный ввод в run: |
action | неверные with: у известного экшена, слишком старая версия (actions/setup-node@v1) |
runner-label | неизвестная метка в runs-on (для своих раннеров — в конфиге) |
shellcheck | замечание shellcheck к скрипту в run: с кодом SCxxxx |
pyflakes | замечание к Python-скрипту |
Конфиг .github/actionlint.yaml
self-hosted-runner:
labels: [my-runner, gpu] # свои метки для runs-on
config-variables: null # null — не проверять имена vars.*
paths:
.github/workflows/**/*.yml:
ignore: ['SC2086'] # игнорировать сообщения по шаблону для этих файлов
Примеры
mkdir -p .github/workflows && git init -q
cat > .github/workflows/ci.yml <<'EOF'
name: CI
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "hello"
EOF
# проверить все workflow репозитория
actionlint
# конкретный файл (работает и вне репозитория)
actionlint .github/workflows/ci.yml
# без shellcheck и pyflakes — только сама структура workflow
actionlint -shellcheck= -pyflakes= .github/workflows/ci.yml
# коротко, по строке на ошибку; посчитать ошибки
actionlint -oneline | wc -l
# заглушить конкретные замечания
actionlint -ignore 'SC2086' -ignore 'potentially untrusted'
# свой формат: файл, строка, правило
actionlint -format '{{range $e := .}}{{$e.Filepath}}:{{$e.Line}} [{{$e.Kind}}]\n{{end}}'
# JSON для обработки jq
actionlint -format '{{json .}}' | jq length
# workflow из stdin с нормальным именем в сообщениях
cat .github/workflows/ci.yml | actionlint -stdin-filename ci.yml -
Подводные камни
no project was found in any parent directories(код3) — запуск без аргументов вне Git-репозитория. Сделайgit initили передай файлы явно.shellcheckдолжен быть установлен, иначе правило молча отключается. То же сpyflakes: в песочнице IzzyLab его нет, и-verboseпокажетRule "pyflakes" was disabled. Явный-shellcheck=/-pyflakes=отключает их намеренно.- Ошибки shellcheck приходят как одна строка с кодом вида
SC2086:info:1:6: Double quote to prevent globbing…. Строка и столбец после кода — внутри скриптаrun:, а не файла workflow. ${{ github.event.* }}прямо вrun:— это не стиль, а уязвимость. Заголовок PR подставится в shell как есть. Передавай такие значения черезenv:и обращайся как к"$TITLE".- Свои раннеры.
runs-on: my-runnerдастlabel "my-runner" is unknown. Перечисли метки вself-hosted-runner.labelsконфига, а не глуши ошибку через-ignore. -formatи перевод строки.actionlintсам заменяет\nв шаблоне на перевод строки, поэтому пиши\nпрямо в тексте шаблона:…{{$e.Line}}\n{{end}}. Привычное для Go{{"\n"}}здесь ломается сunterminated quoted stringи кодом3.- Синтаксис ≠ работоспособность. Секреты, переменные окружения раннера и права токена
actionlintне проверяет — workflow может пройти линтер и упасть на GitHub.
См. также
shellcheck · github-actions · yamlschema · kubeconform · yq · jq