diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..bc09bd5 --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,28 @@ +--- +# Start narrow. Widen the profile once the existing playbooks pass it. +profile: basic + +exclude_paths: + - .ansible/ + - .scratch/ + # These roles include postgres/roles/attach with a path built from + # playbook_dir. ansible-lint resolves it against the role directory instead + # and reports a load failure for a file that does not exist. Exclude the thin + # wrapper files rather than the real attach role. + - "*/roles/postgres/tasks/main.yaml" + - "*/roles/valkey/tasks/main.yaml" + +# Legacy debt that needs a repo-wide rename or a module rewrite. These report +# without failing CI. Remove an entry when its last finding is fixed. +skip_list: + - var-naming[no-role-prefix] + - name[play] + - command-instead-of-module + - yaml + +warn_list: + - experimental + - literal-compare + - no-free-form + - role-name + - command-instead-of-shell diff --git a/.gitea/workflows/check.yml b/.gitea/workflows/check.yml new file mode 100644 index 0000000..258c39d --- /dev/null +++ b/.gitea/workflows/check.yml @@ -0,0 +1,18 @@ +name: check + +on: + push: + pull_request: + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install linters + run: pip install ansible-core ansible-lint yamllint + - name: Run checks + run: ./scripts/check.sh diff --git a/.gitignore b/.gitignore index 4f5eb82..b33229b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,8 @@ # Provider bootstrap private key for prepare-server. Never commit this. bootstrap.pem + +# Local check output and scratch files. +.ansible/ +.scratch/ +*.log diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..76fa5ce --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +--- +# Local hook, same checks as CI. Enable with: +# pre-commit install +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + args: [--allow-multiple-documents] + - id: check-added-large-files + - repo: https://github.com/adrienverge/yamllint + rev: v1.35.1 + hooks: + - id: yamllint + - repo: https://github.com/ansible/ansible-lint + rev: v24.7.0 + hooks: + - id: ansible-lint diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..2b100ff --- /dev/null +++ b/.yamllint @@ -0,0 +1,25 @@ +--- +extends: default + +ignore: | + .ansible/ + .scratch/ + .git/ + +rules: + line-length: + max: 200 + allow-non-breakable-words: true + comments: + min-spaces-from-content: 1 + comments-indentation: false + document-start: disable + indentation: + spaces: 2 + indent-sequences: consistent + level: warning + braces: + level: warning + truthy: + allowed-values: ["true", "false"] + check-keys: false diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..25fd12e --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,20 @@ +[defaults] +# The fleet inventory committed next to this file. +inventory = hosts + +# Ansible writes per-run temp files under $HOME by default. The agent sandbox +# and some CI images mount $HOME read-only, which makes every run fail before +# it parses a playbook. Keep the temp tree inside the checkout instead. +local_tmp = ./.ansible/tmp + +# Keep Galaxy collections local too, so CI and the check script do not depend on +# a writable $HOME. +collections_path = ./.ansible/collections + +# .retry files are noise now that every playbook is checked in CI. +retry_files_enabled = False + +[galaxy] +# Galaxy caches API responses under $HOME by default, which breaks the same +# read-only sandbox. +cache_dir = ./.ansible/galaxy_cache diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..d9ebfb2 --- /dev/null +++ b/requirements.yml @@ -0,0 +1,7 @@ +--- +# Collections the playbooks import. CI and scripts/check.sh install these into +# .ansible/collections so ansible-lint can resolve every module. +collections: + - name: community.docker + - name: community.general + - name: ansible.posix diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 0000000..1749d3d --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Run every check this repo has. CI and pre-commit call the same script. +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")/.." + +# Ansible and uv both want a writable cache. Keep them in the checkout so the +# script works when $HOME is read-only, which is the case in the agent sandbox. +export ANSIBLE_CONFIG="${ANSIBLE_CONFIG:-$PWD/ansible.cfg}" +export ANSIBLE_HOME="${ANSIBLE_HOME:-$PWD/.ansible/home}" +export ANSIBLE_LOCAL_TEMP="${ANSIBLE_LOCAL_TEMP:-$PWD/.ansible/tmp}" +export UV_CACHE_DIR="${UV_CACHE_DIR:-$PWD/.ansible/uv-cache}" +export UV_TOOL_DIR="${UV_TOOL_DIR:-$PWD/.ansible/uv-tools}" +export ANSIBLE_GALAXY_CACHE_DIR="${ANSIBLE_GALAXY_CACHE_DIR:-$PWD/.ansible/galaxy_cache}" +mkdir -p "$ANSIBLE_LOCAL_TEMP" "$UV_CACHE_DIR" "$UV_TOOL_DIR" "$ANSIBLE_GALAXY_CACHE_DIR" "$ANSIBLE_HOME" + +if [ -f requirements.yml ]; then + echo "== ansible-galaxy collection install ==" + ansible-galaxy collection install -r requirements.yml +fi + +run() { + local tool="$1" + shift + if command -v "$tool" >/dev/null 2>&1; then + "$tool" "$@" + else + uvx "$tool" "$@" + fi +} + +echo "== yamllint ==" +run yamllint . + +echo "== ansible-lint ==" +run ansible-lint + +echo "== ansible-playbook --syntax-check ==" +shopt -s nullglob +for playbook in */playbook.yml; do + echo "-- $playbook" + ansible-playbook --syntax-check "$playbook" +done