← Весь справочник

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-needsneeds на несуществующую задачу или циклическая зависимость
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