Skip to content

ci: add CI floor gating the deployed app/ tree (+ evidence on the duplicate app trees) - #4

Merged
catomean merged 1 commit into
mainfrom
ci/floor-on-deployed-app
Aug 2, 2026
Merged

ci: add CI floor gating the deployed app/ tree (+ evidence on the duplicate app trees)#4
catomean merged 1 commit into
mainfrom
ci/floor-on-deployed-app

Conversation

@catomean

@catomean catomean commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

What

main had no CI at all (.github/workflows 404s). This adds the golden npm floor from dotfiles/templates/ci/ci-npm.yml, wired to the tree that actually ships.

First: which tree is production?

main carries two complete Next.js apps with identical file sets — root src/ and app/src/. Before adding CI I had to establish which one is live. It is app/, not the root.

# Evidence Source
1 Deploy registry pins APP_DIR=appdeploy.sh runs npm run build in app/ and rsyncs app/.next/standalone fleetcrown/scripts/hetzner/apps.conf:17printcraft|4015|printcraft.orangecat.ch|/home/g/dev/printcraft|app|-
2 Only app/next.config.ts has output: "standalone". deploy.sh hard-aborts with "no standalone output" without it — the root tree cannot be what deploys. Root has never had it (checked at db5e5ef, the commit the live build came from). repo + deploy.sh
3 The box runs a standalone bundle: /opt/printcraft/app/server.js + .next/. Its app-path-routes-manifest.json (21 routes) matches an app/ build exactly — I rebuilt app/ locally and got the identical route list. ssh root@167.233.22.31
4 Only app/src/lib/supabase/{client,server,admin}.ts carry the June self-hosted migration (db: { schema: 'printcraft' }, supabase.orangecat.ch). Root src/ still points at the dead managed project ckpynkpsfnuqndplaapc.supabase.co. The box's .env is NEXT_PUBLIC_SUPABASE_URL=https://supabase.orangecat.ch, and the deployed chunks contain both that host and the printcraft schema. repo + box
5 app/ has commits through 2026-07-22. Root src/ is frozen at 2026-03-28 (45e1a38) and only re-entered main via the unrelated-history merge e657365 (2026-07-17), which unioned the original create-next-app repo back on top of the maintained lineage. git log
6 CLAUDE.md's own "File Structure" section documents app/src/... as the layout. repo

The two src trees have the same file set; app/src is the strict superset in content (it has the self-host migration plus the react-hooks/set-state-in-effect fix from 3da51e5). Root src/ is a frozen ancestor copy.

⚠️ This means PR #2 is dangerous

#2 deletes app/ (−17,483 lines, 90 of its 101 files under app/) 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 already CONFLICTING. It should be closed, not rebased. I have not touched it.

What this PR adds

app/package.json

  • typecheck: tsc --noEmit
  • verify: npm run lint && npm run typecheck

No test in verify — 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's lint + typecheck + test.

.github/workflows/ci.yml — golden template, checkout@v7 / setup-node@v7, concurrency cancel-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 what deploy.sh actually builds the standalone bundle on. The box only runs the prebuilt artifact (it has node 20.20.2).
  • An extra "Deploy contract" step asserting .next/standalone/**/server.js exists after the build. If output: "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 that apps.conf is the SSOT for APP_DIR), so this ambiguity does not cost the next reader — or the next agent — another investigation.

Verified locally

Run from a clean npm ci in app/, exactly as CI runs them:

Command Result
npm run verify exit 0 — lint: 0 errors, 11 warnings; typecheck: 0 errors
npm run build (placeholder env) exit 0 — 19 routes, matches production's manifest
standalone contract OK.next/standalone/app/server.js present

Caveat: verified on node 22 locally (only version available here); CI pins 22 to match, so this should hold.

Deliberately NOT done

  • Nothing deleted. The stale root tree (src/, root package.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.
  • No runtime/app code touched. Zero behaviour change; this cannot affect the live site.
  • 11 lint warnings (unused vars, <img> vs next/image) left non-blocking.
  • No test gate, no verify added to the stale root package.json (that would define "verified" twice).

Also worth knowing

Production is stale relative to main: the live build is from 2026-06-12 (db5e5ef), but main has four later commits (0f74a68, e475b7f, 3da51e5, 03dc036 — including a build-blocking lint fix). printcraft.orangecat.ch returns 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

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>
@catomean
catomean merged commit f48fe3c into main Aug 2, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant