diff --git a/prompts/REPO_POLICIES.md b/prompts/REPO_POLICIES.md index f952f04..1e95840 100644 --- a/prompts/REPO_POLICIES.md +++ b/prompts/REPO_POLICIES.md @@ -73,11 +73,15 @@ possible: - Never force-push to `main`. - Never use `git add -A` or `git add .`. Always stage files explicitly by name. - **No build artifacts in version control.** Code-derived data must never be - committed if it can be avoided: compiled bundles, minified output, generated - assets. The build process (e.g. Dockerfile, Makefile) should generate these at - build time. Notable exception: Go protobuf generated files (`.pb.go`) ARE - committed, because repos need to work with `go get`, which downloads code but - does not execute code generation. + committed to the repository if it can be avoided. Code-derived data includes: + - compiled bundles + - minified output + - generated assets + + The build process (e.g. Dockerfile, Makefile) should generate these at build + time. Notable exception: Go protobuf generated files (`.pb.go`) ARE + committed, because repos need to work with `go get`, which downloads code + but does not execute code generation. # Entrypoints @@ -222,8 +226,9 @@ present. Use `yarn`, not `npm`. - `script/bootstrap` installs node, yarn and the node packages only when - `yarn.lock` is in the tree. A `Dockerfile` stage that needs no yarn therefore - copies no `yarn.lock`. The Go and Python stages below are such stages. + `yarn.lock` is in the tree. A `Dockerfile` stage that needs no yarn copies no + `yarn.lock` before it runs `script/bootstrap`, so `script/bootstrap` installs + no node and no yarn there. The Go and Python stages below are such stages. - It installs the node packages with `yarn install --immutable`, which fails when a package's `checksum` is wrong, or when `package.json` is out of step with `yarn.lock`. @@ -266,8 +271,14 @@ what the tool even is. The canonical form: - compares the installed version against the pin over the **whole** version token. A parser that stops at the first `-` reports `2.12.2` for a host running `2.12.2-rc1` and skips the install. -- treats `--version` output that is absent, non-zero, empty or unrecognised as a - mismatch. A failure then causes a redundant install, never a skipped one. +- treats `--version` output as a mismatch when it is any of these: + - absent + - non-zero + - empty + - unrecognised + + So a failure causes a redundant install, never a skipped one. + - after installing, re-resolves the binary the way callers do: `hash -r`, then `PATH`, not the directory the installer wrote to. A shadowing binary can hide an install that reported success. In that case it fails and names the resolved @@ -305,7 +316,8 @@ docker build --build-arg CACHEBUST="$(date +%s)-$$" \ two gates are always invoked by name here, and why the final stage carries a `COPY --from=` of a harmless file from each of them. Without those copies, a plain `docker build .` builds the last stage's chain alone and exits 0 having - linted and tested nothing. Keep the final stage last. If you add a stage after + linted and tested nothing. Keep the stage that ships last: the runtime image, + or the development environment in a non-server repo. If you add a stage after it, give it the same two copies. - **The gate builds write no image.** With `--output type=cacheonly` the phase runs and a failing step fails the build, but the result is not exported. @@ -368,8 +380,12 @@ step that also installs anything else comes after `ARG CACHEBUST` too. A `script/bootstrap` that also installs node packages is one. - 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. + by content and need nothing. Docker keeps the layers above the line until one + of these changes: + - the base image + - their own lines + - the files they copy + - A stage without the `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 not pass `--no-cache`. That flag reruns every step too, but it @@ -545,17 +561,26 @@ Key points: 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. +that `RUN` installed until one of these changes: + +- the base image +- `script/` +- `Pipfile` +- `Pipfile.lock` +- a line up to and including the `RUN` The tests run after `ARG CACHEBUST`, on every check build. In every other stage, a step that installs Python packages comes after `ARG CACHEBUST`. In that step, `script/bootstrap`: -- installs nothing beyond system packages, pipenv and its dependencies, and the - packages from `Pipfile.lock`. If it installs anything else there, the step - goes after `ARG CACHEBUST`. +- installs nothing beyond these: + - system packages + - pipenv and its dependencies + - the packages from `Pipfile.lock` + + If it installs anything else there, the step goes after `ARG CACHEBUST`. + - installs no Python: the tests use the distribution's Python, which a step above it installs. - puts pipenv in a virtual environment at `$HOME/.pipenv`, because Debian's @@ -569,9 +594,11 @@ In that step, `script/bootstrap`: that lists each of them with `==` and its `--hash`. pip refuses to install a dependency the file does not list. - 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`. + development packages. `--deploy` does all of these: + - installs only what `Pipfile.lock` names + - checks every package against the lockfile's hashes + - fails when the lockfile is out of date with `Pipfile` + - sets `PIP_ONLY_BINARY=:all:` for both installs, and pipenv passes it on to pip. A package with no ready-built wheel for the image then stops the install. Without the setting, pip builds that package from source and downloads the @@ -674,8 +701,9 @@ canonical Go `Dockerfile` above shows: stream keeps the sender's file owners, and git refuses a checkout owned by another user, so the version would come out empty. - The build fails if the context carries `.git` and the version still comes out - empty, `dev` or `unknown`. Otherwise a missing `git` or an unreadable checkout - would leave `-X main.Version=` empty, and the build would still exit 0. + empty, `dev` or `unknown`. Without that check, a missing `git` or an + unreadable checkout would leave `-X main.Version=` empty, and the build would + still exit 0. - A plain `docker build .` with no build arguments must succeed; a Dockerfile that refuses an empty build argument drops that refusal and keeps the argument. @@ -693,11 +721,11 @@ canonical Go `Dockerfile` above shows: - that of a submodule keeping its own `.git` directory `git describe` does not need them, and each can 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 (`config`, `deploy/config`, `config/lib`) loses its -whole git directory to `**/.git/modules/**/config`, and Go's version stamping -then fails the build. Give it a name without that segment -(`git submodule add --name`). +a remote URL, or the token the CI checkout step stores there. Some submodule +names have a `config` segment, as in `config`, `deploy/config` or `config/lib`. +Such a submodule loses its whole git directory to `**/.git/modules/**/config`, +and Go's version stamping then fails the build. Give it a name without that +segment (`git submodule add --name`). `script/docker` and `script/cibuild` pass the version they compute on the host; it takes precedence. They do this byte-identically across repos: @@ -727,7 +755,7 @@ docker build --build-arg CACHEBUST="$(date +%s)-$$" \ Every repo should have a Gitea Actions workflow (`.gitea/workflows/`) that runs `script/cibuild` on push. The workflow's only other step checks out the repo. -The canonical `.gitea/workflows/check.yml` sets these: +The workflow sets these: - `persist-credentials: false` on the checkout step. `script/cibuild` needs no token, and without that setting the checkout leaves the job's token in @@ -950,8 +978,14 @@ including, without limitation, all of the following: to an explicit allowlist of known origins. Wildcard (`*`) is acceptable only for public, unauthenticated read-only APIs. - **Error handling:** Internal errors must never leak implementation details to - the client: stack traces, SQL queries, file paths. Return generic error - messages in production; detailed errors only when `DEBUG` is enabled. + the client. Those details include: + - stack traces + - SQL queries + - file paths + + Return generic error messages in production; detailed errors only when + `DEBUG` is enabled. + - **TLS:** Services never terminate TLS directly. They are always deployed behind a TLS-terminating reverse proxy. The service itself listens on plain HTTP. However, HSTS headers and `Secure` cookie flags must still be set by the @@ -977,10 +1011,17 @@ the binary. `README.md` is the primary documentation. Required sections: -- **Description**: First line must include the project name, purpose, category - (web server, SPA, CLI tool, etc.), license and author. Example: "µPaaS is an - MIT-licensed Go web application by @sneak that receives git-frontend webhooks - and deploys applications via Docker in realtime." +- **Description**: First line must include: + - the project name + - purpose + - category (web server, SPA, CLI tool, etc.) + - license + - author + + Example: "µPaaS is an MIT-licensed Go web application by @sneak that + receives git-frontend webhooks and deploys applications via Docker in + realtime." + - **Getting Started**: Copy-pasteable install/usage code block. - **Entrypoints**: Opens by stating that the repo adheres to the [Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all) @@ -998,10 +1039,19 @@ the binary. # Repository layout Avoid putting files in the repo root unless necessary. Root should contain only -project-level config files: `README.md`, `AGENTS.md`, `Makefile`, `Dockerfile`, -`LICENSE`, `.gitignore`, `.editorconfig`, `REPO_POLICIES.md` and -language-specific config. Everything else goes in a subdirectory. Canonical -subdirectory names: +project-level config files: + +- `README.md` +- `AGENTS.md` +- `Makefile` +- `Dockerfile` +- `LICENSE` +- `.gitignore` +- `.editorconfig` +- `REPO_POLICIES.md` +- language-specific config + +Everything else goes in a subdirectory. Canonical subdirectory names: - `bin/` — executable scripts and tools - `cmd/` — Go command entrypoints; thin only: one `main.go` per binary whose