Author SHA1 Message Date
sneak b910a1e808 Move the canonical golangci-lint to v2.15.0 and bring back canonicalheader
check / check (push) Canceled after 0s
v2.15.0 (released 2026-10-16, built with go1.27.0 like v2.14.0) bundles
canonicalheader with its upstream fix for non-deterministic results, the
defect behind issue 105. Linted 20 times each on a scratch package with
two planted findings beside a ResponseWriter.Header() call, v2.14.0
reported them on 8 runs and nothing on 12; v2.15.0 reported them on all
20. REPO_POLICIES.md names the new digest; .golangci.yml no longer
disables canonicalheader and is otherwise unchanged, since it verifies
against v2.15.0 as it was and default: all switches on no new linter.
2026-11-01 16:40:35 -08:00
5 changed files with 485 additions and 756 deletions
+10 -22
View File
@@ -3,9 +3,6 @@ version: "2"
# Config schema uses the golangci-lint v2 layout (settings live under # Config schema uses the golangci-lint v2 layout (settings live under
# linters.settings, not top-level linters-settings) so that the # linters.settings, not top-level linters-settings) so that the
# thresholds below are actually applied by golangci-lint >= v2. # thresholds below are actually applied by golangci-lint >= v2.
#
# This file has no formatters section, so golangci-lint run does not run
# gofmt. A repository's script/fmt-check runs it, on the host.
run: run:
timeout: 5m timeout: 5m
@@ -19,13 +16,13 @@ linters:
- gomodguard_v2 - gomodguard_v2
disable: disable:
# Genuinely incompatible with project patterns # Genuinely incompatible with project patterns
- exhaustruct_v5 # Requires all struct fields - exhaustruct # Requires all struct fields
- exhaustruct_v5 # Requires all struct fields (successor to exhaustruct)
- godot # Requires comments to end with periods - godot # Requires comments to end with periods
- wrapcheck # Too verbose for internal packages - wrapcheck # Too verbose for internal packages
- varnamelen # Short names like db, id are idiomatic Go - varnamelen # Short names like db, id are idiomatic Go
# Deprecated: the warning is attached to the old name, so it is # Deprecated: the warning is attached to the old name, so it is
# silenced by disabling that name, not by enabling the successor. # silenced by disabling that name, not by enabling the successor.
- exhaustruct # Deprecated, replaced by exhaustruct_v5
- wsl # Deprecated, replaced by wsl_v5 - wsl # Deprecated, replaced by wsl_v5
- gomodguard # Deprecated, replaced by gomodguard_v2 - gomodguard # Deprecated, replaced by gomodguard_v2
settings: settings:
@@ -43,10 +40,7 @@ linters:
# test-support package exists to hand a test privileges the program # test-support package exists to hand a test privileges the program
# itself must never have, so a file that is not a test must not import # itself must never have, so a file that is not a test must not import
# one. Test files, and the files inside a package whose directory name # one. Test files, and the files inside a package whose directory name
# ends in `test`, are where that code belongs, and are exempt. A package # ends in `test`, are where that code belongs, and are exempt.
# in a subdirectory of that directory is not exempt. `${base-path}` is
# the directory holding this file, so the name of the directory a
# repository is cloned into does not count.
# #
# The deny list below is the one part of this file a repository is # The deny list below is the one part of this file a repository is
# expected to extend, and the only part it may. depguard matches an # expected to extend, and the only part it may. depguard matches an
@@ -61,25 +55,24 @@ linters:
files: files:
- "$all" - "$all"
- "!$test" - "!$test"
- "!${base-path}**/*test/*" - "!**/*test/**"
deny: deny:
- pkg: net/http/httptest - pkg: net/http/httptest
desc: >- desc: >-
Test-support code belongs in test files and in packages whose Test-support code belongs in test files and in packages whose
directory name ends in test, not in the shipped binary. directory name ends in test, not in the shipped binary.
# Each entry names a module not to use and what to use instead. Every # Only decisions already recorded in the Go package defaults are
# entry matches the module path exactly. # listed here. Every entry matches the module path exactly.
gomodguard_v2: gomodguard_v2:
blocked: blocked:
- module: github.com/rs/zerolog - module: github.com/rs/zerolog
recommendations: recommendations:
- log/slog - log/slog
reason: "Structured logging is stdlib log/slog." reason: "Structured logging is stdlib log/slog."
# These entries cover only the github.com/go-redis/redis module paths, # One entry per pre-fork module path, because the later releases
# one entry per path, because the later releases are separate paths. # are separate paths. A prefix match would be shorter but would
# A prefix match would be shorter but would also reach # also reach github.com/go-redis/redismock, the test double for
# github.com/go-redis/redismock/v9, the test double for the successor # the successor these entries recommend.
# these entries recommend.
- module: github.com/go-redis/redis - module: github.com/go-redis/redis
recommendations: recommendations:
- github.com/redis/go-redis/v9 - github.com/redis/go-redis/v9
@@ -92,10 +85,6 @@ linters:
recommendations: recommendations:
- github.com/redis/go-redis/v9 - github.com/redis/go-redis/v9
reason: "Pre-fork module; use the maintained go-redis v9." reason: "Pre-fork module; use the maintained go-redis v9."
- module: github.com/go-redis/redis/v9
recommendations:
- github.com/redis/go-redis/v9
reason: "Pre-fork module; use the maintained go-redis v9."
- module: github.com/sergi/go-diff - module: github.com/sergi/go-diff
recommendations: recommendations:
- github.com/aymanbagabas/go-udiff - github.com/aymanbagabas/go-udiff
@@ -119,4 +108,3 @@ linters:
issues: issues:
max-issues-per-linter: 0 max-issues-per-linter: 0
max-same-issues: 0 max-same-issues: 0
uniq-by-line: false
+9 -47
View File
@@ -21,53 +21,15 @@ fmt-check, and commit.
# Completed Steps # Completed Steps
- 2026-11-04: Rewrote `prompts/REPO_POLICIES.md` sentence by sentence - 2026-11-01: Moved the canonical golangci-lint to v2.15.0 (released
(https://git.eeqj.de/sneak/prompts/issues/136), on sneak's yes at 2026-10-16), still built with go1.27.0, so the `go` directive rule is
https://git.eeqj.de/sneak/prompts/issues/136#issuecomment-165193. Every rule unchanged. The canonical `.golangci.yml` passes `golangci-lint config verify`
keeps its meaning and its place, and `must` and `should` stay as they were. on v2.15.0, and `default: all` switches on no new linter. `canonicalheader` is
Long sentences are split, comma-chained lists are now bulleted lists, and enabled again (issue 105): v2.15.0 carries the upstream commit "fix:
every reason stays. Code blocks, digests, URLs, file names, commands and non-deterministic results", and on a scratch package with two planted findings
quoted strings are unchanged. beside a `ResponseWriter.Header()` call, linted 20 times with each pinned
- 2026-11-03: The canonical golangci-lint is now v2.15.0, still built with image, v2.14.0 reported them on 8 runs and nothing on 12, while v2.15.0
go1.27.0, so the `go` directive rule is unchanged (issue 142). Repositories reported them on all 20.
pick this up on their next re-vendor. Each item in the canonical
`.golangci.yml` was tried on a scratch module. These changed:
- `canonicalheader` is enabled again, because v2.15.0 fixes the random
misses behind issue 105.
- The `depguard` exemption covers only the files directly inside a directory
whose name ends in `test`. It used to reach every directory below that one
too. The name of the directory a repository is cloned into no longer
counts.
- `gomodguard_v2` also blocks `github.com/go-redis/redis/v9`, whose
pre-releases can still be imported under that path.
- `uniq-by-line` is now `false`, so a line that trips two linters reports
both findings.
- Three comments are corrected:
- `exhaustruct` is listed as deprecated.
- The test double is named `github.com/go-redis/redismock/v9`.
- The claim that every blocked module is in the Go package defaults is
gone.
- A comment says the file has no `formatters` section, so
`golangci-lint run` does not run `gofmt`.
- 2026-11-03: A package from the distribution's own package index now needs no
version and no hash, and pulling that index is allowed
(https://git.eeqj.de/sneak/prompts/issues/72), on sneak's answer at
https://git.eeqj.de/sneak/prompts/issues/72#issuecomment-165196. The
distribution is the pinned base image's in a `Dockerfile` and the host's on a
host. It is trusted, and everything else stays pinned by hash. A package
source that a `Dockerfile` adds itself does not get the exception. The pinning
rule of `REPO_POLICIES.md` and both checklists say so, and the canonical Go
`Dockerfile`'s `apk add` line has a comment saying why it is allowed.
- 2026-11-03: A first test run that timed out now gets no verbose rerun
(https://git.eeqj.de/sneak/prompts/issues/61), on sneak's approval at
https://git.eeqj.de/sneak/prompts/issues/61#issuecomment-165195. The Go recipe
in `REPO_POLICIES.md` looks in the first run's output for
`panic: test timed out` or `*** Test killed`, and exits 1 when either is
there. The Python `make test` example takes the same `timeout 90 sh -c`
wrapper as its `Dockerfile` example. The 60 and 90 second rule and the
5-minute rule now say a hung package uses the backstop once. The Go item in
both repo checklists now asks for this, so a repository with the old recipe
fails it.
- 2026-10-27: Added `prompts/API_POLICIES.md` - 2026-10-27: Added `prompts/API_POLICIES.md`
(https://git.eeqj.de/sneak/prompts/issues/137). It holds sneak's rule from (https://git.eeqj.de/sneak/prompts/issues/137). It holds sneak's rule from
https://git.eeqj.de/sneak/routewatch/issues/199 that a value in an API https://git.eeqj.de/sneak/routewatch/issues/199 that a value in an API
+52 -68
View File
@@ -1,6 +1,6 @@
--- ---
title: Existing Repo Checklist title: Existing Repo Checklist
last_modified: 2026-11-04 last_modified: 2026-10-26
--- ---
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
@@ -56,39 +56,34 @@ with your task.
- the step that runs `script/bootstrap` to install a Python `test` phase's - the step that runs `script/bootstrap` to install a Python `test` phase's
packages from `Pipfile.lock` packages from `Pipfile.lock`
- [ ] A stage with none of these steps declares `ARG CACHEBUST` right after - [ ] A stage with none of these steps declares `ARG CACHEBUST` right after
`FROM`. A step that also installs anything else comes after `FROM`. A step that installs anything else as well, such as a
`ARG CACHEBUST` too. A `script/bootstrap` that also installs node packages `script/bootstrap` that also installs node packages, comes after it too.
is one. The `script/` builds pass a new `CACHEBUST` value on every build, The `script/` builds pass a new `CACHEBUST` value on every build, which
which reruns every `RUN` step after that line and leaves the layers above reruns every `RUN` step after that line and leaves the layers above it and
it and the cache mounts in place; `--no-cache` would build those layers the cache mounts in place; `--no-cache` would build those layers again and
again and empty the mounts. A stage without the `ARG` line is served from empty the mounts. A stage without the `ARG` line is served from cache on
cache on an unchanged tree, so the scripts and the `ARG` lines are an unchanged tree, so the scripts and the `ARG` lines are re-vendored in
re-vendored in one commit. one commit.
- [ ] Go: each Go stage downloads the modules (`COPY go.mod go.sum ./`, - [ ] Go: each Go stage downloads the modules (`COPY go.mod go.sum ./`,
`RUN go mod download`) above `ARG CACHEBUST`; the steps that compile `RUN go mod download`) above `ARG CACHEBUST`; the steps that compile
(`golangci-lint run`, `go test`, `go build`) mount Go's build cache (`golangci-lint run`, `go test`, `go build`) mount Go's build cache
(`--mount=type=cache,target=/root/.cache/go-build`), `golangci-lint run` (`--mount=type=cache,target=/root/.cache/go-build`), `golangci-lint run`
also mounts golangci-lint's also mounts golangci-lint's
(`--mount=type=cache,target=/root/.cache/golangci-lint`), and no step (`--mount=type=cache,target=/root/.cache/golangci-lint`), and no step
mounts anything else; both `go test` runs carry `-count=1`; a first run mounts anything else; both `go test` runs carry `-count=1`, as in the
that timed out gets no rerun, as in the canonical Go `Dockerfile` in canonical Go `Dockerfile` in `REPO_POLICIES.md`
`REPO_POLICIES.md`
- [ ] Python: a `test` phase that installs its packages from `Pipfile.lock` does - [ ] Python: a `test` phase that installs its packages from `Pipfile.lock` does
it above `ARG CACHEBUST`, with `COPY script/ script/`, it above `ARG CACHEBUST`, with `COPY script/ script/`,
`COPY Pipfile Pipfile.lock ./` and `RUN script/bootstrap`. When `COPY Pipfile Pipfile.lock ./` and `RUN script/bootstrap`. When
`script/bootstrap` runs in that step, it installs nothing beyond these: `script/bootstrap` runs in that step, it installs nothing beyond system
- system packages packages, pipenv and its dependencies, and the packages from
- pipenv and its dependencies `Pipfile.lock`. It installs pipenv and its dependencies by hash, with
- the packages from `Pipfile.lock` `pip install --require-hashes`. It installs the packages from
`Pipfile.lock` with `pipenv install --deploy`, plus `--dev` when the tests
It installs pipenv and its dependencies by hash, with need the development packages. It installs no Python there, because the
`pip install --require-hashes`. It installs the packages from `Pipfile.lock` tests use the base image's. The tests run after `ARG CACHEBUST`, and every
with `pipenv install --deploy`, plus `--dev` when the tests need the other stage installs its Python packages after it, as in the Python `test`
development packages. It installs no Python there, because the tests use the phase example in `REPO_POLICIES.md`
base image's. The tests run after `ARG CACHEBUST`, and every other stage
installs its Python packages after it, as in the Python `test` phase example
in `REPO_POLICIES.md`
- [ ] The gate phases invoke their tools directly, never through `make lint` or - [ ] The gate phases invoke their tools directly, never through `make lint` or
`script/test` — those are themselves a `docker build` and would recurse `script/test` — those are themselves a `docker build` and would recurse
inside a build step inside a build step
@@ -109,43 +104,37 @@ with your task.
`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 every git `config` at any depth the build context. It keeps out every git `config` at any depth
(`**/.git/config`, `**/.git/modules/**/config`): (`**/.git/config`, `**/.git/modules/**/config`): the repository's own,
- the repository's own each submodule's under `.git/modules/`, and that of a submodule keeping
- each submodule's under `.git/modules/` its own `.git` directory. `git describe` does not need them, and each can
- that of a submodule keeping its own `.git` directory hold a credential: a password in a remote URL, or the token the CI
checkout step stores there. A submodule whose name has a `config` segment
`git describe` does not need them, and each can hold a credential: a (`config`, `deploy/config`, `config/lib`) loses its whole git directory to
password in a remote URL, or the token the CI checkout step stores there. `**/.git/modules/**/config`, and Go's version stamping then fails the
Some submodule names have a `config` segment, as in `config`, build: give it a name without that segment (`git submodule add --name`).
`deploy/config` or `config/lib`. Such a submodule loses its whole git The stage that compiles has `git` (the Debian Go image has it; an alpine
directory to `**/.git/modules/**/config`, and Go's version stamping then one needs `apk add --no-cache git`) and takes the version from the
fails the build. Give it a name without that segment `VERSION` build argument when one is given, otherwise from
(`git submodule add --name`). The stage that compiles has `git`: the Debian `git describe --tags --always`. That gives the tag on a tagged commit; on
Go image has it, and an alpine one needs `apk add --no-cache git`. That a later commit, the tag, the number of commits since it and the short
stage takes the version from the `VERSION` build argument when one is given, commit (`v1.2.3-4-gabc1234`); and the short commit when no tag is
otherwise from `git describe --tags --always`. That gives: reachable. The stage that compiles also marks its working directory safe
- the tag, on a tagged commit for git (`git config --system --add safe.directory /src`): a context sent
- the tag, the number of commits since it and the short commit as a tar stream keeps the sender's file owners, and git refuses a checkout
(`v1.2.3-4-gabc1234`), on a later commit owned by another user, so the version would come out empty. `ARG VERSION`
- the short commit, when no tag is reachable has no default, and the build fails if the context carries `.git` and the
version still comes out empty, `dev` or `unknown`. A plain
The stage that compiles also marks its working directory safe for git `docker build .` with no build arguments must succeed; a Dockerfile that
(`git config --system --add safe.directory /src`). A context sent as a tar refuses an empty build argument drops that refusal and keeps the argument.
stream keeps the sender's file owners, and git refuses a checkout owned by A checkout whose `.git` is a file (a linked worktree, or a repository
another user, so the version would come out empty. `ARG VERSION` has no checked out as a submodule) is the exception: that file points to a git
default. The build fails if the context carries `.git` and the version still directory outside the build context, so the build cannot read the version
comes out empty, `dev` or `unknown`. A plain `docker build .` with no build and a plain `docker build .` fails; pass the version with
arguments must succeed; a Dockerfile that refuses an empty build argument `--build-arg VERSION=...`. `script/docker` and `script/cibuild` already
drops that refusal and keeps the argument. A checkout whose `.git` is a file pass the version they compute on the host; it takes precedence. The
is the exception: a linked worktree, or a repository checked out as a canonical `.gitea/workflows/check.yml` sets `fetch-depth: 0` on its
submodule. That file points to a git directory outside the build context, so checkout step, which otherwise clones shallow and fetches no tags, so a CI
the build cannot read the version and a plain `docker build .` fails. Pass build finds the tag too.
the version with `--build-arg VERSION=...`. `script/docker` and
`script/cibuild` already pass the version they compute on the host; it takes
precedence. The canonical `.gitea/workflows/check.yml` sets `fetch-depth: 0`
on its checkout step, which otherwise clones shallow and fetches no tags, so
a CI build finds the tag too.
- [ ] Gitea Actions workflow in `.gitea/workflows/` runs `script/cibuild` on - [ ] Gitea Actions workflow in `.gitea/workflows/` runs `script/cibuild` on
push, checks out with `persist-credentials: false` and with push, checks out with `persist-credentials: false` and with
`fetch-depth: 0` (which fetches the tags `git describe` needs), carries `fetch-depth: 0` (which fetches the tags `git describe` needs), carries
@@ -226,12 +215,7 @@ with your task.
- [ ] Pre-commit hook is installed (`make hooks`) - [ ] Pre-commit hook is installed (`make hooks`)
- [ ] No secrets in the repo (`.env`, keys, credentials) - [ ] No secrets in the repo (`.env`, keys, credentials)
- [ ] No mutable references in Dockerfiles or scripts (tags, `@latest`) — all - [ ] No mutable references in Dockerfiles or scripts (tags, `@latest`) — all
pinned by cryptographic hash with version/date comment. The one exception pinned by cryptographic hash with version/date comment
is a package from the distribution's own package index, which needs no
version and no hash. Pulling that index is also allowed. In a
`Dockerfile`, the distribution is the pinned base image's. On a host, it
is the host's. A package source that a `Dockerfile` adds itself does not
get this exception
- [ ] Using `yarn`, not `npm` (JS projects) - [ ] Using `yarn`, not `npm` (JS projects)
# Directory Structure # Directory Structure
+47 -65
View File
@@ -1,6 +1,6 @@
--- ---
title: New Repo Checklist title: New Repo Checklist
last_modified: 2026-11-04 last_modified: 2026-10-26
--- ---
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
@@ -75,42 +75,34 @@ Template files can be fetched from:
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 every git `config` at any depth into the build context. It keeps out every git `config` at any depth
(`**/.git/config`, `**/.git/modules/**/config`): (`**/.git/config`, `**/.git/modules/**/config`): the repository's own,
- the repository's own each submodule's under `.git/modules/`, and that of a submodule keeping
- each submodule's under `.git/modules/` its own `.git` directory. `git describe` does not need them, and each can
- that of a submodule keeping its own `.git` directory hold a credential: a password in a remote URL, or the token the CI
checkout step stores there. A submodule whose name has a `config` segment
`git describe` does not need them, and each can hold a credential: a (`config`, `deploy/config`, `config/lib`) loses its whole git directory to
password in a remote URL, or the token the CI checkout step stores `**/.git/modules/**/config`, and Go's version stamping then fails the
there. Some submodule names have a `config` segment, as in `config`, build: give it a name without that segment (`git submodule add --name`).
`deploy/config` or `config/lib`. Such a submodule loses its whole git The stage that compiles has `git` (the Debian Go image has it; an alpine
directory to `**/.git/modules/**/config`, and Go's version stamping then one needs `apk add --no-cache git`) and takes the version from the
fails the build. Give it a name without that segment
(`git submodule add --name`). The stage that compiles has `git`: the
Debian Go image has it, and an alpine one needs
`apk add --no-cache git`. That stage takes the version from the
`VERSION` build argument when one is given, otherwise from `VERSION` build argument when one is given, otherwise from
`git describe --tags --always`. That gives: `git describe --tags --always`. That gives the tag on a tagged commit; on
- the tag, on a tagged commit a later commit, the tag, the number of commits since it and the short
- the tag, the number of commits since it and the short commit commit (`v1.2.3-4-gabc1234`); and the short commit when no tag is
(`v1.2.3-4-gabc1234`), on a later commit reachable. The stage that compiles also marks its working directory safe
- the short commit, when no tag is reachable 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
The stage that compiles also marks its working directory safe for git owned by another user, so the version would come out empty. `ARG VERSION`
(`git config --system --add safe.directory /src`). A context sent as a has no default, and the build fails if the context carries `.git` and the
tar stream keeps the sender's file owners, and git refuses a checkout version still comes out empty, `dev` or `unknown`. A plain
owned by another user, so the version would come out empty. `docker build .` with no build arguments must succeed; a Dockerfile that
`ARG VERSION` has no default. The build fails if the context carries refuses an empty build argument drops that refusal and keeps the argument.
`.git` and the version still comes out empty, `dev` or `unknown`. A A checkout whose `.git` is a file (a linked worktree, or a repository
plain `docker build .` with no build arguments must succeed; a checked out as a submodule) is the exception: that file points to a git
Dockerfile that refuses an empty build argument drops that refusal and directory outside the build context, so the build cannot read the version
keeps the argument. A checkout whose `.git` is a file is the exception: and a plain `docker build .` fails; pass the version with
a linked worktree, or a repository checked out as a submodule. That file `--build-arg VERSION=...`, as `script/docker` and `script/cibuild` already
points to a git directory outside the build context, so the build cannot do.
read the version and a plain `docker build .` fails. Pass the version
with `--build-arg VERSION=...`, as `script/docker` and `script/cibuild`
already do.
- 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
@@ -123,43 +115,37 @@ Template files can be fetched from:
- the step that runs `script/bootstrap` to install a Python `test` - the step that runs `script/bootstrap` to install a Python `test`
phase's packages from `Pipfile.lock` phase's packages from `Pipfile.lock`
- A stage with none of these steps declares `ARG CACHEBUST` right after - A stage with none of these steps declares `ARG CACHEBUST` right after
`FROM`. A step that also installs anything else comes after `FROM`. A step that installs anything else as well, such as a
`ARG CACHEBUST` too. A `script/bootstrap` that also installs node packages `script/bootstrap` that also installs node packages, comes after it too.
is one. The `script/` builds pass a new `CACHEBUST` value on every build, The `script/` builds pass a new `CACHEBUST` value on every build, which
which reruns every `RUN` step after that line and leaves the layers above reruns every `RUN` step after that line and leaves the layers above it and
it and the cache mounts in place; `--no-cache` would build those layers the cache mounts in place; `--no-cache` would build those layers again and
again and empty the mounts. A stage without the `ARG` line is served from empty the mounts. A stage without the `ARG` line is served from cache on
cache on an unchanged tree, so the scripts and every stage's an unchanged tree, so the scripts and every stage's `ARG CACHEBUST` line
`ARG CACHEBUST` line are added in the same commit. are added in the same commit.
- Go: each Go stage downloads the modules (`COPY go.mod go.sum ./`, - Go: each Go stage downloads the modules (`COPY go.mod go.sum ./`,
`RUN go mod download`) above `ARG CACHEBUST`; the steps that compile `RUN go mod download`) above `ARG CACHEBUST`; the steps that compile
(`golangci-lint run`, `go test`, `go build`) mount Go's build cache (`golangci-lint run`, `go test`, `go build`) mount Go's build cache
(`--mount=type=cache,target=/root/.cache/go-build`), `golangci-lint run` (`--mount=type=cache,target=/root/.cache/go-build`), `golangci-lint run`
also mounts golangci-lint's also mounts golangci-lint's
(`--mount=type=cache,target=/root/.cache/golangci-lint`), and no step (`--mount=type=cache,target=/root/.cache/golangci-lint`), and no step
mounts anything else; both `go test` runs carry `-count=1`; a first run mounts anything else; both `go test` runs carry `-count=1`, as in the
that timed out gets no rerun, as in the canonical Go `Dockerfile` in canonical Go `Dockerfile` in `REPO_POLICIES.md`
`REPO_POLICIES.md`
- Python: a `test` phase that installs its packages from `Pipfile.lock` does - Python: a `test` phase that installs its packages from `Pipfile.lock` does
it above `ARG CACHEBUST`, with `COPY script/ script/`, it above `ARG CACHEBUST`, with `COPY script/ script/`,
`COPY Pipfile Pipfile.lock ./` and `RUN script/bootstrap`. When `COPY Pipfile Pipfile.lock ./` and `RUN script/bootstrap`. When
`script/bootstrap` runs in that step, it installs nothing beyond these: `script/bootstrap` runs in that step, it installs nothing beyond system
- system packages packages, pipenv and its dependencies, and the packages from
- pipenv and its dependencies `Pipfile.lock`. It installs pipenv and its dependencies by hash, with
- the packages from `Pipfile.lock`
It installs pipenv and its dependencies by hash, with
`pip install --require-hashes`. It installs the packages from `pip install --require-hashes`. It installs the packages from
`Pipfile.lock` with `pipenv install --deploy`, plus `--dev` when the `Pipfile.lock` with `pipenv install --deploy`, plus `--dev` when the tests
tests need the development packages. It installs no Python there, need the development packages. It installs no Python there, because the
because the tests use the base image's. The tests run after tests use the base image's. The tests run after `ARG CACHEBUST`, and every
`ARG CACHEBUST`, and every other stage installs its Python packages other stage installs its Python packages after it, as in the Python `test`
after it, as in the Python `test` phase example in `REPO_POLICIES.md` phase example in `REPO_POLICIES.md`
- Server: the final stage builds and runs the application - Server: the final stage builds and runs the application
- Non-server: the final stage brings up the dev environment - Non-server: the final stage brings up the dev environment
- Image pinned by sha256 hash with version/date comment - Image pinned by sha256 hash with version/date comment
- [ ] Gitea Actions workflow at `.gitea/workflows/check.yml` that runs - [ ] Gitea Actions workflow at `.gitea/workflows/check.yml` that runs
`script/cibuild` on push, checks out with `persist-credentials: false` and `script/cibuild` on push, checks out with `persist-credentials: false` and
with `fetch-depth: 0` (which fetches the tags `git describe` needs), with `fetch-depth: 0` (which fetches the tags `git describe` needs),
@@ -256,11 +242,7 @@ are thin shims calling them. Model scripts:
final stage is missing its `COPY --from=` edge to the gate phases. final stage is missing its `COPY --from=` edge to the gate phases.
- [ ] No secrets in repo, and none in the build context: enumerate a probe image - [ ] No secrets in repo, and none in the build context: enumerate a probe image
rather than reading `.dockerignore` rather than reading `.dockerignore`
- [ ] No mutable image/package references. The one exception is a package from - [ ] No mutable image/package references
the distribution's own package index, which needs no version and no hash.
Pulling that index is also allowed. In a `Dockerfile`, the distribution is
the pinned base image's. On a host, it is the host's. A package source
that a `Dockerfile` adds itself does not get this exception
- [ ] No unnecessary files in repo root - [ ] No unnecessary files in repo root
- [ ] All dates written as YYYY-MM-DD - [ ] All dates written as YYYY-MM-DD
+365 -552
View File
File diff suppressed because it is too large Load Diff