1
0
forked from sneak/dcf

bring the repo up to org standards

Adopt the standard tooling: a Makefile of thin shims over a full
scripts-to-rule-them-all `script/` set, the canonical `.golangci.yml`
vendored byte-identical from `sneak/prompts`, docker-only linting via
`Dockerfile.lint`, a `Dockerfile` and Gitea workflow that gate every
push, and prettier/editorconfig/dockerignore config.

`make build` pointed at a `cmd/dcfinfo` that is not in the tree and
could never have succeeded; this repo is a library, so `build` is now
the compile check over every package.

Clear the 57 findings the canonical linter config reports on `pkg/dcf`.
Two were real: `findDCFMountPoints` checked a never-assigned `erro`
instead of the error from `findAllMountPoints`, discarding it, and
`privatePath` was computed twice so the first computation was dead.
Mountpoint selection otherwise behaves exactly as before; the defects
that survive are filed as issue #6, not fixed here.

Rename `DCFStore` to `Store` and `DCFObject` to `Object` (with its
`DCFStoreRoot` field to `StoreRoot`), which revive's stutter rule
requires and which the `fs.FS` rework in issue #4 will build on.

Replace the placeholder test with tests over the exported surface.
The filesystem walk stays uncovered: it is reachable only through
`GetDCFStores`, which needs real mounted media.

README keeps its content, reorganised into the required sections and
gaining Entrypoints.

(closes #1)
This commit is contained in:
2026-08-30 11:36:58 +00:00
parent f585e8fa34
commit ed95e66f7d
32 changed files with 1622 additions and 152 deletions

89
script/cibuild Executable file
View File

@@ -0,0 +1,89 @@
#!/bin/sh
# script/cibuild: run the CI build. The Dockerfile runs the checks
# (gofmt, config verify, lint, test), so a successful build implies a
# green repo. The Gitea workflow runs this on push.
#
# Only if the checks actually ran, though, and a green `docker build` is
# no evidence that they did:
#
# 1. On an unchanged tree every layer comes from cache and the build
# exits 0 in under a second having executed nothing. Hence the two
# --no-cache-filter flags.
#
# 2. BuildKit silently ignores a --no-cache-filter naming a stage that
# does not exist, so a rename restores that green no-op.
#
# 3. BuildKit builds only the final stage's dependency graph. A stage
# nothing references is never built, and with no --target the final
# stage is whichever is last in the file — so appending a stage, or
# moving a reference into a stage that is itself unreachable, drops
# the checks out of the build while every reference to them is still
# there to read.
#
# Rather than predict any of that from the Dockerfile's text, this asks
# the finished build what it did: script/assert-step-ran requires that
# the log contain a lint step and a test step that ran, and that each
# wrote the line its own tool writes on success. Every trap above ends
# with the step absent from the log or served from cache, and both fail
# that assertion. See that script for what it does not cover.
#
# 4. A step that ran is not a step that saw the repo. .dockerignore, or
# a COPY narrower than the tree, removes files from the build
# context, and the linter then reports `0 issues.` over what is left
# while `go test ./...` never compiles the package that went missing.
# So each source-consuming stage inventories what reached it, and
# script/assert-context-complete compares that inventory against the
# git index — a source .dockerignore cannot reach.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
dockerfile=Dockerfile
lint_stage=lint
test_stage=builder
# `ok <pkg> 1.234s`: go test prints `(cached)` in place of the duration
# when it replays a result, so requiring the duration requires a package
# that was really exercised.
test_ran='^ok[[:space:]]+[^[:space:]]+[[:space:]]+[0-9]+[.][0-9]+s'
main() {
cd "$ROOT"
tmp="$(mktemp -d "${TMPDIR:-/tmp}/$("$SCRIPT_DIR/projectname")-cibuild.XXXXXX")"
trap 'rm -rf "$tmp"' EXIT INT TERM
# --progress=plain is load-bearing: the assertions below read the
# steps' own output out of this log, and the tty renderer discards
# all but the tail of it. The exit status travels through a file
# because a pipeline's status is the last command's; `set +e` is
# what lets the recording line run at all, since errexit would
# otherwise abandon the subshell on a failing build. A missing file
# reads as failure.
(
set +e
docker build \
--progress=plain \
--no-cache-filter="$lint_stage" \
--no-cache-filter="$test_stage" \
-f "$dockerfile" . 2>&1
echo "$?" >"$tmp/status"
) | tee "$tmp/build.log"
status="$(cat "$tmp/status" 2>/dev/null || echo 1)"
[ "${status:-1}" -eq 0 ] || exit "${status:-1}"
script/assert-step-ran "$tmp/build.log" "$lint_stage" \
'^RUN golangci-lint run' '^0 issues[.]$' 'the linter'
script/assert-step-ran "$tmp/build.log" "$test_stage" \
'^RUN go test' "$test_ran" 'the tests'
# Both stages, separately: each has its own COPY, so an intact
# context in one is no evidence about the other.
script/repo-source-manifest >"$tmp/expected"
script/assert-context-complete "$tmp/build.log" "$lint_stage" "$tmp/expected"
script/assert-context-complete "$tmp/build.log" "$test_stage" "$tmp/expected"
}
main "$@"