Skip to content

Add pre-commit hooks, Makefile, dev container, agent guide and CI - #198

Merged
mainman94 merged 4 commits into
mainfrom
claude/precommit-hooks-devenv-yutvof
Sep 5, 2026
Merged

Add pre-commit hooks, Makefile, dev container, agent guide and CI#198
mainman94 merged 4 commits into
mainfrom
claude/precommit-hooks-devenv-yutvof

Conversation

@mainman94

Copy link
Copy Markdown
Owner

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.
  • Makefilemake help lists everything; make check is 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-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 that stack's .env.example. ${VAR:-default} is optional by construction and exempt.

paperless-ngx and pocket-id are 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.env holds the Backblaze credentials and was never gitignored. Now is.
  • CDPATH=CDPATH='' in check-backup-paths.sh — identical to the shell, no longer trips shellcheck SC1007.
  • .yamlfmt uses scan_folded_as_literal so the folded command: blocks keep one flag per line. It 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.

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-unfixed for the same reason. make scan-strict exits 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-files passes on the full tree; all 30 compose files validate and all 29 stacks pass the convention check.
  • Both hooks were negative-tested against a deliberately broken stack.
  • Workflows are actionlint-clean.
  • Not run here: the trivy sweep — the sandbox proxy blocks the Trivy DB. Worth a manual workflow_dispatch before 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

mainman94 and others added 4 commits September 5, 2026 14:03
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
@mainman94
mainman94 merged commit 88b6975 into main Sep 5, 2026
2 checks passed
@mainman94
mainman94 deleted the claude/precommit-hooks-devenv-yutvof branch September 5, 2026 15:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant