All checks were successful
check / check (push) Successful in 23s
Per the owner ruling on issue 40, linting and testing are phases of the main Dockerfile rather than a separate lint file. script/lint and script/test build one phase each by name with caching disabled, and the final stage copies a harmless file from each so the image cannot be built unless both passed. A stage that is not the last is built only when something depends on it or --target names it, so the gates are invoked by name and the edges kept. script/check runs the gates and builds no image of its own; script/cibuild bootstraps first, because CI runs it alone and fmt-check is native. fmt and fmt-check source nvm for the pinned node before calling yarn, which bootstrap installs but leaves off its caller's PATH. Every build in script/ is tagged and uncached. Issue 30 closes too: a container has its own lint cache and lock. Model: opus-5
8.1 KiB
8.1 KiB
title, last_modified
| title | last_modified |
|---|---|
| New Repo Checklist | 2026-09-08 |
Use this checklist when creating a new repository from scratch. Follow the steps
in order. Full policies are at
https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md.
Template files can be fetched from:
https://git.eeqj.de/sneak/prompts/raw/branch/main/<path>
1. Initialize
git init- Ask the user for the license (MIT, GPL, or WTFPL)
2. First Commit (README only)
- Create
README.mdwith all required sections:- Description: name, purpose, category, license, author
- Getting Started: copy-pasteable code block
- Rationale: why does this exist?
- Design: how is it structured?
- TODO: initial task list
- License: matches chosen license
- Author: @sneak
git add README.md && git commit
3. Scaffolding (feature branch)
git checkout -b initial-scaffolding
Fetch Template Files
.gitignore— fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.gitignore, extend for language-specific artifacts. Extensions are written to.gitignore's own semantics, where an unanchored pattern already matches at every depth: never add a**/prefix here, which is a.dockerignoreform. The canonical file already carries.claude/so agent worktrees cannot be committed by accident..editorconfig— fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.editorconfigMakefile— fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/Makefile, adapt targets for the project's language and tools- For JS/docs repos:
.prettierrcand.prettierignore— fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrcandhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierignore
Create Project Files
LICENSEfile matching the chosen licenseREPO_POLICIES.md— fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.mdDockerfileand.dockerignore— fetch.dockerignorefromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.dockerignore- Extend
.dockerignorewith the repo's own host-built artifacts, giving every depth-independent pattern a**/prefix — but write a repo-root binary anchored,/myappand never**/myapp, which would also matchcmd/myapp/and delete the package directory. Do not transplant.gitignore's patterns:.dockerignoreanchors an unprefixed pattern at the context root, so the copied form leavesconfig/.envin the build context while reading as solved. The canonical file's.claudeentry is anchored for the same reason as a repo-root binary; leave it that way, but note that it only covers agents running at the repo root — if this repo will run them in subdirectories,services/api/.claude/needs its own anchored entry. - If the image embeds a version in a binary, the version is computed on the
host and passed with
--build-arg VERSION=..., andARG VERSION=devis declared in the stage that compiles. No stage callsgit describe—.dockerignoreexcludes.git, so it yields an empty version without failing the build. - The Dockerfile carries a
lintphase and atestphase, each invoking its tool directly rather than throughmakeorscript/, and the final stage carries aCOPY --from=of a harmless file from each so the image cannot be built unless both passed. Keep the final stage last: a stage nothing depends on is built only when--targetnames it. - Server: the final stage builds and runs the application
- Non-server: the final stage brings up the dev environment
- Image pinned by sha256 hash with version/date comment
- Extend
- Gitea Actions workflow at
.gitea/workflows/check.ymlthat runsscript/cibuildon push — referencehttps://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml - Language-specific:
- Go:
go mod init sneak.berlin/go/<name>,.golangci.yml(fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml) - JS:
yarn init,yarn add --dev prettier - Python:
pyproject.toml
- Go:
Configure script/ Entrypoints and Makefile
Implementations live in script/ (scripts-to-rule-them-all); Makefile targets
are thin shims calling them. Model scripts:
https://git.eeqj.de/sneak/prompts/raw/branch/main/script/<name>
- scripts are POSIX sh (
#!/bin/sh,set -eu, no bashisms) so they run on alpine images without bash script/bootstrap/make bootstrap— installs all dependencies, idempotently, assuming nothing (pkg manager detection nix/apt/brew/apk; node used if present, else pinned version via nvm from a hash-verified archive; pinned yarn via corepack); Dockerfile runs it instead of inline installsscript/setup/make setup— readies a fresh clone: runsbootstrap, theninstall-precommit, plus repo-specific initscript/test/make test—docker build --no-cache --target test ., tagged; the phase runs real tests, not a no-op (90-second timeout, 60-second hard cap on wall time)script/lint/make lint—docker build --no-cache --target lint ., tagged. No lint verdict may come from a host invocation of the linter.script/fmt/make fmt— formats code (writes; native, never in a container)script/fmt-check/make fmt-check— checks formatting (read-only; native)script/check/make check— runstest,lint,fmt-check; must not modify filesscript/projectname— outputs the project name (used byscript/dockerfor the image tag)script/docker/make docker— builds Docker image, tagged viascript/projectname(byte-identical across repos);--no-cache, plus the version as a build argscript/cibuild— cd to repo root, runscript/bootstrap, runscript/check, thendocker build --no-cache --build-arg VERSION="$version" .(what CI runs). The bootstrap is required: CI checks out and runs this alone, andscript/fmt-checkruns the formatter on the host.script/fmtandscript/fmt-checksource nvm for the pinned node version before invokingyarn, asscript/bootstrap's own install step does.script/bootstrapleaves the node and yarn it installs off thePATHof the shell that called it, so a bareyarnexits 127 on a runner carrying nothing but docker and git.- Every
docker buildinscript/is tagged, so no invocation leaves a dangling image behind script/precommit— called by the pre-commit hook; runsscript/checkscript/install-precommit— installs the pre-commit hook that runsscript/precommitmake hooks— shims toscript/install-precommit- README Entrypoints section documents the scripts and links the standard
4. Verify
make checkpassesmake dockersucceedsscript/cibuildsucceeds in a fresh clone on a host carrying nothing but docker and git, with no node or yarn onPATH, which is what CI has, and demonstrably executed the checks — a sub-second build, orCACHEDon a gate layer, means nothing ran- Plant a lint violation and confirm both
make lintand a plaindocker build .fail on it; revert. A plain build that passes proves the final stage is missing itsCOPY --from=edge to the gate phases. - No secrets in repo, and none in the build context: enumerate a probe image
rather than reading
.dockerignore - No mutable image/package references
- No unnecessary files in repo root
- All dates written as YYYY-MM-DD
5. Merge and Set Up
- Commit, merge to
main make hooksto install pre-commit hook- Add remote and push
- Verify
mainpassesmake check