Files
computer-playbook/scripts/lint/autoformat.sh
T
kevinveenbirkenbachandClaude Opus 5 8d45159233 style(lint): let ansible-lint say why it failed
The runner sent both streams to /dev/null and then classified by exit code, so
a failure produced one line -- "FAILED ansible-lint" -- with no code and no
message. A lint finding, a crash and a lock conflict were indistinguishable,
which is exactly the state a quality run left me in while ansible-lint was
clean on the host at the same commit.

Output now goes to a temp file, is discarded on success because ansible-lint is
verbose when it passes, and is written to stderr together with the exit code
when it fails. The accepted codes are untouched, so a passing run behaves
exactly as before. run_ruff already reports this way, which is why the TID252
error it hit was fixable on sight.

This changes no outcome. The failure that motivated it has not recurred, so the
new path is still unexercised against a real fault.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 03:20:01 +02:00

218 lines
5.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# shellcheck shell=bash
# Auto-format all source files using available tools.
# Missing tools are reported and skipped rather than aborting the run.
#
# Per-tool workers run concurrently by default. Set INFINITO_WORKER_ENABLED=0
# (also accepts `false`/`no`/`off`) to fall back to sequential execution.
# Tools that touch the same files are kept on the same worker (the
# shfmt+shellcheck pair both writes to scripts/*.sh, so they share a
# worker) regardless of the parallelism setting.
set -euo pipefail
# Default lives in default.env (SPOT); load.sh exports it.
PARALLEL="${INFINITO_WORKER_ENABLED}"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd -- "${SCRIPT_DIR}/../.." && pwd)"
cd "${REPO_ROOT}"
RUFF_CACHE_DIR="build/ruff-cache-$(id -u)"
export RUFF_CACHE_DIR
# Exception: no trap-delete on EXIT for status_dir -- that would race the
# background workers if the parent shell is interrupted mid-run (the trap
# fires, the dir disappears, an in-flight worker then tries to write its
# status file and crashes with "No such file or directory"). Cleanup happens
# explicitly after `wait` returns, see end of script.
status_dir="$(mktemp -d)"
# $1 = tool name, $2 = OK|SKIP
write_status() {
printf '%s %s\n' "$2" "$1" >"${status_dir}/$1"
}
# ── individual workers ───────────────────────────────────────────────────────
run_ruff() {
if ! command -v ruff &>/dev/null; then
write_status ruff SKIP
return 0
fi
local rc=0
ruff format . || rc=$?
[[ $rc -eq 0 ]] && { ruff check . --fix || rc=$?; }
if [[ $rc -eq 0 ]]; then
write_status ruff OK
else
write_status ruff FAIL
fi
}
run_claude() {
if ! command -v python3 &>/dev/null; then
write_status claude SKIP
return 0
fi
if bash "${SCRIPT_DIR}/claude.sh"; then
write_status claude OK
else
write_status claude FAIL
fi
}
# shfmt + shellcheck both write to scripts/*.sh and therefore share a
# worker. They run sequentially within this function so their edits do
# not race.
run_shell_pair() {
if ! command -v shfmt &>/dev/null; then
write_status shfmt SKIP
elif shfmt -w scripts; then
write_status shfmt OK
else
write_status shfmt FAIL
fi
if command -v shellcheck &>/dev/null; then
mapfile -t shellcheck_files < <(find . -type f -name '*.sh' 2>/dev/null | sort)
if [[ "${#shellcheck_files[@]}" -gt 0 ]]; then
local diff_file rc=0
diff_file="$(mktemp)"
set +e
shellcheck -f diff -x "${shellcheck_files[@]}" >"${diff_file}" 2>/dev/null
set -e
if [[ -s "${diff_file}" ]]; then
printf 'Applying ShellCheck autofixes.\n'
git apply --whitespace=nowarn "${diff_file}" || rc=$?
fi
rm -f "${diff_file}"
if [[ $rc -eq 0 ]]; then
write_status shellcheck OK
else
write_status shellcheck FAIL
fi
else
write_status shellcheck OK
fi
else
write_status shellcheck SKIP
fi
}
run_markdownlint() {
if ! command -v markdownlint-cli2 &>/dev/null; then
write_status markdownlint-cli2 SKIP
return 0
fi
local rc=0
markdownlint-cli2 --fix >/dev/null 2>&1 || rc=$?
if [[ $rc -le 1 ]]; then
write_status markdownlint-cli2 OK
else
write_status markdownlint-cli2 FAIL
fi
}
run_ansible_lint() {
if ! command -v ansible-lint &>/dev/null; then
write_status ansible-lint SKIP
return 0
fi
local rc=0 out
out="$(mktemp)"
ansible-lint --fix >"${out}" 2>&1 || rc=$?
if [[ $rc -eq 0 || $rc -eq 2 || $rc -eq 8 ]]; then
write_status ansible-lint OK
else
printf 'ansible-lint exited %s:\n' "${rc}" >&2
cat "${out}" >&2
write_status ansible-lint FAIL
fi
rm -f "${out}"
}
run_mbake() {
if ! command -v mbake &>/dev/null; then
write_status mbake SKIP
return 0
fi
local rc=0
mbake format Makefile >/dev/null 2>&1 || rc=$?
[[ $rc -eq 0 ]] && { mbake format Makefile >/dev/null 2>&1 || rc=$?; }
if [[ $rc -eq 0 ]]; then
write_status mbake OK
else
write_status mbake FAIL
fi
}
run_eslint() {
if [[ ! -d "node_modules/eslint" ]]; then
write_status eslint SKIP
return 0
fi
local rc=0
npx --no-install eslint --fix 'roles/**/files/**/*.js' >/dev/null 2>&1 || rc=$?
if [[ $rc -le 1 ]]; then
write_status eslint OK
else
write_status eslint FAIL
fi
}
# ── dispatch ─────────────────────────────────────────────────────────────────
is_truthy() {
case "${1:-}" in
1 | true | TRUE | True | yes | YES | Yes | on | ON | On) return 0 ;;
*) return 1 ;;
esac
}
workers=(run_ruff run_claude run_shell_pair run_markdownlint run_ansible_lint run_mbake run_eslint)
if is_truthy "${PARALLEL}"; then
for w in "${workers[@]}"; do "${w}" & done
wait || true # nocheck: shell-or-true -- grandfathered: worked in practice; TODO: sharpen to catch only the exact tolerated error
else
for w in "${workers[@]}"; do "${w}" || true; done # nocheck: shell-or-true -- grandfathered: worked in practice; TODO: sharpen to catch only the exact tolerated error
fi
ran=()
failed=()
skipped=()
for f in "${status_dir}"/*; do
[[ -f "${f}" ]] || continue
line="$(<"${f}")"
case "${line}" in
OK\ *) ran+=("${line#OK }") ;;
FAIL\ *) failed+=("${line#FAIL }") ;;
SKIP\ *) skipped+=("${line#SKIP }") ;;
esac
done
if [[ "${#ran[@]}" -gt 0 ]]; then
printf 'autoformat: ran %s\n' "$(
IFS=', '
echo "${ran[*]}"
)"
fi
if [[ "${#skipped[@]}" -gt 0 ]]; then
printf 'autoformat: skipped (not installed) %s\n' "$(
IFS=', '
echo "${skipped[*]}"
)"
fi
if [[ "${#failed[@]}" -gt 0 ]]; then
printf 'autoformat: FAILED %s\n' "$(
IFS=', '
echo "${failed[*]}"
)" >&2
fi
rm -rf "${status_dir}"
[[ "${#failed[@]}" -eq 0 ]] || exit 1