cd.yml template guard is defeated by placeholder substitution: every new repo skips check/build/deploy while green #10

Closed
opened 2026-09-03 07:58:58 +00:00 by mathias · 1 comment
Owner

Risk: LOW (CI config)

Symptom (mathias/openbanking-api, born 2026-09-03 via gitea-mcp create_project_from_template): run 5277 reported success, but jobs Lint / Test / Vet, Build & Import, Deploy via GitOps were all skipped. No image ever reached the registry; the pod sat in ImagePullBackOff.

Cause: .gitea/workflows/cd.yml guard step reads

if grep -q '__MODULE_PATH__' go.mod; then

Both scaffolders (gitea-mcp's create_project_from_template and local-dev's scripts/new-project.sh) substitute __MODULE_PATH__ in every file, so the generated repo's guard becomes grep -q 'git.d-ma.be/mathias/<name>' go.mod — which matches forever. The comment above the step describes exactly this failure (harg-bygglov-go#8), but the placeholder in the grep is itself substituted.

Fix in the template: split the token so the shell string is unchanged but the substitution engine has nothing to match:

if grep -q '__MODULE''_PATH__' go.mod; then

Same for any other guard that greps __PROJECT_NAME__. Verified in openbanking-api commit "ci: make the template guard immune to placeholder substitution".

Also: audit repos generated since the guard was introduced — any of them with an all-skipped green run has never actually been built by CI.

**Risk:** LOW (CI config) **Symptom** (mathias/openbanking-api, born 2026-09-03 via gitea-mcp `create_project_from_template`): run 5277 reported `success`, but jobs `Lint / Test / Vet`, `Build & Import`, `Deploy via GitOps` were all `skipped`. No image ever reached the registry; the pod sat in ImagePullBackOff. **Cause:** `.gitea/workflows/cd.yml` guard step reads ``` if grep -q '__MODULE_PATH__' go.mod; then ``` Both scaffolders (gitea-mcp's create_project_from_template and local-dev's `scripts/new-project.sh`) substitute `__MODULE_PATH__` in **every** file, so the generated repo's guard becomes `grep -q 'git.d-ma.be/mathias/<name>' go.mod` — which matches forever. The comment above the step describes exactly this failure (harg-bygglov-go#8), but the placeholder in the grep is itself substituted. **Fix in the template:** split the token so the shell string is unchanged but the substitution engine has nothing to match: ``` if grep -q '__MODULE''_PATH__' go.mod; then ``` Same for any other guard that greps `__PROJECT_NAME__`. Verified in openbanking-api commit "ci: make the template guard immune to placeholder substitution". **Also:** audit repos generated since the guard was introduced — any of them with an all-skipped green run has never actually been built by CI.
mathias added the risk/highsize/xstype/fix labels 2026-09-16 22:24:20 +00:00
Author
Owner

Fixed in b9bbbae (PR #11, v0.2.0), verified 2026-09-17. Auto-closed by the merge; evidence here.

Same root cause as #9 and fixed by the same commit — an explicitly tied pair rather than two separate fixes.

Your diagnosis was exact, including the detail that made this one worse than the first: the broken line sat directly under a comment describing the failure mode. That is the part that decided the shape of the fix. A guard living inline in a workflow step can only be reviewed by reading, so the comment was doing the job a test should have been doing — and a comment cannot fail.

So the detection moved to scripts/is-template.sh with scripts/tests/is-template_test.sh, which substitutes a copy of the guard and asserts it still answers correctly. Both shipped versions fail that test.

One deviation from your proposed fix. You suggested splitting the token so the substitution engine has nothing to match:

if grep -q '__MODULE''_PATH__' go.mod; then

That works, but it is invisible: it reads like a typo and the next person tidying the file closes the bug again. Shipped the regex form instead — it matches the shape of a placeholder, so there is no complete token to rewrite, and it additionally catches leftovers the scaffolders know nothing about. A half-substituted tree is not a working repo either.

Verified against a fully substituted child rather than by inspection:

guard in child     -> false   (CI runs)
guard in template  -> true    (template still skips its own build)
suite, both states -> 8 passed, 0 failed

Your closing note — audit repos generated since the guard was introduced — is not covered by this and is the part with live consequences: any of them showing an all-skipped green run has never been built by CI. openbanking-api is known; the rest are not. Not filed as its own issue yet because the right scope is an estate-wide sweep rather than a template change, and that belongs with whoever picks up the next backlog pass.


Created via git-mcp on behalf of @mathias

**Fixed in `b9bbbae` (PR #11, v0.2.0), verified 2026-09-17.** Auto-closed by the merge; evidence here. Same root cause as #9 and fixed by the same commit — an explicitly tied pair rather than two separate fixes. Your diagnosis was exact, including the detail that made this one worse than the first: the broken line sat **directly under a comment describing the failure mode**. That is the part that decided the shape of the fix. A guard living inline in a workflow step can only be reviewed by reading, so the comment was doing the job a test should have been doing — and a comment cannot fail. So the detection moved to `scripts/is-template.sh` with `scripts/tests/is-template_test.sh`, which substitutes a copy of the guard and asserts it still answers correctly. Both shipped versions fail that test. **One deviation from your proposed fix.** You suggested splitting the token so the substitution engine has nothing to match: ``` if grep -q '__MODULE''_PATH__' go.mod; then ``` That works, but it is invisible: it reads like a typo and the next person tidying the file closes the bug again. Shipped the regex form instead — it matches the *shape* of a placeholder, so there is no complete token to rewrite, and it additionally catches leftovers the scaffolders know nothing about. A half-substituted tree is not a working repo either. Verified against a fully substituted child rather than by inspection: ``` guard in child -> false (CI runs) guard in template -> true (template still skips its own build) suite, both states -> 8 passed, 0 failed ``` Your closing note — **audit repos generated since the guard was introduced** — is not covered by this and is the part with live consequences: any of them showing an all-skipped green run has never been built by CI. `openbanking-api` is known; the rest are not. Not filed as its own issue yet because the right scope is an estate-wide sweep rather than a template change, and that belongs with whoever picks up the next backlog pass. --- _Created via git-mcp on behalf of @mathias_
Sign in to join this conversation.