Commit Graph
12 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5.5 6d44e1e28a fix(ci): check the PR head against the main it was tested with
The Validation step "Assert the base branch is fully merged into the PR
head" ran main_merged.sh, which fetches the live tip of main when the job
starts. PR runs wait in the queue for hours while image-update PRs land
on main several times an hour, so nearly every queued run failed
Validation on start and had to be re-pushed after another main merge
(PR #1265: behind_by=0 at push, behind_by=9 while its run still queued).

main_merged.sh takes a new INFINITO_MAIN_COMMIT override: when set, it
fetches that full commit SHA from the remote and requires HEAD to
contain it instead of the live tip; a set but malformed or empty value
exits 2. The Validation step passes github.event.pull_request.base.sha,
the main tip at event time, which the step's pull_request-only guard
always provides. The run tests the merge against that base, so the gate
still guarantees the tested tree contains it.

The local pre-push hook (make test-main-merged) leaves the override
unset and keeps checking the live tip: a push happens right now, so
lagging the current main is the real defect there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:10:45 +01:00
kevinveenbirkenbach f2e9ce04d4 fix(git-hooks): run the merge-signed gate on prepare-commit-msg
git writes MERGE_HEAD only after the pre-merge-commit hook returns, so the
gate never found it and aborted every non-fast-forward merge with "No merge
in progress"; git then wrote the merge state via abort_commit(), leaving a
half-finished merge that git status reported as "still merging". The gate
had therefore never verified a single signature.

git also skips pre-merge-commit entirely once a merge conflicts, so the
conflict-resolution commit was unguarded as well. prepare-commit-msg fires
with MERGE_HEAD present on both paths and no-ops on ordinary commits.

Verified in a scratch repo with a real pre-commit install: ordinary commit
passes silently, clean automerge and conflict-resolution commit both block
an unsigned incoming commit, and the extra stash/restore cycle round-trips
unstaged changes byte-identically.

pre_commit install never removes hook types dropped from the config, so the
install script now also uninstalls the stale pre-merge-commit hook.
2026-07-28 17:01:24 +02:00
kevinveenbirkenbachandClaude Fable 5 40e553069d refactor(structure): nest prefix-grouped sibling files into subfolders
Move files that share a delimiter-bounded name prefix within a folder (threshold 2) into a subfolder named after the prefix, dropping the prefix from the filename. Covers plugins/filter (docker, generate, has, merge, get/all), env handler subpackages, utils/roles/entity, utils/tests/swarm/write, role SQL/scripts/templates, keycloak/mailu task-includes, shared tasks/utils/load, and CI scripts. Update every import, query_file/script/include_tasks/src/lookup target, compose bind mount, and python -m path to the new locations.

Extend the prefix-grouped-files lint to threshold 2 with dir-level .nocheck and file-head nocheck opt-outs, and skip groups whose shortest member's stem equals the shared prefix. Keep flat only where a loader forbids nesting: WordPress mu-plugins and the docs/requirements namespace. Ansible filter discovery is recursive, so nested filters stay Jinja-resolvable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 20:00:03 +02:00
kevinveenbirkenbachandClaude Opus 4.8 bc047de312 feat(git): gate merges on signed incoming commits
A merge commit bypassed every signature check: test-signed only inspects
HEAD and the pre-commit hook does not run for merge commits. Add
scripts/git/assert_merge_signed.sh, which fails when any commit the merge
brings in (HEAD..MERGE_HEAD) reports %G? status N, wire it up as
make test-merge-signed, and register it as a pre-merge-commit hook so
pre-commit install now also installs that hook type.

Fast-forward merges create no commit and skip the hook (use --no-ff to
gate them); conflicted merges fall back to the regular pre-commit and
pre-push gates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 03:26:12 +02:00
kevinveenbirkenbachandClaude Opus 4.8 0d84f30ed5 fix(web-app-erpnext): satisfy make quality after the swarm fix
Follow-up to the swarm CID/redis fix that make quality flagged:
- Extract the site-bootstrap sequence into 02b_create_site.yml (renamed from
  02b_restart_frappe.yml; now new-site + restart + re-resolve + wait) and include
  it with when: rc \!= 0, so the oversized when-block disappears and the sub-file
  is not parsed at all when the site already exists.
- The backend entrypoint now execs the image's real /usr/local/bin/entrypoint.sh
  after the config-ready gate, so the sites/assets symlink that entrypoint creates
  is preserved instead of relying on a sibling replica to recreate it.
- Drop the now-dead ERPNEXT_BACKEND_CONTAINER_ADDRESS var, shorten two over-long
  OIDC restart task names, and remove stale narration comments.

Also suppress the INFINITO_MAIN_REMOTE/BRANCH env lints in the git pre-push helper:
they are maintainer tooling knobs, not deployment keys.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 02:13:51 +02:00
kevinveenbirkenbachandClaude Opus 4.8 e070bb2d8d ci: fail when upstream main is not merged into the branch
Adds scripts/git/assert_main_merged.sh, which fetches the upstream main
tip and fails unless it is fully merged into HEAD. Wired in two places:

- pre-push: a new make-test-main-merged pre-commit hook (pre-push stage)
  runs 'make test-main-merged' before every push.
- pull request: a new assert-main-merged job checks out the real PR head
  (not the merge ref) and asserts the base branch is merged into it, so a
  PR whose branch lags main goes red.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 01:47:22 +02:00
kevinveenbirkenbachandClaude Opus 4.8 0ec8f41772 chore(make): add quality + autoformat-restage targets
quality runs autoformat then the full test suite in one shot as a
pre-commit gate.

autoformat-restage runs autoformat and re-stages files it rewrote that
were already staged, but only when the working tree held no unstaged
tracked changes beforehand -- so a partially-staged hunk is never swept
into the index. Logic lives in scripts/git/autoformat_restage.sh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 01:11:20 +02:00
kevinveenbirkenbachandClaude Opus 4.7 16bdbf03d7 docs: fix repo-wide drift against documentation.md style rules
Three classes of drift cleaned up across 85 .md files:

- Em-dashes: replaced ` — ` with the contextually correct separator
  (colon for `term: explanation`, period to split clauses across
  sentences/source lines, parentheses for asides, semicolon for
  tight-linked continuations). Two intentional exceptions remain
  in `docs/contributing/documentation.md` (the rule literal) and
  inside a fenced ```sh block in `docs/contributing/actions/debugging/ci.md`.
- SPOT self-labels: rewrote "This is the SPOT for X" to describe
  the subject directly ("This directory holds X" / "This file
  describes X"). Cross-references to other artefacts' canonical
  role remain unchanged.
- Heading-emoji position: moved leading-emoji headings (`# 🧩 Title`)
  to trailing position (`# Title 🧩`) per the headline rule that
  emojis MUST come after the title text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 20:12:07 +02:00
kevinveenbirkenbach e87bd9d37b chore: adopt git-maintainer-tools as dev dependency for git ops
Drop the project-local copy of scripts/git/sign-push.sh in favour of
the git-maintainer-tools CLIs (git-setup-remotes, git-sign-push),
which now ship as a standalone PyPI/GitHub package so the same helpers
can be reused across every maintainer repo without vendoring shell
scripts.

The package is declared in pyproject.toml's `dev` extras and lands on
$PATH via `make install-python-dev` (which already runs
`pip install -e .[dev]`). Consumers invoke the CLIs directly; there
are no make wrappers because doing so would just duplicate help text
the tool itself already prints.

Fallout:
  * scripts/git/sign-push.sh deleted.
  * Makefile: sign-push target removed (no replacement target).
  * .claude/settings.json: deny rule for `make sign-push*` replaced
    with direct `git-setup-remotes*` / `git-sign-push*` denies so the
    sandbox blocks the real binaries, not just the make alias.
  * CLAUDE.md: operator instruction points at the bare `git-sign-push`
    CLI + `make install-python-dev` for setup.
  * docs/contributing/artefact/git/remotes.md: new tool-focused page.
    Describes the layout and the two CLIs; does not duplicate raw git
    commands already covered by git-maintainer-tools.
  * docs/contributing/artefact/git/README.md: trimmed per readme_md.md
    (docs READMEs must not act as a file index).
  * docs/contributing/tools/make.md: Git section now documents the
    external CLIs and the install entry point, not a wrapper target.
2026-04-24 21:05:40 +02:00
kevinveenbirkenbach 19de80b506 fix(sign-push): sign via rebase -S instead of exec amend
The --exec 'git commit --amend --allow-empty --no-edit -S' pattern
multiplied identical-tree empty commits during a rebase-merges run
(observed: 3 upstream empties became 16 locally). git rebase -S signs
every rewritten commit natively, without the amend loop, while still
preserving merges via --rebase-merges.
2026-04-19 22:16:44 +02:00
kevinveenbirkenbach eb5e87ee28 fix(sign-push): allow amending empty commits during re-sign
The re-sign rebase failed on empty commits pulled in from upstream
because git refused the --amend. Adding --allow-empty keeps those
commits in place while still GPG-signing them.
2026-04-19 22:04:24 +02:00
kevinveenbirkenbachandClaude Opus 4.7 5e75a4b0ad feat(git): add make sign-push and deny direct push in sandbox
Route pushes through scripts/git/sign-push.sh so the operator's GPG key
stays outside the sandbox. Script signs only @{u}..HEAD (or merge-base
with origin/main|master on first push), skips re-signing when all
commits already carry a good signature, and uses --force-with-lease on
rewritten history. settings.json denies git push*, git send-pack*, and
make sign-push* so the agent cannot trigger a push path directly.
CLAUDE.md instructs the agent to hand off to the operator instead.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 12:15:44 +02:00