generated-child harness substitutes the Dockerfile but never validates its cmd/ build path #14

Open
opened 2026-09-17 16:18:08 +00:00 by mathias · 0 comments
Owner

The gap

scripts/tests/generated-child_test.sh substitutes Dockerfile into the throwaway child (added in 2c4158a), but nothing ever executes or parses it. The harness runs templ generate, go list, go build, go vet and golangci-lint — none of which read the Dockerfile. So the build path it references is substituted and then never checked.

Demonstrated on main (2c4158a) by breaking it on purpose:

$ sed -i 's|./cmd/__PROJECT_NAME__|./cmd/does_not_exist|' Dockerfile
$ bash scripts/tests/generated-child_test.sh
  ...
  5 passed, 0 failed        (exit 0)

Dockerfile:8 is:

RUN templ generate && CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o /out/app ./cmd/__PROJECT_NAME__

A wrong path there produces a child repo that passes task check and fails in Build & Import, which in the template is always skipped — so the template can never see it.

Why this is worth closing

This is the estate's "rename leftovers hide in the Dockerfile" footgun, and it is the one file where a stale cmd/ path survives every Go-level gate by construction. template-go-agent#1/#2 were the same shape: a defect in the entrypoint that the repo's own green CI could not reach.

Note the build job calls .gitea/build-image.sh (infra#309), so the real build is not inlined here — but the ./cmd/<name> argument in the Dockerfile still is.

Options

  1. Cheapest, no daemon: assert that the path the Dockerfile builds exists in the substituted child — extract the go build ... ./cmd/X argument and check [ -d "$tmp/cmd/X" ]. Catches the rename-leftover class, runs in milliseconds, no container runtime needed in task check.
  2. Fuller: actually build the image in the child inside CI. More faithful, but it puts a container build in task check, which is a real cost on every local run and needs a runtime that may not be present — and the harness's missing-tool branch is now (correctly) a hard failure, so that would have to be handled deliberately.

Option 1 is probably the right trade; option 2 arguably belongs in the child's own CI rather than the template's gate.

Done when

  • Changing the ./cmd/... path in Dockerfile to something that does not exist makes task check fail, demonstrated by breaking it rather than by reading the script
  • A correct Dockerfile still passes, and the check does not require a container runtime to be installed (or, if it does, its absence is a FAIL with a stated reason — never a skip)
  • The same gap is closed in mathias/template-go-agent, whose harness shares this lineage

Source

Found while gating #13 (which closed #8 and #12). Raised there as a note-only finding — it is not a regression from that PR, which improved matters by substituting the file at all. Related: template-go-agent#1, #2, infra#309.

## The gap `scripts/tests/generated-child_test.sh` substitutes `Dockerfile` into the throwaway child (added in `2c4158a`), but nothing ever *executes* or parses it. The harness runs `templ generate`, `go list`, `go build`, `go vet` and `golangci-lint` — none of which read the Dockerfile. So the build path it references is substituted and then never checked. Demonstrated on `main` (`2c4158a`) by breaking it on purpose: ``` $ sed -i 's|./cmd/__PROJECT_NAME__|./cmd/does_not_exist|' Dockerfile $ bash scripts/tests/generated-child_test.sh ... 5 passed, 0 failed (exit 0) ``` `Dockerfile:8` is: ```dockerfile RUN templ generate && CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o /out/app ./cmd/__PROJECT_NAME__ ``` A wrong path there produces a child repo that passes `task check` and fails in `Build & Import`, which in the template is always skipped — so the template can never see it. ## Why this is worth closing This is the estate's "rename leftovers hide in the Dockerfile" footgun, and it is the one file where a stale `cmd/` path survives every Go-level gate by construction. `template-go-agent#1`/`#2` were the same shape: a defect in the entrypoint that the repo's own green CI could not reach. Note the build job calls `.gitea/build-image.sh` (infra#309), so the real build is not inlined here — but the `./cmd/<name>` argument in the Dockerfile still is. ## Options 1. **Cheapest, no daemon:** assert that the path the Dockerfile builds exists in the substituted child — extract the `go build ... ./cmd/X` argument and check `[ -d "$tmp/cmd/X" ]`. Catches the rename-leftover class, runs in milliseconds, no container runtime needed in `task check`. 2. **Fuller:** actually build the image in the child inside CI. More faithful, but it puts a container build in `task check`, which is a real cost on every local run and needs a runtime that may not be present — and the harness's missing-tool branch is now (correctly) a hard failure, so that would have to be handled deliberately. Option 1 is probably the right trade; option 2 arguably belongs in the child's own CI rather than the template's gate. ## Done when - Changing the `./cmd/...` path in `Dockerfile` to something that does not exist makes `task check` fail, demonstrated by breaking it rather than by reading the script - A correct Dockerfile still passes, and the check does not require a container runtime to be installed (or, if it does, its absence is a FAIL with a stated reason — never a skip) - The same gap is closed in `mathias/template-go-agent`, whose harness shares this lineage ## Source Found while gating #13 (which closed #8 and #12). Raised there as a note-only finding — it is not a regression from that PR, which improved matters by substituting the file at all. Related: `template-go-agent#1`, `#2`, infra#309.
Sign in to join this conversation.