From 13e713ba54ae180f0c73ed0d0bef2ed2854bd58b Mon Sep 17 00:00:00 2001 From: sneak Date: Sun, 4 Oct 2026 02:54:48 +0000 Subject: [PATCH] Keep each submodule's git config out of the build context (closes #75) The canonical .dockerignore kept out .git/config but not the config in each submodule's git directory under .git/modules/, nested again for a submodule's own submodules, which can hold the same credential. Add .git/modules/**/config and say so in REPO_POLICIES.md and both checklists. The pattern stays under .git/modules/: .git/**/config would also drop a branch or tag named config in the top-level repository, which git describe may need. Model: opus-5-5 --- .dockerignore | 3 ++ TODO.md | 6 ++++ prompts/EXISTING_REPO_CHECKLIST.md | 44 ++++++++++++++++-------------- prompts/NEW_REPO_CHECKLIST.md | 37 +++++++++++++------------ prompts/REPO_POLICIES.md | 31 +++++++++++---------- 5 files changed, 67 insertions(+), 54 deletions(-) diff --git a/.dockerignore b/.dockerignore index 5c135d9..351278b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,7 +17,10 @@ # stage that compiles runs `git describe --tags --always` on .git, which # does not need .git/config; that file can hold a credential, such as a # password in a remote URL or the token the CI checkout step stores there. +# Each submodule keeps a config with the same exposure in its git directory +# under .git/modules/, nested again for a submodule's own submodules. .git/config +.git/modules/**/config # Agent scratch: one full checkout of the repo per in-flight agent. # Anchored because it occurs once where agents run at the repo root. diff --git a/TODO.md b/TODO.md index dda29c2..73a5f93 100644 --- a/TODO.md +++ b/TODO.md @@ -21,6 +21,12 @@ fmt-check, and commit. # Completed Steps +- 2026-10-04: The canonical `.dockerignore` now also keeps out each submodule's + `config` (issue 75). A submodule's git directory lives under `.git/modules/`, + nested again for its own submodules, and its `config` can hold a credential + just like `.git/config`. The pattern `.git/modules/**/config` covers every + depth and leaves the top-level `.git` that `git describe` reads untouched. + `REPO_POLICIES.md` and both checklists say so in the same words. - 2026-10-03: Fixed two defects in the canonical Go `Dockerfile` example (issue 73). The test phase now uses the Debian Go image, since `-race` needs cgo and the alpine image has no C compiler, so the phase failed before running a test. diff --git a/prompts/EXISTING_REPO_CHECKLIST.md b/prompts/EXISTING_REPO_CHECKLIST.md index c92a947..21b00fa 100644 --- a/prompts/EXISTING_REPO_CHECKLIST.md +++ b/prompts/EXISTING_REPO_CHECKLIST.md @@ -1,6 +1,6 @@ --- title: Existing Repo Checklist -last_modified: 2026-10-03 +last_modified: 2026-10-04 --- Use this checklist when beginning work in a repo that may not yet conform to our @@ -59,26 +59,28 @@ with your task. here run anywhere other than the repo root, the anchored entry misses `services/api/.claude/`: add anchored entries for those directories. - [ ] If the repo embeds a version in a binary: `.dockerignore` lets `.git` into - the build context. It keeps out `.git/config`, which `git describe` does - not need and which can hold a credential: a password in a remote URL, or - the token the CI checkout step stores there. The stage that compiles has - `git` (the Debian Go image has it; an alpine one needs - `apk add --no-cache git`) and takes the version from the `VERSION` build - argument when one is given, otherwise from `git describe --tags --always`. - That gives the tag on a tagged commit; on a later commit, the tag, the - number of commits since it and the short commit (`v1.2.3-4-gabc1234`); and - the short commit when no tag is reachable. The stage that compiles also - marks its working directory safe for git - (`git config --system --add safe.directory /src`): a context sent as a tar - stream keeps the sender's file owners, and git refuses a checkout owned by - another user, so the version would come out empty. `ARG VERSION` has no - default, and the build fails if the context carries `.git` and the version - still comes out empty, `dev` or `unknown`. A plain `docker build .` with - no build arguments must succeed; a Dockerfile that refuses an empty build - argument drops that refusal and keeps the argument. `script/docker` and - `script/cibuild` pass the version they compute on the host; it takes - precedence. A tag-derived version additionally needs `fetch-depth: 0` on - the CI checkout step, which clones shallow and fetches no tags by default. + the build context. It keeps out `.git/config` and each submodule's + `config` under `.git/modules/` at any depth (`.git/modules/**/config`), + which `git describe` does not need and which can hold a credential: a + password in a remote URL, or the token the CI checkout step stores there. + The stage that compiles has `git` (the Debian Go image has it; an alpine + one needs `apk add --no-cache git`) and takes the version from the + `VERSION` build argument when one is given, otherwise from + `git describe --tags --always`. That gives the tag on a tagged commit; on + a later commit, the tag, the number of commits since it and the short + commit (`v1.2.3-4-gabc1234`); and the short commit when no tag is + reachable. The stage that compiles also marks its working directory safe + for git (`git config --system --add safe.directory /src`): a context sent + as a tar stream keeps the sender's file owners, and git refuses a checkout + owned by another user, so the version would come out empty. `ARG VERSION` + has no default, and the build fails if the context carries `.git` and the + version still comes out empty, `dev` or `unknown`. A plain + `docker build .` with no build arguments must succeed; a Dockerfile that + refuses an empty build argument drops that refusal and keeps the argument. + `script/docker` and `script/cibuild` pass the version they compute on the + host; it takes precedence. A tag-derived version additionally needs + `fetch-depth: 0` on the CI checkout step, which clones shallow and fetches + no tags by default. - [ ] Gitea Actions workflow in `.gitea/workflows/` runs `script/cibuild` on push — reference `https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml` diff --git a/prompts/NEW_REPO_CHECKLIST.md b/prompts/NEW_REPO_CHECKLIST.md index 5afd36a..ecdd444 100644 --- a/prompts/NEW_REPO_CHECKLIST.md +++ b/prompts/NEW_REPO_CHECKLIST.md @@ -1,6 +1,6 @@ --- title: New Repo Checklist -last_modified: 2026-10-03 +last_modified: 2026-10-04 --- Use this checklist when creating a new repository from scratch. Follow the steps @@ -68,23 +68,24 @@ Template files can be fetched from: will run them in subdirectories, `services/api/.claude/` needs its own anchored entry. - If the image embeds a version in a binary: `.dockerignore` lets `.git` - into the build context. It keeps out `.git/config`, which `git describe` - does not need and which can hold a credential: a password in a remote URL, - or the token the CI checkout step stores there. The stage that compiles - has `git` (the Debian Go image has it; an alpine one needs - `apk add --no-cache git`) and takes the version from the `VERSION` build - argument when one is given, otherwise from `git describe --tags --always`. - That gives the tag on a tagged commit; on a later commit, the tag, the - number of commits since it and the short commit (`v1.2.3-4-gabc1234`); and - the short commit when no tag is reachable. The stage that compiles also - marks its working directory safe for git - (`git config --system --add safe.directory /src`): a context sent as a tar - stream keeps the sender's file owners, and git refuses a checkout owned by - another user, so the version would come out empty. `ARG VERSION` has no - default, and the build fails if the context carries `.git` and the version - still comes out empty, `dev` or `unknown`. A plain `docker build .` with - no build arguments must succeed; a Dockerfile that refuses an empty build - argument drops that refusal and keeps the argument. + into the build context. It keeps out `.git/config` and each submodule's + `config` under `.git/modules/` at any depth (`.git/modules/**/config`), + which `git describe` does not need and which can hold a credential: a + password in a remote URL, or the token the CI checkout step stores there. + The stage that compiles has `git` (the Debian Go image has it; an alpine + one needs `apk add --no-cache git`) and takes the version from the + `VERSION` build argument when one is given, otherwise from + `git describe --tags --always`. That gives the tag on a tagged commit; on + a later commit, the tag, the number of commits since it and the short + commit (`v1.2.3-4-gabc1234`); and the short commit when no tag is + reachable. The stage that compiles also marks its working directory safe + for git (`git config --system --add safe.directory /src`): a context sent + as a tar stream keeps the sender's file owners, and git refuses a checkout + owned by another user, so the version would come out empty. `ARG VERSION` + has no default, and the build fails if the context carries `.git` and the + version still comes out empty, `dev` or `unknown`. A plain + `docker build .` with no build arguments must succeed; a Dockerfile that + refuses an empty build argument drops that refusal and keeps the argument. - The Dockerfile carries a `lint` phase and a `test` phase, each invoking its tool directly rather than through `make` or `script/`, and the final stage carries a `COPY --from=` of a harmless file from each so the image diff --git a/prompts/REPO_POLICIES.md b/prompts/REPO_POLICIES.md index 29b6ab0..3f35e47 100644 --- a/prompts/REPO_POLICIES.md +++ b/prompts/REPO_POLICIES.md @@ -1,6 +1,6 @@ --- title: Repository Policies -last_modified: 2026-10-03 +last_modified: 2026-10-04 --- This document covers repository structure, tooling, and workflow standards. Code @@ -239,20 +239,21 @@ style conventions are in separate documents: - If the project requires CGO or system libraries for linting (e.g. `vips-dev`), install them in the lint phase with `apk add`. - `.dockerignore` lets `.git` into the build context. It keeps out - `.git/config`, which `git describe` does not need and which can hold a - credential: a password in a remote URL, or the token the CI checkout step - stores there. The stage that compiles has `git` (the Debian Go image has - it; an alpine one needs `apk add --no-cache git`) and takes the version - from the `VERSION` build argument when one is given, otherwise from - `git describe --tags --always`. That gives the tag on a tagged commit; on - a later commit, the tag, the number of commits since it and the short - commit (`v1.2.3-4-gabc1234`); and the short commit when no tag is - reachable. The stage that compiles also marks its working directory safe - for git (`git config --system --add safe.directory /src`): a context sent - as a tar stream keeps the sender's file owners, and git refuses a checkout - owned by another user, so the version would come out empty. `ARG VERSION` - has no default, and the build fails if the context carries `.git` and the - version still comes out empty, `dev` or `unknown`. A plain + `.git/config` and each submodule's `config` under `.git/modules/` at any + depth (`.git/modules/**/config`), which `git describe` does not need and + which can hold a credential: a password in a remote URL, or the token the + CI checkout step stores there. The stage that compiles has `git` (the + Debian Go image has it; an alpine one needs `apk add --no-cache git`) and + takes the version from the `VERSION` build argument when one is given, + otherwise from `git describe --tags --always`. That gives the tag on a + tagged commit; on a later commit, the tag, the number of commits since it + and the short commit (`v1.2.3-4-gabc1234`); and the short commit when no + tag is reachable. The stage that compiles also marks its working directory + safe for git (`git config --system --add safe.directory /src`): a context + sent as a tar stream keeps the sender's file owners, and git refuses a + checkout owned by another user, so the version would come out empty. + `ARG VERSION` has no default, and the build fails if the context carries + `.git` and the version still comes out empty, `dev` or `unknown`. A plain `docker build .` with no build arguments must succeed; a Dockerfile that refuses an empty build argument drops that refusal and keeps the argument. -- 2.54.0