Add lint configuration and Gitea Actions CI

Configure yamllint and ansible-lint, plus a workflow running yamllint,
ansible-lint and ansible-playbook --syntax-check over every playbook.

ansible-lint uses the moderate profile: on the current code it reports
exactly the same violations as basic, so it costs nothing today while
holding a higher bar for new code. skip_list is empty; noisy legacy
rules go to warn_list with a comment on why and when to restore them.
Correctness and safety rules stay fatal.

Two constraints are encoded in the workflow: syntax-check must run from
ansible/ because roles_path is relative, and ansible-lint needs absolute
ANSIBLE_ROLES_PATH/ANSIBLE_COLLECTIONS_PATH when run from the root.

The runner is not registered yet; registration notes are in the workflow.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTocXkGUUazHdKKd3r9k71
This commit is contained in:
Dmitry
2026-08-26 22:09:59 +03:00
co-authored by Claude Opus 5
parent b953909e0a
commit a7b0635830
3 changed files with 288 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
---
# ansible-lint для HomeLab infras.
#
# Profile: moderate.
# - `production` / `safety` дали бы сотни нарушений на текущем коде (fqcn,
# jinja[spacing], no-handler, key-order, галактические метаданные ролей) —
# линтер стал бы шумом, который все игнорируют.
# - Фактическая проверка: на этом репозитории `basic` и `moderate` дают
# ровно один и тот же набор нарушений (все сработавшие правила помечены
# profile:basic). То есть `moderate` сегодня ничего не стоит, но держит
# планку выше для нового кода. Отсюда выбор.
# - Правила корректности и безопасности (syntax-check, risky-file-permissions,
# risky-shell-pipe, risky-octal, no-changed-when, no-free-form, deprecated-*,
# jinja[invalid], sanity) НЕ отключены — они остаются fatal.
profile: moderate
exclude_paths:
- archive/ # исторические NixOS/docker-compose/ansible конфиги, read-only
- ansible/.venv/ # gitignored, локальное venv
- ansible/collections/ # gitignored, установленные galaxy-коллекции
- ansible/generated/ # gitignored артефакты
- .opencode/ # конфиги агентов + node_modules
- tools/ # tools/grimmory-mcp — JS, не ansible
- node_modules/
- .direnv/
- .git/
# Зашифрованный ansible-vault: линтер не может его расшифровать и шумит
# предупреждениями о Decryption failed.
- ansible/inventory/host_vars/gyro/vault.yml
# Явно указываем, что считать плейбуками/тасками — иначе ansible-lint
# принимает inventory/*.yml и roles/*/files/*.yml за плейбуки.
kinds:
- playbook: 'ansible/playbooks/*.yml'
- tasks: 'ansible/tasks/*.yml'
# ------------------------------------------------------------------
# warn_list — правила, которые СЕЙЧАС массово срабатывают на легаси-коде.
# Они видны в выводе как warning, но не роняют CI. Это осознанный
# «нулевой baseline»: CI зелёный, долг виден.
# По каждому пункту — почему и стоит ли возвращать в fatal.
# ------------------------------------------------------------------
warn_list:
# 40 срабатываний. Роли используют осмысленные кросс-ролевые префиксы
# (openvpn_*, monitoring_*, emergency_*), а не имя роли. Переименование
# затронет inventory, host_vars и все плейбуки разом.
# ВЕРНУТЬ В FATAL: после разового переименования переменных ролей.
- var-naming[no-role-prefix]
# 19 срабатываний. Имена задач в нижнем регистре ("restart gitea lxc").
# Чисто косметика, на поведение не влияет.
# ВЕРНУТЬ В FATAL: после массового причёсывания имён (дешёвый разовый PR).
- name[casing]
# 18 срабатываний. Часть файлов без "---" в начале.
# ВЕРНУТЬ В FATAL: тривиально чинится, но затрагивает 18 файлов.
- yaml[document-start]
# 6 срабатываний. Все — верификационные команды с changed_when: false
# (curl для проверки HTTPS-эндпоинта, systemctl is-active, git config
# внутри чужого чекаута, docker exec caddy validate). Замена на
# uri/systemd/git-модули здесь не улучшает код, а иногда невозможна
# (команда исполняется внутри pct/docker exec).
# ВЕРНУТЬ В FATAL: вряд ли — правило по сути false-positive для этого стиля.
- command-instead-of-module
# 5 срабатываний: безымянные `- import_playbook:` записи в *-update.yml.
# 1 срабатывание: безымянный `- block:` в ru-vps-mihomo-harden.yml.
# Влияет только на читаемость вывода ansible-playbook.
# ВЕРНУТЬ В FATAL: да, после того как проставят name (мелкий PR).
- name[play]
- name[missing]
# 1 срабатывание: emergency_access/tasks/client.yml:46 — become_user без
# become. Это, вероятно, НАСТОЯЩИЙ баг (ключ создаётся не тем пользователем),
# но чинить его — задача не линтера. Держим в warn_list, чтобы CI не был
# красным с первого дня; ВЕРНУТЬ В FATAL сразу после фикса.
- partial-become
# skip_list пуст намеренно: ничего не отключаем полностью, всё либо fatal,
# либо видимый warning.
skip_list: []
use_default_rules: true