Add pre-commit hooks, Makefile, dev container, agent guide and CI - #198
Merged
Conversation
Local checks for a repo that has no CI deploy: a mistake in a compose file
is otherwise only found when a stack is brought up on the host.
Two hooks are repo-specific:
- compose-config runs `docker compose config` per stack. The real .env and
backup.env are host-only, so each stack is validated in a scratch copy
holding just the compose file and its env files, with `<name>.example`
materialised as `<name>`. Nothing is written into the worktree.
- stack-conventions checks the AGENTS.md rules that are cheap to verify
statically: the default network is named <stack>_network, and every
mandatory ${VAR} has a key in the stack's .env.example. ${VAR:-default}
is optional by construction and exempt. paperless-ngx and pocket-id are
grandfathered on the network rule, since renaming a live network restarts
everything attached to it.
The rest is standard hygiene plus yamlfmt, shellcheck, shfmt and gitleaks.
.yamlfmt matches multi-k8s-infra and adds scan_folded_as_literal so the
folded `command:` blocks keep one flag per line; the config reformats
nothing in the current tree. Application config under <stack>/config/ is
excluded from yamlfmt and check-yaml — it follows each app's own format,
and recyclarr's uses a custom !env_var tag.
Also:
- gitignore infrastructure/volume-backup/backup.env, which holds the
Backblaze credentials and was never ignored.
- CDPATH= -> CDPATH='' in check-backup-paths.sh, identical to the shell but
no longer trips shellcheck SC1007.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0134YCw5jnsz4cXPAPN5rHhN
This repo had no CI at all, so the hooks only ran for whoever remembered to install them and nothing ever looked at the images the stacks actually run. ci.yml runs every pre-commit hook on each PR. The runner has a Docker daemon, so compose-config really validates there rather than skipping. scan.yml sweeps all 47 referenced images with trivy — weekly, on demand, and when a compose file changes — and writes a per-image table to the run summary. `make scan` runs the same script locally; `make images` just lists them. The sweep is advisory on purpose. These are other people's images and a new upstream CVE is not something a commit here can fix; a red build nobody can clear is a build people stop reading. --ignore-unfixed for the same reason. `make scan-strict` exits non-zero on fixable CRITICALs if a gate is ever wanted. Image enumeration is a grep: the compose files never interpolate the image field, so it needs neither docker nor the host-only .env files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134YCw5jnsz4cXPAPN5rHhN
Follows the What / Why / Checklist shape docker-strapi already uses, so the six repos read the same way. The checklist is the repo's actual failure modes, not generic hygiene: a new stack that forgets its .env.example or its backup service, and backup wiring that was never checked against real host paths — which only `make backup-check` on the deploy host can do. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134YCw5jnsz4cXPAPN5rHhN
The sweep never ran: Unable to resolve action `aquasecurity/setup-trivy@v0.2.3`, unable to find version `v0.2.3` That version does not exist — I guessed it. Uses Aqua's own install script instead, pinned to v0.74.0, the trivy version the portfolio repo's image scan already runs successfully, and prints `trivy --version` so a future install problem shows up as itself rather than as a scan failure. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0134YCw5jnsz4cXPAPN5rHhN
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This repo has no CI deploy — stacks come up on the host — so a mistake in a compose file was previously only caught when something failed to start. This adds the local and CI checks that catch it earlier.
What's in it
.pre-commit-config.yaml— hygiene,yamlfmt,shellcheck,shfmt,gitleaks, plus two repo-specific hooks.Makefile—make helplists everything;make checkis the gate..devcontainer/— Docker, Python, trivy, jq and the hook toolchain.AGENTS.md— existing conventions kept verbatim, plus workflow and scanning sections..github/workflows/ci.yml— every hook on each PR..github/workflows/scan.yml— weekly trivy sweep of all 47 referenced images.The two repo-specific hooks
compose-configrunsdocker compose configper stack. The real.envandbackup.envare host-only, so each stack is validated in a scratch copy holding just the compose file and its env files, with<name>.examplematerialised as<name>. Nothing is written into the worktree.stack-conventionschecks the AGENTS.md rules that are cheap to verify statically: the default network is named<stack>_network, and every mandatory${VAR}has a key in that stack's.env.example.${VAR:-default}is optional by construction and exempt.paperless-ngxandpocket-idare grandfathered on the network rule — renaming a live network restarts everything attached to it, which is a deploy decision, not a lint fix. Worth a look if you'd rather do the rename.Worth reviewing
infrastructure/volume-backup/backup.envholds the Backblaze credentials and was never gitignored. Now is.CDPATH=→CDPATH=''incheck-backup-paths.sh— identical to the shell, no longer trips shellcheck SC1007..yamlfmtusesscan_folded_as_literalso the foldedcommand:blocks keep one flag per line. It reformats nothing in the current tree.<stack>/config/is excluded fromyamlfmtandcheck-yaml— it follows each app's own format, and recyclarr's uses a custom!env_vartag.The image sweep is advisory on purpose
These are other people's images; a new upstream CVE isn't something a commit here can fix, and a red build nobody can clear is a build people stop reading.
--ignore-unfixedfor the same reason.make scan-strictexits non-zero on fixable CRITICALs if you ever want a gate.Renovate already moves the digest pins, so the usual fix for a finding is to let it bump the image.
Test plan
pre-commit run --all-filespasses on the full tree; all 30 compose files validate and all 29 stacks pass the convention check.actionlint-clean.workflow_dispatchbefore trusting the weekly schedule; 47 third-party images is where the backlog is most likely to be non-trivial.🤖 Generated with Claude Code
https://claude.ai/code/session_0134YCw5jnsz4cXPAPN5rHhN
Generated by Claude Code