Skip to content

fix(ci): list the files to check on the host, not in the container (#190) - #191

Merged
Gerrrt merged 1 commit into
mainfrom
gerrrt/lint-worktree-git-listing
Aug 31, 2026
Merged

fix(ci): list the files to check on the host, not in the container (#190)#191
Gerrrt merged 1 commit into
mainfrom
gerrrt/lint-worktree-git-listing

Conversation

@Gerrrt

@Gerrrt Gerrrt commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Closes #190.

make validate failed on the monitoring host and passed in CI — the wrong way
round for a script whose header claims "Everything CI runs, runnable locally".

stacks/observability/alertmanager/.rendered/heartbeat_url:
	Wrong line endings or no final newline
...
4 errors found
  FAIL editorconfig-checker

It was not a missing exclude

scripts/lint.sh:111-119 already says those files should be skipped, and says
how — editorconfig-checker lists files with git ls-files, so gitignored trees
are untracked in both a workstation and a CI checkout and both runs see the same
files. The image is even chosen to make that work: it installs git and marks
/check a safe.directory.

The reasoning is right. The mechanism was not running.

Where it broke

In a git worktree, .git is a file holding an absolute path to a gitdir
outside the bind mount:

$ docker run --rm -v "$PWD:/check" -w /check --entrypoint sh <image> -c 'git ls-files'
fatal: not a git repository: (null)

The checker then silently falls back to walking the filesystem and picks up
every gitignored tree it finds — backups/, certificates/, .venv/,
.rendered/. A normal checkout is unaffected, which is why this only ever
showed up in the agent worktrees on this host.

The silent substitution is the real defect: nothing reports that the file set
changed, the check just starts examining something else. Same family as #62 and
#63.

Mounting the real gitdir does not help — the worktree's gitdir holds a
back-pointer to the worktree's own .git path, which inside the container is
/check, and git rejects the mismatch. Tested.

Fix

Build the list on the host, where git resolves a worktree correctly, and pass it
in. The checker now gets the same tracked files whether it runs from the pinned
image or a local binary, and cannot substitute a different set without saying
so.

An empty list fails rather than skips — a skip means the linter was
unreachable; this means it was reachable with no trustworthy answer to what it
should check, which is exactly the state the fallback used to paper over.

Two details the fallback was hiding:

  • -z, because a tracked path may contain a space.
  • an existence test, because a tracked-but-deleted file made the checker panic
    with a Go stack trace
    rather than report anything.

Verified on the deploy host, after make up, with the rendered files present

check result
reproduced first on a clean main 4 errors found / FAIL
make validate after the fix green end to end
same file set as CI 114 both ways — host git ls-files, and the container's own listing in a normal checkout
still catches real defects trailing space added to scripts/snmp-verify.sh303: Trailing whitespace, FAIL
tracked-but-deleted file PASS, no panic (was a Go stack trace)

Not the same as #175

#175 is validate running fewer checks than CI. This is validate running one CI
cannot fail. Both are #68's family, but separate changes in separate files.

🤖 Generated with Claude Code

`make validate` failed on the monitoring host and passed in CI, which is the
wrong way round for a script whose first line claims "everything CI runs,
runnable locally".

editorconfig-checker is the only linter in the list that decides for itself
what to look at: given no arguments it runs `git ls-files`. The comment in
runner_for() says that is what makes a local run equal a CI one, because the
gitignored backups/, certificates/, .venv/ and .rendered/ trees are untracked
either way. That reasoning is right and the mechanism was not working.

Inside the container the call fails, and fails silently. A git worktree's .git
is a file holding an absolute path to a gitdir outside the bind mount, so git
answers "not a git repository: (null)" and the checker falls back to walking
the filesystem. It then flagged the four decrypted Alertmanager webhook URLs
that render-config.sh writes without a trailing newline — files that are
gitignored, that CI never creates because it never renders, and that no amount
of reading .gitignore would have excluded, because nothing was consulting it.

Mounting the real gitdir does not fix it: the worktree's gitdir holds a
back-pointer to the worktree's own .git path, which inside the container is
/check, and git rejects the mismatch. So the listing moves to the host, where
git resolves a worktree correctly. The checker is now handed the same tracked
files whether it runs from the pinned image or a local binary, and cannot
substitute a different set without saying so. An empty list fails rather than
skips: a skip means the linter was unreachable, and this means it was reachable
with no trustworthy answer to what it should check — the state the fallback
used to paper over.

Two details the fallback was hiding. -z, because a tracked path may contain a
space. And an existence test, because a tracked file deleted from the working
tree makes the checker panic with a Go stack trace rather than report anything;
it now passes.

Verified on the deploy host after `make up`, with the rendered files present:
`make validate` green end to end, the file set identical to the 114 the
container's own listing produces in a normal checkout, a trailing space added
to scripts/snmp-verify.sh still caught, and a deleted README.md no longer a
panic.

Adjacent to #175, which is the opposite asymmetry — validate running fewer
checks than CI rather than one CI cannot fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Gerrrt
Gerrrt merged commit a923109 into main Aug 31, 2026
3 checks passed
@Gerrrt
Gerrrt deleted the gerrrt/lint-worktree-git-listing branch August 31, 2026 01:47
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.

make validate fails on the deploy host and passes in CI — editorconfig-checker is not listing through git

1 participant