mirror of
https://github.com/tiennm99/noitu.git
synced 2026-10-11 03:13:45 +00:00
7.7 KiB
7.7 KiB
Server core, packaging, CI and deployment fixes
Date: 2026-09-29, branch dev, nothing committed.
Changes per finding
Server core review:
- 1 (shutdown order).
serve()inserver/cmd/noitu-server/main.gonow builds the wsapi server oncontext.Background(). The signal context is only the "stop now" trigger, so rooms and sessions survive untilStartDraininghas run and the drain has waited. The sequence lives indrainAndShutdown(drain, wait up toNOITU_DRAIN_TIMEOUT,Shutdown, then a bounded 2s flush).run()was split intorun(store, listener, signal context) andserveso the sequence is testable. - 4 (second signal).
servecalls the signal context'sstopas soon as the context fires, so a second SIGTERM/SIGINT kills the process. - Shutdown flush. After
api.Shutdown()the process sleepsshutdownFlush(2s). wsapi exposes no live-session count, so this is a fixed bound, as the brief allowed. - 2 (Hard, equal-score losses).
negamaxreturnsloseScore - depth. The doc comment onloseScoreexplains it. - 5 (out-of-turn resign).
Engine.Resignonly callssettle()(and restarts the clock) when the turn moved. The README was right, so only code changed. - 6 (
<ref name="a/b"/>).refElementinwikitext.goreads quoted attribute values whole. - 7 (builder version). The store now reads
builder_versioninloadMetaand refuses a missing or different value, naming the version found. The constant is exported asdictionary.RequiredBuilderVersion, and the builder'sbuilderVeris now that same constant, so the two cannot drift. Test fixtures write the row. - 8 (IdleTimeout).
newHTTPServersetsIdleTimeout: 120s(with the existingReadHeaderTimeout) for both the public and debug listeners. No Read/WriteTimeout. - 9 (real-corpus seed).
playRealGamenow draws openings from a PCG seeded byseed, through a newStore.RandomOpeningWordFrom(rng, min)(RandomOpeningWorddelegates to the same helper). - 10 (syllable nits). Dropped the
nghcoda, the duplicateaoandeunuclei, and the redundant0x031Bclause. - 3. Belongs to wsapi. Not touched.
Security and ops review:
- M1. New "Coolify and Traefik" section in
docs/deployment.md. It gives the exact env vars and what each changes (NOITU_TRUSTED_PROXIESas the Traefik subnet,NOITU_MAX_CONNECTIONS_PER_IP=32as a starting point,NOITU_DRAIN_TIMEOUT). It also covers Cloudflare (TraefikforwardedHeaders.trustedIPs) and a Traefik/readyzload-balancer health check label. - M2. Added
noitu-server -healthcheck(checkHealth: GET/healthzonNOITU_ADDR, wildcard or bare-port host dialled on 127.0.0.1, exit 0 on 200, else 1 with the reason on stderr). Added a DockerfileHEALTHCHECK(interval 30s, timeout 5s, start-period 10s, retries 3). The docs explain how Coolify uses it and state the stop-grace rule:NOITU_DRAIN_TIMEOUT + 2s < container stop grace, so at most about 6s with Docker's 10s default. Also documented that a second signal ends the process. - L4.
proto.ymlnow usesbufbuild/buf-action@v1withsetup_only: true. I confirmedsetup_onlyin the action'saction.yml. I droppedversion: latest: theversioninput is optional and I could not confirm thatlatestis a valid value, so an unset version is the safer way to get the newest buf. - L5.
COPY LICENSE /app/LICENSEin the Dockerfile, andapp/LICENSEadded to the CI "licence travels with the data" check. Docs note the licence file now ships. The third-party notices gap is accepted while the image is unpublished (recorded indeployment.mdand here; no code change). The web footer link is the web agent's job. - L6.
go-version: stablein everysetup-gostep (ci.yml x2, proto.yml), withgo.modleft as the minimum. Addedgo run golang.org/x/vuln/cmd/govulncheck@latest ./...to the Go job andnpm audit --omit=dev --audit-level=highto the web job. Moving major tags only, no SHA pins. - L8. Covered with finding 8.
- N2. Documented only, in the Observability section.
- N3.
.dockerignorenow excludes.claude,**/.claude,.env*and**/.env*. - N4.
persist-credentials: falseon all five checkout steps. - I also added
docker exec noitu /app/noitu-server -healthcheckto the CI image job, so the HEALTHCHECK command is exercised.
Skipped
- H1, L1, L2, L3, N1: wsapi agent.
- M3, L7: GitHub settings, manual (below).
- D1, D2, N5, N6: non-issues or optional, per the brief.
- Web footer link for the modification record (L5 item 3): web agent.
Verification
go vet ./...clean,gofmt -l .empty,golangci-lint run ./...0 issues.go test ./... -race -count=1: every package passes. The first full run had one wsapi failure (TestFrameFloodClosesTheConnection) while the other agent was mid-edit. A single rerun of./internal/wsapipassed.- Tests that fail without their fix (I reverted each fix and confirmed the failure):
TestServeDrainsLiveGamesBeforeShuttingDowndrivesserveover a real WebSocket bot game. With the signal context passed toNewServerit fails with EOF anddraining rooms=0 live_games=0.TestResignOutOfTurnDoesNotSettleAPendingDeadEndTestHardPrefersTheSlowerLossWhenEveryLineLoses- the new
TestStripWikitextcase TestOpenRefusesAMismatchedBuilderVersion
- Tests added that do not depend on a reverted fix:
TestDrainAndShutdownOrdersDrainBeforeShutdownTestServersSetOnlyAnIdleTimeoutTestCheckHealthTestRandomOpeningWordFromIsReproducibleTestOpenRefusesADatabaseWithNoBuilderVersion
- Docker is available.
docker build --build-arg FIXTURE_DICT=1 -t noitu:review .succeeded. In the running container,docker exec ... -healthcheckreturned 0, the container becamehealthy,app/LICENSEwas in the image, anddocker stopproduceddrainingthenshutting downlog lines and exited in 2.3s. I removed the test container and image afterwards. make helpstill lists all 14 lines. Workflow YAML parses.- The real-corpus ladder test is skipped here (no real dictionary), so the effect of the negamax change on it was not re-measured.
Manual steps for the maintainer
Coolify (app "noitu", currently zero env vars, health check off):
- Run
docker network inspect coolifyon the host and note the subnet Traefik reaches the app on (or the app's own network). - Set
NOITU_TRUSTED_PROXIES=<that CIDR>andNOITU_MAX_CONNECTIONS_PER_IP=32. - Set
NOITU_DRAIN_TIMEOUTbelow the container's stop grace minus 2s. With Docker's 10s default that means at most6s. Check what Coolify's stop timeout actually is before going higher. - Leave the dashboard HTTP health check off. After the next deploy, confirm Coolify picks up the Dockerfile
HEALTHCHECK. - Optional: add the Traefik
loadbalancer.healthcheck.path=/readyzcustom label, using the generated service name. - If Cloudflare is in front, configure Traefik
forwardedHeaders.trustedIPsfor its ranges.
GitHub:
- M3: merge
devintomain, or cherry-pick.github/dependabot.yml. Enable Dependabot alerts and security updates, secret scanning and push protection. Then accept the action major bumps Dependabot proposes. - L7: Settings, Actions, Workflow permissions: set read-only and untick "Allow GitHub Actions to create and approve pull requests".
Unresolved questions
- The stop grace Coolify applies to this container is unverified. The docs state the rule and Docker's default only.
- The Coolify claims (Dockerfile
HEALTHCHECKhonoured,docker network inspect coolify) come from the review reports and Coolify's public behaviour, not from a deploy. The docs say to confirm after the first deploy. buf-actionwith noversioninput is assumed to resolve to the newest buf. The first CI run onproto.ymlwill confirm it.