Keep each submodule's git config out of the build context (closes #75)
check / check (push) Successful in 44s

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
This commit is contained in:
2026-10-04 02:54:48 +00:00
parent dcc0ba0b66
commit 13e713ba54
5 changed files with 67 additions and 54 deletions
+3
View File
@@ -17,7 +17,10 @@
# stage that compiles runs `git describe --tags --always` on .git, which # 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 # 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. # 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/config
.git/modules/**/config
# Agent scratch: one full checkout of the repo per in-flight agent. # Agent scratch: one full checkout of the repo per in-flight agent.
# Anchored because it occurs once where agents run at the repo root. # Anchored because it occurs once where agents run at the repo root.
+6
View File
@@ -21,6 +21,12 @@ fmt-check, and commit.
# Completed Steps # 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 - 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 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. the alpine image has no C compiler, so the phase failed before running a test.
+23 -21
View File
@@ -1,6 +1,6 @@
--- ---
title: Existing Repo Checklist 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 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 here run anywhere other than the repo root, the anchored entry misses
`services/api/.claude/`: add anchored entries for those directories. `services/api/.claude/`: add anchored entries for those directories.
- [ ] If the repo embeds a version in a binary: `.dockerignore` lets `.git` into - [ ] 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 the build context. It keeps out `.git/config` and each submodule's
not need and which can hold a credential: a password in a remote URL, or `config` under `.git/modules/` at any depth (`.git/modules/**/config`),
the token the CI checkout step stores there. The stage that compiles has which `git describe` does not need and which can hold a credential: a
`git` (the Debian Go image has it; an alpine one needs password in a remote URL, or the token the CI checkout step stores there.
`apk add --no-cache git`) and takes the version from the `VERSION` build The stage that compiles has `git` (the Debian Go image has it; an alpine
argument when one is given, otherwise from `git describe --tags --always`. one needs `apk add --no-cache git`) and takes the version from the
That gives the tag on a tagged commit; on a later commit, the tag, the `VERSION` build argument when one is given, otherwise from
number of commits since it and the short commit (`v1.2.3-4-gabc1234`); and `git describe --tags --always`. That gives the tag on a tagged commit; on
the short commit when no tag is reachable. The stage that compiles also a later commit, the tag, the number of commits since it and the short
marks its working directory safe for git commit (`v1.2.3-4-gabc1234`); and the short commit when no tag is
(`git config --system --add safe.directory /src`): a context sent as a tar reachable. The stage that compiles also marks its working directory safe
stream keeps the sender's file owners, and git refuses a checkout owned by for git (`git config --system --add safe.directory /src`): a context sent
another user, so the version would come out empty. `ARG VERSION` has no as a tar stream keeps the sender's file owners, and git refuses a checkout
default, and the build fails if the context carries `.git` and the version owned by another user, so the version would come out empty. `ARG VERSION`
still comes out empty, `dev` or `unknown`. A plain `docker build .` with has no default, and the build fails if the context carries `.git` and the
no build arguments must succeed; a Dockerfile that refuses an empty build version still comes out empty, `dev` or `unknown`. A plain
argument drops that refusal and keeps the argument. `script/docker` and `docker build .` with no build arguments must succeed; a Dockerfile that
`script/cibuild` pass the version they compute on the host; it takes refuses an empty build argument drops that refusal and keeps the argument.
precedence. A tag-derived version additionally needs `fetch-depth: 0` on `script/docker` and `script/cibuild` pass the version they compute on the
the CI checkout step, which clones shallow and fetches no tags by default. 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 - [ ] Gitea Actions workflow in `.gitea/workflows/` runs `script/cibuild` on
push — reference push — reference
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml` `https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml`
+19 -18
View File
@@ -1,6 +1,6 @@
--- ---
title: New Repo Checklist 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 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 will run them in subdirectories, `services/api/.claude/` needs its own
anchored entry. anchored entry.
- If the image embeds a version in a binary: `.dockerignore` lets `.git` - If the image embeds a version in a binary: `.dockerignore` lets `.git`
into the build context. It keeps out `.git/config`, which `git describe` into the build context. It keeps out `.git/config` and each submodule's
does not need and which can hold a credential: a password in a remote URL, `config` under `.git/modules/` at any depth (`.git/modules/**/config`),
or the token the CI checkout step stores there. The stage that compiles which `git describe` does not need and which can hold a credential: a
has `git` (the Debian Go image has it; an alpine one needs password in a remote URL, or the token the CI checkout step stores there.
`apk add --no-cache git`) and takes the version from the `VERSION` build The stage that compiles has `git` (the Debian Go image has it; an alpine
argument when one is given, otherwise from `git describe --tags --always`. one needs `apk add --no-cache git`) and takes the version from the
That gives the tag on a tagged commit; on a later commit, the tag, the `VERSION` build argument when one is given, otherwise from
number of commits since it and the short commit (`v1.2.3-4-gabc1234`); and `git describe --tags --always`. That gives the tag on a tagged commit; on
the short commit when no tag is reachable. The stage that compiles also a later commit, the tag, the number of commits since it and the short
marks its working directory safe for git commit (`v1.2.3-4-gabc1234`); and the short commit when no tag is
(`git config --system --add safe.directory /src`): a context sent as a tar reachable. The stage that compiles also marks its working directory safe
stream keeps the sender's file owners, and git refuses a checkout owned by for git (`git config --system --add safe.directory /src`): a context sent
another user, so the version would come out empty. `ARG VERSION` has no as a tar stream keeps the sender's file owners, and git refuses a checkout
default, and the build fails if the context carries `.git` and the version owned by another user, so the version would come out empty. `ARG VERSION`
still comes out empty, `dev` or `unknown`. A plain `docker build .` with has no default, and the build fails if the context carries `.git` and the
no build arguments must succeed; a Dockerfile that refuses an empty build version still comes out empty, `dev` or `unknown`. A plain
argument drops that refusal and keeps the argument. `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 - The Dockerfile carries a `lint` phase and a `test` phase, each invoking
its tool directly rather than through `make` or `script/`, and the final 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 stage carries a `COPY --from=` of a harmless file from each so the image
+16 -15
View File
@@ -1,6 +1,6 @@
--- ---
title: Repository Policies title: Repository Policies
last_modified: 2026-10-03 last_modified: 2026-10-04
--- ---
This document covers repository structure, tooling, and workflow standards. Code 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. - If the project requires CGO or system libraries for linting (e.g.
`vips-dev`), install them in the lint phase with `apk add`. `vips-dev`), install them in the lint phase with `apk add`.
- `.dockerignore` lets `.git` into the build context. It keeps out - `.dockerignore` lets `.git` into the build context. It keeps out
`.git/config`, which `git describe` does not need and which can hold a `.git/config` and each submodule's `config` under `.git/modules/` at any
credential: a password in a remote URL, or the token the CI checkout step depth (`.git/modules/**/config`), which `git describe` does not need and
stores there. The stage that compiles has `git` (the Debian Go image has which can hold a credential: a password in a remote URL, or the token the
it; an alpine one needs `apk add --no-cache git`) and takes the version CI checkout step stores there. The stage that compiles has `git` (the
from the `VERSION` build argument when one is given, otherwise from Debian Go image has it; an alpine one needs `apk add --no-cache git`) and
`git describe --tags --always`. That gives the tag on a tagged commit; on takes the version from the `VERSION` build argument when one is given,
a later commit, the tag, the number of commits since it and the short otherwise from `git describe --tags --always`. That gives the tag on a
commit (`v1.2.3-4-gabc1234`); and the short commit when no tag is tagged commit; on a later commit, the tag, the number of commits since it
reachable. The stage that compiles also marks its working directory safe and the short commit (`v1.2.3-4-gabc1234`); and the short commit when no
for git (`git config --system --add safe.directory /src`): a context sent tag is reachable. The stage that compiles also marks its working directory
as a tar stream keeps the sender's file owners, and git refuses a checkout safe for git (`git config --system --add safe.directory /src`): a context
owned by another user, so the version would come out empty. `ARG VERSION` sent as a tar stream keeps the sender's file owners, and git refuses a
has no default, and the build fails if the context carries `.git` and the checkout owned by another user, so the version would come out empty.
version still comes out empty, `dev` or `unknown`. A plain `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 `docker build .` with no build arguments must succeed; a Dockerfile that
refuses an empty build argument drops that refusal and keeps the argument. refuses an empty build argument drops that refusal and keeps the argument.