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",