Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14e79875e1 | ||
|
|
899beebc1f | ||
|
|
a3ec8461af | ||
|
|
83f148f3b2 |
@@ -145,15 +145,16 @@ alpine. We provide:
|
|||||||
`make hooks` installs the pre-commit hook that runs `script/precommit`.
|
`make hooks` installs the pre-commit hook that runs `script/precommit`.
|
||||||
|
|
||||||
In every stage of a `Dockerfile`, `ARG CACHEBUST` comes right after the steps
|
In every stage of a `Dockerfile`, `ARG CACHEBUST` comes right after the steps
|
||||||
that install system packages and download Go's modules, or right after `FROM` in
|
that install system packages, download Go's modules or run `script/bootstrap` to
|
||||||
a stage with neither, and every step that lints, tests or compiles comes after
|
install a Python `test` phase's packages from `Pipfile.lock`, or right after
|
||||||
it. The `script/` builds pass a new `CACHEBUST` value on every build, which
|
`FROM` in a stage with none of these, and every step that lints, tests or
|
||||||
reruns every `RUN` step after that line and leaves the layers above it and the
|
compiles comes after it. The `script/` builds pass a new `CACHEBUST` value on
|
||||||
cache mounts in place; `--no-cache` would build those layers again and empty the
|
every build, which reruns every `RUN` step after that line and leaves the layers
|
||||||
mounts. This repo's own `Dockerfile` declares `ARG CACHEBUST` right after `FROM`
|
above it and the cache mounts in place; `--no-cache` would build those layers
|
||||||
in every stage: each stage installs system packages and node packages together
|
again and empty the mounts. This repo's own `Dockerfile` declares
|
||||||
in one `script/bootstrap` step, and check builds may keep system packages but
|
`ARG CACHEBUST` right after `FROM` in every stage: each stage installs system
|
||||||
not node packages.
|
packages and node packages together in one `script/bootstrap` step, and check
|
||||||
|
builds may keep system packages but not node packages.
|
||||||
|
|
||||||
## Rationale
|
## Rationale
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,23 @@ fmt-check, and commit.
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-10-21: Check builds may now also keep the Python packages a `test` phase
|
||||||
|
installs from `Pipfile.lock` (issue 133), on sneak's ruling in
|
||||||
|
https://git.eeqj.de/sneak/rfscan/issues/80#issuecomment-153437. The install
|
||||||
|
comes before `ARG CACHEBUST`: `COPY script/ script/`,
|
||||||
|
`COPY Pipfile Pipfile.lock ./`, then `RUN script/bootstrap`. In that step
|
||||||
|
`script/bootstrap` installs nothing beyond system packages, pipenv and its
|
||||||
|
dependencies by hash, and the packages from the lockfile, and no Python, since
|
||||||
|
the tests use the base image's. `--deploy` installs only what the lockfile
|
||||||
|
names, checks every package against its hashes, and fails when the lockfile is
|
||||||
|
out of date with `Pipfile`. A kept layer holds nothing a new install would
|
||||||
|
refuse, except that a package built from source and the system packages may
|
||||||
|
differ from a new install. The packages are kept as a layer, not a cache
|
||||||
|
mount, for the reason Go's modules are. The tests still run after
|
||||||
|
`ARG CACHEBUST`, and every other stage still installs its Python packages
|
||||||
|
after it. `REPO_POLICIES.md` gains a short Python `test` phase example, and
|
||||||
|
both checklists and the README state the rule the same way. rfscan picks this
|
||||||
|
up on its next re-vendor.
|
||||||
- 2026-10-21: The canonical `.golangci.yml` now accepts a local `replace` of
|
- 2026-10-21: The canonical `.golangci.yml` now accepts a local `replace` of
|
||||||
`github.com/grafana/sobek` in a repository's root `go.mod` (issue 134), for
|
`github.com/grafana/sobek` in a repository's root `go.mod` (issue 134), for
|
||||||
homoicon, which keeps a local copy of sobek on sneak's decision in
|
homoicon, which keeps a local copy of sobek on sneak's decision in
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Existing Repo Checklist
|
title: Existing Repo Checklist
|
||||||
last_modified: 2026-10-18
|
last_modified: 2026-10-21
|
||||||
---
|
---
|
||||||
|
|
||||||
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
|
||||||
@@ -50,16 +50,17 @@ with your task.
|
|||||||
passing while the gate never runs. Confirm by planting a violation, not by
|
passing while the gate never runs. Confirm by planting a violation, not by
|
||||||
reading the file.
|
reading the file.
|
||||||
- [ ] In every stage `ARG CACHEBUST` comes right after the steps that install
|
- [ ] In every stage `ARG CACHEBUST` comes right after the steps that install
|
||||||
system packages and download Go's modules, or right after `FROM` in a
|
system packages, download Go's modules or run `script/bootstrap` to
|
||||||
stage with neither, and every step that lints, tests or compiles comes
|
install a Python `test` phase's packages from `Pipfile.lock`, or right
|
||||||
after it. A step that installs anything else as well, such as a
|
after `FROM` in a stage with none of these, and every step that lints,
|
||||||
`script/bootstrap` that also installs node packages, comes after it too.
|
tests or compiles comes after it. A step that installs anything else as
|
||||||
The `script/` builds pass a new `CACHEBUST` value on every build, which
|
well, such as a `script/bootstrap` that also installs node packages, comes
|
||||||
reruns every `RUN` step after that line and leaves the layers above it and
|
after it too. The `script/` builds pass a new `CACHEBUST` value on every
|
||||||
the cache mounts in place; `--no-cache` would build those layers again and
|
build, which reruns every `RUN` step after that line and leaves the layers
|
||||||
empty the mounts. A stage without the `ARG` line is served from cache on
|
above it and the cache mounts in place; `--no-cache` would build those
|
||||||
an unchanged tree, so the scripts and the `ARG` lines are re-vendored in
|
layers again and empty the mounts. A stage without the `ARG` line is
|
||||||
one commit.
|
served from cache on an unchanged tree, so the scripts and the `ARG` lines
|
||||||
|
are re-vendored in 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
|
||||||
@@ -68,6 +69,17 @@ with your task.
|
|||||||
(`--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`, as in the
|
mounts anything else; both `go test` runs carry `-count=1`, as in the
|
||||||
canonical Go `Dockerfile` in `REPO_POLICIES.md`
|
canonical Go `Dockerfile` in `REPO_POLICIES.md`
|
||||||
|
- [ ] Python: a `test` phase that installs its packages from `Pipfile.lock` does
|
||||||
|
it above `ARG CACHEBUST`, with `COPY script/ script/`,
|
||||||
|
`COPY Pipfile Pipfile.lock ./` and `RUN script/bootstrap`.
|
||||||
|
`script/bootstrap`, when it runs in that step, installs nothing beyond
|
||||||
|
system packages, pipenv and its dependencies by hash
|
||||||
|
(`pip install --require-hashes`), and the packages from the lockfile
|
||||||
|
(`pipenv install --deploy`, with `--dev` when the tests need the
|
||||||
|
development packages). It installs no Python there, because the tests use
|
||||||
|
the 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
|
||||||
@@ -232,9 +244,10 @@ with your task.
|
|||||||
(the runner image `docker.gitea.com/runner-images:ubuntu-latest`: docker,
|
(the runner image `docker.gitea.com/runner-images:ubuntu-latest`: docker,
|
||||||
git and node 24, with no yarn on `PATH`), and demonstrably executed the
|
git and node 24, with no yarn on `PATH`), and demonstrably executed the
|
||||||
checks — a sub-second build, or `CACHED` on a step that lints or tests,
|
checks — a sub-second build, or `CACHED` on a step that lints or tests,
|
||||||
one of the `RUN` steps below `ARG CACHEBUST`, means nothing ran. The steps
|
one of the `RUN` steps below `ARG CACHEBUST`, means nothing ran. Above
|
||||||
that install system packages and download Go's modules above that line are
|
that line, the steps that install system packages, download Go's modules
|
||||||
expected to show `CACHED`.
|
or run `script/bootstrap` to install a Python `test` phase's packages from
|
||||||
|
`Pipfile.lock` are expected to show `CACHED`.
|
||||||
- [ ] A planted lint violation fails both `make lint` and a plain
|
- [ ] A planted lint violation fails both `make lint` and a plain
|
||||||
`docker build .`; revert it afterwards
|
`docker build .`; revert it afterwards
|
||||||
- [ ] Commit and merge fixes before starting your actual task
|
- [ ] Commit and merge fixes before starting your actual task
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: New Repo Checklist
|
title: New Repo Checklist
|
||||||
last_modified: 2026-10-18
|
last_modified: 2026-10-21
|
||||||
---
|
---
|
||||||
|
|
||||||
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
|
||||||
@@ -109,16 +109,17 @@ Template files can be fetched from:
|
|||||||
cannot be built unless both passed. Keep the final stage last: a stage
|
cannot be built unless both passed. Keep the final stage last: a stage
|
||||||
nothing depends on is built only when `--target` names it.
|
nothing depends on is built only when `--target` names it.
|
||||||
- In every stage `ARG CACHEBUST` comes right after the steps that install
|
- In every stage `ARG CACHEBUST` comes right after the steps that install
|
||||||
system packages and download Go's modules, or right after `FROM` in a
|
system packages, download Go's modules or run `script/bootstrap` to
|
||||||
stage with neither, and every step that lints, tests or compiles comes
|
install a Python `test` phase's packages from `Pipfile.lock`, or right
|
||||||
after it. A step that installs anything else as well, such as a
|
after `FROM` in a stage with none of these, and every step that lints,
|
||||||
`script/bootstrap` that also installs node packages, comes after it too.
|
tests or compiles comes after it. A step that installs anything else as
|
||||||
The `script/` builds pass a new `CACHEBUST` value on every build, which
|
well, such as a `script/bootstrap` that also installs node packages, comes
|
||||||
reruns every `RUN` step after that line and leaves the layers above it and
|
after it too. The `script/` builds pass a new `CACHEBUST` value on every
|
||||||
the cache mounts in place; `--no-cache` would build those layers again and
|
build, which reruns every `RUN` step after that line and leaves the layers
|
||||||
empty the mounts. A stage without the `ARG` line is served from cache on
|
above it and the cache mounts in place; `--no-cache` would build those
|
||||||
an unchanged tree, so the scripts and every stage's `ARG CACHEBUST` line
|
layers again and empty the mounts. A stage without the `ARG` line is
|
||||||
are added in the same commit.
|
served from cache on an unchanged tree, so the scripts and every stage's
|
||||||
|
`ARG CACHEBUST` line 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
|
||||||
@@ -127,6 +128,17 @@ Template files can be fetched from:
|
|||||||
(`--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`, as in the
|
mounts anything else; both `go test` runs carry `-count=1`, as in the
|
||||||
canonical Go `Dockerfile` in `REPO_POLICIES.md`
|
canonical Go `Dockerfile` in `REPO_POLICIES.md`
|
||||||
|
- Python: a `test` phase that installs its packages from `Pipfile.lock` does
|
||||||
|
it above `ARG CACHEBUST`, with `COPY script/ script/`,
|
||||||
|
`COPY Pipfile Pipfile.lock ./` and `RUN script/bootstrap`.
|
||||||
|
`script/bootstrap`, when it runs in that step, installs nothing beyond
|
||||||
|
system packages, pipenv and its dependencies by hash
|
||||||
|
(`pip install --require-hashes`), and the packages from the lockfile
|
||||||
|
(`pipenv install --deploy`, with `--dev` when the tests need the
|
||||||
|
development packages). It installs no Python there, because the tests use
|
||||||
|
the 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`
|
||||||
- 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
|
||||||
@@ -215,9 +227,10 @@ are thin shims calling them. Model scripts:
|
|||||||
(the runner image `docker.gitea.com/runner-images:ubuntu-latest`: docker,
|
(the runner image `docker.gitea.com/runner-images:ubuntu-latest`: docker,
|
||||||
git and node 24, with no yarn on `PATH`), and demonstrably executed the
|
git and node 24, with no yarn on `PATH`), and demonstrably executed the
|
||||||
checks — a sub-second build, or `CACHED` on a step that lints or tests,
|
checks — a sub-second build, or `CACHED` on a step that lints or tests,
|
||||||
one of the `RUN` steps below `ARG CACHEBUST`, means nothing ran. The steps
|
one of the `RUN` steps below `ARG CACHEBUST`, means nothing ran. Above
|
||||||
that install system packages and download Go's modules above that line are
|
that line, the steps that install system packages, download Go's modules
|
||||||
expected to show `CACHED`.
|
or run `script/bootstrap` to install a Python `test` phase's packages from
|
||||||
|
`Pipfile.lock` are expected to show `CACHED`.
|
||||||
- [ ] Plant a lint violation and confirm both `make lint` and a plain
|
- [ ] Plant a lint violation and confirm both `make lint` and a plain
|
||||||
`docker build .` fail on it; revert. A plain build that passes proves the
|
`docker build .` fail on it; revert. A plain build that passes proves the
|
||||||
final stage is missing its `COPY --from=` edge to the gate phases.
|
final stage is missing its `COPY --from=` edge to the gate phases.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Repository Policies
|
title: Repository Policies
|
||||||
last_modified: 2026-10-18
|
last_modified: 2026-10-21
|
||||||
---
|
---
|
||||||
|
|
||||||
This document covers repository structure, tooling, and workflow standards. Code
|
This document covers repository structure, tooling, and workflow standards. Code
|
||||||
@@ -175,12 +175,13 @@ style conventions are in separate documents:
|
|||||||
`script/docker` are the four, and there is no fifth — `script/check` runs the
|
`script/docker` are the four, and there is no fifth — `script/check` runs the
|
||||||
two gate phases and `script/fmt-check`, and builds no image of its own. In
|
two gate phases and `script/fmt-check`, and builds no image of its own. In
|
||||||
every stage of the `Dockerfile`, `ARG CACHEBUST` comes right after the steps
|
every stage of the `Dockerfile`, `ARG CACHEBUST` comes right after the steps
|
||||||
that install system packages and download Go's modules, or right after `FROM`
|
that install system packages, download Go's modules or run `script/bootstrap`
|
||||||
in a stage with neither, and every step that lints, tests or compiles comes
|
to install a Python `test` phase's packages from `Pipfile.lock`, or right
|
||||||
after it. A step that installs anything else as well, such as a
|
after `FROM` in a stage with none of these, and every step that lints, tests
|
||||||
`script/bootstrap` that also installs node packages, comes after it too. A new
|
or compiles comes after it. A step that installs anything else as well, such
|
||||||
value reruns every `RUN` step after that line; `COPY` steps are compared by
|
as a `script/bootstrap` that also installs node packages, comes after it too.
|
||||||
content and need nothing. Docker keeps the layers above the line until the
|
A new value reruns every `RUN` step after that line; `COPY` steps are compared
|
||||||
|
by content and need nothing. Docker keeps the layers above the line until the
|
||||||
base image, their own lines or the files they copy change. A stage without the
|
base image, their own lines or the files they copy change. A stage without the
|
||||||
`ARG` line is served from cache on an unchanged tree, so a repository
|
`ARG` line is served from cache on an unchanged tree, so a repository
|
||||||
re-vendors the scripts and adds the `ARG` lines in one commit. The scripts do
|
re-vendors the scripts and adds the `ARG` lines in one commit. The scripts do
|
||||||
@@ -309,6 +310,9 @@ style conventions are in separate documents:
|
|||||||
a package's findings only when the package's files and those of every
|
a package's findings only when the package's files and those of every
|
||||||
package it imports, `go.mod`, the linters and their settings in
|
package it imports, `go.mod`, the linters and their settings in
|
||||||
`.golangci.yml`, and the golangci-lint version are all unchanged.
|
`.golangci.yml`, and the golangci-lint version are all unchanged.
|
||||||
|
- In a Python `test` phase, the layer of the step that runs
|
||||||
|
`script/bootstrap` to install the packages from `Pipfile.lock`, as
|
||||||
|
described for that phase below.
|
||||||
- If the project uses `//go:embed` directives that reference build artifacts
|
- If the project uses `//go:embed` directives that reference build artifacts
|
||||||
(e.g. a web frontend compiled in a separate stage), the lint phase must
|
(e.g. a web frontend compiled in a separate stage), the lint phase must
|
||||||
create placeholder files so the embed directives resolve. Example:
|
create placeholder files so the embed directives resolve. Example:
|
||||||
@@ -363,6 +367,67 @@ style conventions are in separate documents:
|
|||||||
`--build-arg VERSION=...`, as `script/docker` and `script/cibuild` already
|
`--build-arg VERSION=...`, as `script/docker` and `script/cibuild` already
|
||||||
do.
|
do.
|
||||||
|
|
||||||
|
- **A Python `test` phase that installs its packages from `Pipfile.lock` does it
|
||||||
|
above `ARG CACHEBUST`, so check builds keep them.** The steps are
|
||||||
|
`COPY script/ script/`, `COPY Pipfile Pipfile.lock ./` and
|
||||||
|
`RUN script/bootstrap`, with `ARG CACHEBUST` right after them. Docker keeps
|
||||||
|
what that `RUN` installed until the base image, `script/`, `Pipfile`,
|
||||||
|
`Pipfile.lock` or a line up to and including the `RUN` changes. The tests run
|
||||||
|
after `ARG CACHEBUST`, on every check build. In every other stage, a step that
|
||||||
|
installs Python packages comes after `ARG CACHEBUST`.
|
||||||
|
- When `script/bootstrap` runs in that step, it installs nothing beyond
|
||||||
|
system packages, pipenv and its dependencies, and the packages from
|
||||||
|
`Pipfile.lock`. It installs no Python there: the tests use the base
|
||||||
|
image's Python, which the image's hash pins. If it installs anything else
|
||||||
|
there, the step goes after `ARG CACHEBUST`.
|
||||||
|
- It installs pipenv and its dependencies by hash, as the rule that every
|
||||||
|
external reference is pinned by hash requires. It does so with
|
||||||
|
`pip install --require-hashes -r` and a requirements file in `script/`
|
||||||
|
that lists each of them with `==` and its `--hash`. pip refuses to install
|
||||||
|
a dependency the file does not list.
|
||||||
|
- It then runs `pipenv install --deploy`, with `--dev` when the tests need
|
||||||
|
the development packages. `--deploy` installs only what `Pipfile.lock`
|
||||||
|
names, checks every package against the lockfile's hashes, and fails when
|
||||||
|
the lockfile is out of date with `Pipfile`.
|
||||||
|
- A kept layer holds nothing a new install would refuse: pip checked pipenv
|
||||||
|
and its dependencies, and pipenv checked the lockfile's packages, against
|
||||||
|
their hashes. The packages `virtualenv` puts into the virtualenv that
|
||||||
|
pipenv creates come from wheels the `virtualenv` package ships, which its
|
||||||
|
hash covers. Two things in the layer are not fixed by a hash. A package
|
||||||
|
with no ready-built wheel for the image is built from source, with build
|
||||||
|
tools pip downloads without a hash check, so a kept layer's build of it
|
||||||
|
may differ from a new one. And system packages keep the versions the
|
||||||
|
package manager gave when the layer was built; a new install may get newer
|
||||||
|
ones.
|
||||||
|
- The packages are kept in a layer rather than a cache mount for the reason
|
||||||
|
Go's modules are: a cache mount is shared by every build on the host, and
|
||||||
|
nothing checks its files again.
|
||||||
|
|
||||||
|
In this example, `script/bootstrap` finds `make` and `git` already
|
||||||
|
installed, installs pipenv and its dependencies by hash, and runs
|
||||||
|
`pipenv install --dev --deploy`:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Test phase. The 90-second timeout covers both pytest runs.
|
||||||
|
# python:3.x-slim, YYYY-MM-DD
|
||||||
|
FROM python@sha256:... AS test
|
||||||
|
# make and git are base tools that script/bootstrap installs when they
|
||||||
|
# are missing. Installed here, they stay in their own layer when
|
||||||
|
# script/ changes.
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends make git \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
WORKDIR /app
|
||||||
|
COPY script/ script/
|
||||||
|
COPY Pipfile Pipfile.lock ./
|
||||||
|
RUN script/bootstrap
|
||||||
|
ARG CACHEBUST
|
||||||
|
COPY . .
|
||||||
|
RUN timeout 90 sh -c 'pipenv run pytest || { \
|
||||||
|
echo "--- Rerunning with -v for details ---"; \
|
||||||
|
pipenv run pytest -v; exit 1; }'
|
||||||
|
```
|
||||||
|
|
||||||
- Every repo should have a Gitea Actions workflow (`.gitea/workflows/`) that
|
- Every repo should have a Gitea Actions workflow (`.gitea/workflows/`) that
|
||||||
runs `script/cibuild` on push, and checks out the repo as its only other step,
|
runs `script/cibuild` on push, and checks out the repo as its only other step,
|
||||||
with `persist-credentials: false`: `script/cibuild` needs no token, and
|
with `persist-credentials: false`: `script/cibuild` needs no token, and
|
||||||
|
|||||||
Reference in New Issue
Block a user