ci: add CI floor gating the deployed app/ tree (+ evidence on the duplicate app trees) - #4
Merged
Merged
Conversation
printcraft had no CI at all on main. Add the golden npm floor
(dotfiles/templates/ci/ci-npm.yml) wired to the tree that actually ships.
Which tree ships is not obvious — main carries two complete Next.js apps
(root `src/` and `app/src/`) with identical file sets. Evidence that `app/`
is production:
1. fleetcrown/scripts/hetzner/apps.conf line 17 pins APP_DIR=app, so
deploy.sh builds in `app/` and rsyncs `app/.next/standalone`.
2. Only `app/next.config.ts` sets output:"standalone"; deploy.sh aborts
without it. The root config has never had it.
3. The box runs a standalone bundle (/opt/printcraft/app/server.js) whose
route manifest matches `app/src/app` exactly.
4. Only `app/src/lib/supabase/*` carries the self-hosted migration
(db:{schema:'printcraft'}, supabase.orangecat.ch). Root still targets
the dead managed project ckpynkpsfnuqndplaapc.supabase.co.
5. `app/` has commits through 2026-07-22; root `src/` is frozen at
2026-03-28 and re-entered main only via the unrelated-history merge
e657365 (2026-07-17).
So:
- app/package.json: `typecheck` (tsc --noEmit) and `verify`
(lint && typecheck). No `test` — the repo has no test suite.
- .github/workflows/ci.yml: checkout@v7 / setup-node@v7 (node 22, the
version deploy.sh builds on), working-directory app, npm ci ->
npm run verify -> npm run build -> assert the standalone bundle exists,
so a lost output:"standalone" fails CI instead of failing a deploy.
- README: document the layout so the stale root tree is not mistaken for
the live one (and vice versa).
Nothing is deleted and no runtime code changes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
mainhad no CI at all (.github/workflows404s). This adds the golden npm floor fromdotfiles/templates/ci/ci-npm.yml, wired to the tree that actually ships.First: which tree is production?
maincarries two complete Next.js apps with identical file sets — rootsrc/andapp/src/. Before adding CI I had to establish which one is live. It isapp/, not the root.APP_DIR=app→deploy.shrunsnpm run buildinapp/and rsyncsapp/.next/standalonefleetcrown/scripts/hetzner/apps.conf:17→printcraft|4015|printcraft.orangecat.ch|/home/g/dev/printcraft|app|-app/next.config.tshasoutput: "standalone".deploy.shhard-aborts with "no standalone output" without it — the root tree cannot be what deploys. Root has never had it (checked atdb5e5ef, the commit the live build came from).deploy.sh/opt/printcraft/app/server.js+.next/. Itsapp-path-routes-manifest.json(21 routes) matches anapp/build exactly — I rebuiltapp/locally and got the identical route list.ssh root@167.233.22.31app/src/lib/supabase/{client,server,admin}.tscarry the June self-hosted migration (db: { schema: 'printcraft' },supabase.orangecat.ch). Rootsrc/still points at the dead managed projectckpynkpsfnuqndplaapc.supabase.co. The box's.envisNEXT_PUBLIC_SUPABASE_URL=https://supabase.orangecat.ch, and the deployed chunks contain both that host and theprintcraftschema.app/has commits through 2026-07-22. Rootsrc/is frozen at 2026-03-28 (45e1a38) and only re-enteredmainvia the unrelated-history mergee657365(2026-07-17), which unioned the original create-next-app repo back on top of the maintained lineage.git logCLAUDE.md's own "File Structure" section documentsapp/src/...as the layout.The two
srctrees have the same file set;app/srcis the strict superset in content (it has the self-host migration plus thereact-hooks/set-state-in-effectfix from3da51e5). Rootsrc/is a frozen ancestor copy.#2 deletes
app/(−17,483 lines, 90 of its 101 files underapp/) on the stated premise "Root is canonical (README + deploy build from root)". That premise is wrong: merging it would delete the production application and the next deploy would fail at "no standalone output" (or, worse, ship the stale March tree pointed at a dead Supabase project). It is alreadyCONFLICTING. It should be closed, not rebased. I have not touched it.What this PR adds
app/package.jsontypecheck:tsc --noEmitverify:npm run lint && npm run typecheckNo
testinverify— this repo has no test suite, and inventing a gate it can't pass would be dishonest. That is the one deliberate deviation from the template'slint + typecheck + test..github/workflows/ci.yml— golden template,checkout@v7/setup-node@v7,concurrencycancel-in-progress, with three repo-specific adjustments:defaults.run.working-directory: app+cache-dependency-path: app/package-lock.json— CI gates the tree that ships.node-version: 22(template says 20) — 22 is whatdeploy.shactually builds the standalone bundle on. The box only runs the prebuilt artifact (it has node 20.20.2)..next/standalone/**/server.jsexists after the build. Ifoutput: "standalone"is ever lost, that now fails in CI instead of aborting a live deploy — closing the class rather than fixing the instance.The build step supplies fake, non-secret placeholder
NEXT_PUBLIC_SUPABASE_*values: they are baked at build time and the statically prerendered pages construct a Supabase browser client that throws on missing vars. No DB is contacted.README.md— documents the layout explicitly (which tree ships, which is stale, and thatapps.confis the SSOT forAPP_DIR), so this ambiguity does not cost the next reader — or the next agent — another investigation.Verified locally
Run from a clean
npm ciinapp/, exactly as CI runs them:npm run verifynpm run build(placeholder env).next/standalone/app/server.jspresentCaveat: verified on node 22 locally (only version available here); CI pins 22 to match, so this should hold.
Deliberately NOT done
src/, rootpackage.json/next.config.ts/tsconfig.json/eslint.config.mjs/postcss.config.mjs/components.json/package-lock.json) is still there. The evidence that it's dead is strong, but deleting ~90 files from a live repo is a separate, reviewable change and does not belong bundled with "add CI". It is now clearly labelled in the README; removing it should be its own PR.<img>vsnext/image) left non-blocking.testgate, noverifyadded to the stale rootpackage.json(that would define "verified" twice).Also worth knowing
Production is stale relative to
main: the live build is from 2026-06-12 (db5e5ef), butmainhas four later commits (0f74a68,e475b7f,3da51e5,03dc036— including a build-blocking lint fix).printcraft.orangecat.chreturns 200, so nothing is broken, but a redeploy is overdue. Deploy is manual (fleetcrown/scripts/hetzner/deploy.sh printcraft) — this PR adds CI only, no CD.🤖 Generated with Claude Code