diff --git a/README.md b/README.md index a6af79a..9f17e30 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,6 @@ expensive. `scan` maintains a persistent SQLite database of file signatures that survives between runs, so it can be run from cron and the reports can be generated at any time from the most recent scan. -This tool was created by [@sneak](https://sneak.berlin) to scratch an itch, -using Claude Code/Fable. - This README is the complete and authoritative specification. ## Getting Started @@ -283,7 +280,7 @@ the workers. files seen this run broken down by disposition, plus skips: ``` -scan: 123456 files seen (1200 added, 34 updated, 56 removed, 122166 unchanged), 3 skipped +scan: 123400 files seen (1200 added, 34 updated, 56 removed, 122166 unchanged), 3 skipped ``` (`removed` counts deleted database records, which are not part of the @@ -443,12 +440,80 @@ Additional requirements: - `2`: usage error (including `scan` with no `PATH` operand and `report`/`trees` with any positional argument). +## Entrypoints + +This repository adheres to the +[Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all) +standard: the normalized executables in `script/` are the entrypoints +for the development workflow, and the `Makefile` targets are thin +shims that call them. Every script is POSIX `sh`, resolves the +repository root itself so it can be run from any working directory, +and may be invoked directly. The provided entrypoints are: + +- `script/bootstrap` — install everything needed to build and + develop this repository, idempotently, assuming nothing is + present. `git`, `make`, and `go` come from the first of nix, apt, + brew, or apk found on the host, and are presence-checked only. + `golangci-lint` is treated differently: it is checked against the + version pinned in the script (the version the `Dockerfile` lint + stage runs) and reinstalled with `go install` whenever the + installed version differs — older or newer, not merely absent — + because a host on any other version lints against different rules + than CI. After installing, the script verifies the pin against the + `golangci-lint` that `PATH` actually resolves; if a different copy + shadows the install, bootstrap fails, naming both the install + directory and the shadowing binary, rather than reporting a + success the gate would not honour. Ends with `go mod download`. +- `script/setup` — make a fresh clone ready for development: runs + `script/bootstrap`, then `script/install-precommit`. +- `script/projectname` — print this project's name (`sfdupes`). + Scripts that need the name call it, so they stay identical across + repositories. +- `script/test` — run the test suite with a 30-second timeout and + coverage enabled, rerunning verbosely on failure so the logs show + which test failed. +- `script/lint` — run `golangci-lint` over the module with the + repository's `.golangci.yml`. +- `script/fmt` — format the Go sources in place (`gofmt -s -w`). + Markdown is not formatted. +- `script/fmt-check` — the read-only counterpart of `script/fmt`: + prints any unformatted file and exits non-zero instead of writing. +- `script/check` — run `script/test`, `script/lint`, and + `script/fmt-check`, in that order. Modifies nothing. +- `script/docker` — build the Docker image, tagged with the name + from `script/projectname`. The `Dockerfile` runs the gates as + build steps, so this is also the check a developer or reviewer + runs by hand. +- `script/cibuild` — build the Docker image untagged. This is what + the Gitea workflow runs on push; because the gates run as build + steps, a successful build implies the repository is green. +- `script/precommit` — run by the git pre-commit hook: `go mod tidy` + must be a no-op (a resulting change to `go.mod` or `go.sum` fails + the commit), then `script/check`. +- `script/install-precommit` — install the git pre-commit hook that + runs `script/precommit`. The hook is written to the common git + directory, so the main checkout and every worktree share it. + +`script/docker` and `script/cibuild` both pass a freshly computed +`CHECK_EPOCH` build argument, and the `Dockerfile`'s gate steps +reference it. Without that, an unchanged tree lets Docker serve the +gate layers from cache and the build exits 0 having executed no tests +and no lint — a green it never earned. `CHECK_EPOCH` invalidates the +gate layers on every run while leaving the pinned base images and the +dependency layers cached. + ## Build -The `Makefile` is the single source of truth for all operations: +The `script/` entrypoints above are where the implementations live; +the `Makefile` targets are shims onto them, except `build`, which +carries the compile recipe: - `make` / `make build` — build the `sfdupes` binary (cgo disabled); building is the default target. +- `make bootstrap` — install the build and development + dependencies. +- `make setup` — prepare a fresh clone: `bootstrap` plus the + pre-commit hook. - `make test` — run the test suite (30-second timeout; reruns with `-v` on failure). - `make lint` — run `golangci-lint` with the repo config. @@ -458,7 +523,7 @@ The `Makefile` is the single source of truth for all operations: - `make docker` — build the Docker image, which runs `make check` as a build stage. - `make hooks` — install the pre-commit hook. -- `make clean` — remove the binary and any legacy local `files.dat`. +- `make clean` — remove the binary. ### Definition of done