From f10ee098936dac305c1aa7710aaa07d49b613497 Mon Sep 17 00:00:00 2001 From: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Date: Sun, 2 Aug 2026 20:23:50 +0200 Subject: [PATCH] ci: add CI floor gating the deployed app/ tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .github/workflows/ci.yml | 63 ++++++++++++++++++++++++++++++++++++++++ README.md | 16 ++++++++++ app/package.json | 4 ++- 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d9d1577 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,63 @@ +# CI floor — modelled on dotfiles/templates/ci/ci-npm.yml. +# +# NB: the deployed application is `app/`, NOT the repo root. The fleet deploy +# registry (fleetcrown/scripts/hetzner/apps.conf) pins +# printcraft|4015|printcraft.orangecat.ch|/home/g/dev/printcraft|app|- +# i.e. APP_DIR=app, so deploy.sh builds in `app/` and ships `app/.next/standalone`. +# The root-level Next tree (root `src/`, `next.config.ts`, `package.json`, …) is a +# stale March-2026 duplicate that nothing builds. CI gates the tree that ships. +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +# A newer push makes the older run obsolete — don't waste minutes finishing it. +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + working-directory: app + +jobs: + verify: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + # 22 = the version deploy.sh actually builds the standalone bundle on. + node-version: 22 + cache: npm + cache-dependency-path: app/package-lock.json + + - name: Install + run: npm ci + + # SSOT: "verified" is defined ONCE, in app/package.json `verify` + # (= lint + typecheck; this repo has no test suite), and run identically + # here and locally via `npm run verify`. Change the checks in one place. + - name: Verify + run: npm run verify + + - name: Build + # Hermetic compile. NEXT_PUBLIC_* are baked at build time and the static + # pages construct a Supabase browser client, which throws on missing + # vars — these are fake, non-secret placeholders. No DB is contacted. + env: + NEXT_PUBLIC_SUPABASE_URL: https://placeholder.supabase.co + NEXT_PUBLIC_SUPABASE_ANON_KEY: placeholder-anon-key + SUPABASE_SERVICE_ROLE_KEY: placeholder-service-role-key + run: npm run build + + - name: Deploy contract — standalone output exists + # deploy.sh aborts with "no standalone output" if next.config.ts ever + # loses `output: "standalone"`. Catch that here, not at deploy time. + run: test -f "$(find .next/standalone -maxdepth 4 -name server.js -not -path '*node_modules*' | head -1)" diff --git a/README.md b/README.md index d7a0ce5..f25b0d1 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,27 @@ Next.js application, self-hosted on Hetzner (bitbaum, behind Caddy) at https://printcraft.orangecat.ch. +## Repository layout + +**The application lives in `app/`, not at the repo root.** + +| Path | What it is | +| --- | --- | +| `app/` | **The deployed Next.js app.** Its own `package.json`, `src/`, `supabase/`. This is what CI gates and what the box runs. | +| `printcraft/`, `projects/`, `templates/`, `pyproject.toml` | The Python image-generation / compositor pipeline (see `PIPELINE.md`). | +| root `src/`, `next.config.ts`, `package.json`, `tsconfig.json`, `eslint.config.mjs`, `postcss.config.mjs`, `components.json`, `package-lock.json` | **Stale duplicate.** A frozen copy of the Next app from 2026-03-28, unioned back into `main` by the unrelated-history merge `e657365` (2026-07-17). Nothing builds it, nothing deploys it, and it lacks the self-hosted Supabase migration (`db: { schema: 'printcraft' }`, `supabase.orangecat.ch`) and `output: "standalone"`. Do not develop against it — and do not mistake `app/` for the duplicate when cleaning up. | + +The deploy registry is the source of truth for which directory ships: +`fleetcrown/scripts/hetzner/apps.conf` → +`printcraft|4015|printcraft.orangecat.ch|/home/g/dev/printcraft|app|-` (field 5 = `APP_DIR`). + ## Development ```bash +cd app npm install npm run dev # http://localhost:3000 +npm run verify # lint + typecheck — same command CI runs ``` ## Deployment diff --git a/app/package.json b/app/package.json index 710aeaa..ec05aa3 100644 --- a/app/package.json +++ b/app/package.json @@ -6,7 +6,9 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint" + "lint": "eslint", + "typecheck": "tsc --noEmit", + "verify": "npm run lint && npm run typecheck" }, "dependencies": { "@base-ui/react": "^1.3.0",