Unify the gate: root make check must cover the backend, and CI must route through script/
#16
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The repo has two disconnected build systems, and the root gate does not actually gate the whole repo. Verified on
mainatfbfe1df.1. Root
make checkignores the backend entirelyMakefilecheckshims toscript/check, which runs the frontendyarn buildplusprettier --check. It never touchesbackend/. Confirmed by running it: rootmake checkpasses while exercising zero Go code.This means the "
mainmust always passmake check, no exceptions" policy is being satisfied vacuously — the Go backend could be entirely broken and the root gate would stay green. Anyone (or any pre-commit hook) runningmake checkat the root gets false assurance.2. The backend is not on scripts-to-rule-them-all
REPO_POLICIES.mdrequires that "the implementation of each Makefile target lives in an executable script inscript/... and the Makefile targets are thin shims that call them."backend/Makefileis a full standalone Makefile with inline recipes (go build,go test,golangci-lint run,gofmt) and noscript/indirection. It also violates the "always use Makefile targets instead of invoking the underlying tools directly" rule at the layer below.3. Two competing pre-commit hook installers, one clobbers the other
script/install-precommit(shimmed by rootmake hooks) installs a hook runningscript/precommit.backend/Makefile'shookstarget writes.git/hooks/pre-commitwith the literal bodycd backend && make check.Both target the same single
.git/hooks/pre-commitpath. Whichever ran last wins, so the developer silently ends up gating on only one half of the repo. There must be exactly one hook installer, and the hook must gate the whole repo.4. CI invokes
docker builddirectly instead of throughscript/.gitea/workflows/check.yml:The second step is a raw tool invocation, bypassing the
script/layer that is supposed to be the single source of truth for how the repo builds. Policy: the workflow runsscript/cibuild;script/cibuildruns the docker build(s).Definition of done
make checkfails if either the frontend or the backend is broken. Demonstrate this: deliberately break a Go file, confirm rootmake checkfails, revert.script/entrypoints, and any retainedbackend/Makefiletargets are thin shims. Whether that means extending the rootscript/*files to cover both halves or addingbackend/script/*is an implementation choice — pick one, apply it consistently, and document the choice in the PR description.script/cibuildbuilds both images (frontendDockerfileandDockerfile.backend)..gitea/workflows/check.ymlcontains exactly one build step:- run: script/cibuild. No rawdocker buildanywhere in the workflow.backend/Makefile'shookstarget is removed.make hooksat the root installs a hook whosescript/precommitrun gates the whole repo (frontend and backend).make checkpasses at the root, and covers the Go code.script/cibuildsucceeds locally.make checkdoes not modify any tracked file (policy: "make checkmust not modify any files in the repo"). Verifygit status --shortis clean afterwards.TODO.mdupdated in the same commit.(closes #N).Implementation requirements
script/files must be POSIX sh (#!/bin/sh,set -eu, no bashisms), locate the repo root with$(cd "$(dirname "$0")/.." && pwd -P), andcdthere before acting.script/projectnameand any other scripts that policy says stay byte-identical across repos unchanged..golangci.ymlhere — that is issue #14. If #14 has already landed, just do not regress it.make testflag changes (-race,-cover, conditional verbose rerun) — that is a separate issue.make testruntime must stay under 20 seconds with a 30-second timeout enforced; docker builds must stay under 5 minutes.maketargets andscript/entrypoints only — never rawgo,yarn,gofmt, orgolangci-lint.Implementation plan
Branching from
mainatfbfe1df.1. Where the backend's implementations go:
backend/script/*.The alternative was extending the root
script/*files to shell out intobackend/.Dockerfile.backendsettles it: its builder doesWORKDIR /repo/backend,COPY backend/ ., and thenRUN make check. Theroot
script/directory is never copied into that image, so if the backend'scheck implementation lived in root
script/*the backend image could not runit without restructuring the Dockerfile and its layer caching. The backend is
also its own project already (own module,
README.md,LICENSE,.golangci.yml,.dockerignore), so it gets its own script layer:backend/script/{build,test,lint,fmt,fmt-check,check,run,clean}, withbackend/Makefilereduced to thin shims. Each script uses the same$(cd "$(dirname "$0")/.." && pwd -P)root discovery; for these, that root isthe backend project root.
2. Root scripts compose over both halves.
The frontend-only steps move to
script/frontend-{test,lint,fmt,fmt-check},and root
script/test,script/lint,script/fmtandscript/fmt-checkeach run the frontend step and then the matching
backend/script/*step.script/checkis unchanged in shape (test, lint, fmt-check) and therefore nowcovers the Go backend, which also means
script/precommitand the installedhook cover it.
3. The frontend Dockerfile.
Its build stage is a node image with no Go toolchain, so it cannot run the
whole
make check. It getsmake check-frontend, shimmed toscript/frontend-check(the frontend half of the gate) — identical coverageto what that image gates today.
make check-backendis added as the mirror.The two Dockerfiles together still gate the whole repo, and
script/cibuildbuilds both.
4. CI and docker.
script/cibuildbuilds both images through a singlebuild_imagehelper, sothe workflow's only build step becomes
- run: script/cibuild.script/dockerlikewise builds and tags both (
netwatch,netwatch-server).backend/Makefile'sdockertarget goes away:Dockerfile.backendlives atthe repo root and builds with the repo root as its context, so it belongs to
the root script layer rather than to a backend script that would have to reach
outside
backend/.5. Hooks.
backend/Makefile'shookstarget is deleted.script/install-precommitbecomes the only installer, and the hook it writes runs the repo-wide
script/check.6. Interaction with #14 / PR #31.
PR #31 is open and unmerged; it adds a sha256 drift guard for
.golangci.ymltobackend/Makefile'slinttarget. That guard moves withthe lint implementation into
backend/script/linthere, parameterized by aGOLANGCI_CONFIG_SHA256constant, pinned to the config as it exists onmainso this branch stays green. Whichever of the two lands second updates that one
constant.
.golangci.ymlitself is not touched here.Verification I will run: deliberately break a Go file and show root
make checkfails on this branch while it passes onmainwith the identicalbreak; the same for a Go formatting break and for the drift guard;
script/cibuildend to end; and the installed hook rejecting a broken commitin each half.
git status --shortclean aftermake check.