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
+134
View File
@@ -0,0 +1,134 @@
---
# Статические проверки Ansible-кода HomeLab infras.
#
# ГДЕ ЭТО ДОЛЖНО ВЫПОЛНЯТЬСЯ
# --------------------------
# Gitea живёт на LXC `gitea` (192.168.1.25). Gitea Actions по умолчанию
# ВЫКЛЮЧЕНЫ и не имеют ни одного раннера — этот workflow не запустится,
# пока раннер не зарегистрирован ОТДЕЛЬНО, вручную:
#
# 1. Включить Actions в Gitea:
# app.ini -> [actions] ENABLED = true
# и в настройках репозитория: Settings -> Actions -> Enable.
#
# 2. Поднять act_runner. Подходящий хост — LXC `docker-test`
# (192.168.1.29): там уже есть Docker, а сборка контейнеров раннера
# не мешает проду. Ставить раннер на сам LXC `gitea` не стоит —
# CI-нагрузка не должна валить git-сервис.
#
# 3. Зарегистрировать раннер (на docker-test):
# act_runner register --no-interactive \
# --instance http://192.168.1.25:3000 \
# --token <RUNNER_TOKEN из Gitea Settings -> Actions -> Runners> \
# --name docker-test-runner \
# --labels ubuntu-latest:docker://catthehacker/ubuntu:act-latest
#
# Метка `ubuntu-latest` обязательна — именно её просит `runs-on` ниже.
#
# 4. Раннеру нужен исходящий интернет (PyPI + Ansible Galaxy).
# На docker-test трафик может идти через mihomo/OpenVPN — проверить,
# что pip и galaxy резолвятся, иначе шаг установки упадёт.
#
# Регистрация раннера НЕ автоматизирована этим репозиторием: она требует
# одноразового токена из веб-интерфейса Gitea.
#
# Локально те же проверки воспроизводятся через nix:
# nix develop -c yamllint .
# nix develop -c ansible-lint
# nix develop -c sh -c 'cd ansible && for f in playbooks/*.yml; do ansible-playbook --syntax-check "$f"; done'
name: lint
on:
push:
pull_request:
jobs:
lint:
name: yamllint + ansible-lint + syntax-check
runs-on: ubuntu-latest
env:
# ansible.cfg лежит в ansible/ и использует ОТНОСИТЕЛЬНЫЕ пути
# (roles_path = roles). Из корня репозитория он не работает, поэтому
# пути задаются абсолютно через окружение. Без этого ansible-lint
# выдаёт 12 ложных syntax-check[specific] «role not found».
ANSIBLE_ROLES_PATH: ${{ github.workspace }}/ansible/roles
ANSIBLE_COLLECTIONS_PATH: ${{ github.workspace }}/ansible/collections
ANSIBLE_INVENTORY: ${{ github.workspace }}/ansible/inventory/hosts.yml
# Ansible шумит депрекейшенами ядра — в CI они не наши.
ANSIBLE_DEPRECATION_WARNINGS: "false"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
steps:
- name: Checkout
uses: actions/checkout@v4
# Образ catthehacker/ubuntu:act-latest уже несёт python3, но не всегда
# python3-venv. Ставим явно, чтобы шаг не был хрупким.
- name: Ensure python3 + venv
run: |
set -eux
if ! command -v python3 >/dev/null 2>&1 || ! python3 -m venv --help >/dev/null 2>&1; then
apt-get update
apt-get install -y --no-install-recommends python3 python3-venv python3-pip
fi
python3 --version
- name: Install ansible-core, ansible-lint, yamllint
run: |
set -eux
python3 -m venv /tmp/lintenv
. /tmp/lintenv/bin/activate
python3 -m pip install --upgrade pip
# ansible-core/proxmoxer/requests берём из репозитория, чтобы CI и
# локальное окружение не разъезжались.
python3 -m pip install -r ansible/requirements.txt
# Линтеры пинуем: обновление ansible-lint регулярно добавляет новые
# правила и красит CI без единого коммита в инфраструктуру.
python3 -m pip install 'ansible-lint==25.8.2' 'yamllint==1.37.1'
echo "/tmp/lintenv/bin" >> "$GITHUB_PATH"
- name: Install Galaxy collections
run: |
set -eux
ansible-galaxy collection install \
-r ansible/requirements.yml \
-p ansible/collections
- name: Versions
run: |
set -eux
ansible --version | head -n1
ansible-lint --version
yamllint --version
# Конфиг в /.yamllint. Падает только на ошибках (табы, дубли ключей,
# битый YAML); стилевые замечания идут как warning и CI не роняют.
- name: yamllint
run: yamllint -f standard .
# Конфиг в /.ansible-lint, profile: moderate.
- name: ansible-lint
run: ansible-lint
# syntax-check запускается ИЗ ansible/, иначе ansible.cfg с
# относительными roles_path не подхватывается и 13 плейбуков падают
# с «role not found». Переменные Proxmox (PROXMOX_*) для syntax-check
# НЕ нужны: `lookup('env', ...)` на этапе парсинга не вычисляется,
# проверено — все 37 плейбуков проходят с пустым окружением.
- name: ansible-playbook --syntax-check (все плейбуки)
working-directory: ansible
run: |
set -u
rc=0
for f in playbooks/*.yml; do
if ansible-playbook --syntax-check "$f" >/tmp/sc.log 2>&1; then
echo "ok $f"
else
rc=1
echo "FAIL $f"
sed 's/^/ /' /tmp/sc.log
fi
done
exit "$rc"