The monorepo was on npm workspaces. pnpm gives it a content-addressed store shared between the eight packages, a lockfile that records the whole graph rather than a flattened view of it, and — the reason this mattered in practice — `workspace:*`, which makes an internal dependency unambiguous instead of a version range that npm may satisfy from the registry. Mechanics: - `packageManager: pnpm@11.21.0` pins the version; corepack installs it in CI and in the image, so all three environments resolve identically. - The npm `workspaces` array is replaced by `pnpm-workspace.yaml`. pnpm ignores the former, and keeping both would leave two sources of truth. - All six internal dependencies moved to `workspace:*`. - Root scripts use `pnpm -r --if-present` and `pnpm -F <pkg>`. Two findings worth recording, both from running it rather than reading it: `tsx` was a devDependency, but the server runs TypeScript directly in production — the container's command is `pnpm exec tsx apps/api/src/server.ts`. Under npm this was concealed by the runtime stage re-installing tsx by hand after pruning dev dependencies. Under `pnpm install --prod` that sleight of hand stops working and the image simply fails to start. tsx is now declared in `dependencies`, which is what it has always actually been. The first image build failed with ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY. That is not a pnpm bug: it had decided the modules directory was stale and wanted confirmation before deleting it, which a non-interactive build cannot give. The trigger was the host's `node_modules` reaching the build context — there was no `.dockerignore` at all. pnpm's tree is symlinks into a content-addressed store, so copying it into an image produces dangling links and a directory pnpm rightly considers corrupt. Fixed by adding `.dockerignore` and setting `CI=true`, which is required in any non-interactive pnpm build. `esbuild` is denied install scripts via `allowBuilds`. Its platform binary arrives through the optional dependency `@esbuild/linux-x64` and the postinstall only verifies it; confirmed by running the binary directly, which reports 0.25.12. Verified under pnpm: typecheck clean, 150 tests / 0 failures, e2e passes, web builds. The image was built and booted against a real Postgres — health ok, `/api/dashboard` 401 with an issuer configured, `/` and `/capacity` serve the SPA, `/og.png` serves as image/png, and the migrator runs from the pruned runtime stage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -52,8 +52,15 @@ docs/ ontology.md, build-plan.md, agents.md, deploy.md, seed-data.md
|
||||
|
||||
## 3. Running it
|
||||
|
||||
**PIG uses pnpm**, pinned by the `packageManager` field. Do not run `npm
|
||||
install` — it will write a `package-lock.json` that nothing reads and resolve a
|
||||
dependency tree that neither CI nor the image uses. Corepack ships with Node and
|
||||
installs the pinned version for you:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
corepack enable
|
||||
|
||||
pnpm install
|
||||
|
||||
# Postgres. PIG needs its own database — never point it at a shared one.
|
||||
docker run -d --name pig-dev -p 5432:5432 \
|
||||
@@ -61,12 +68,12 @@ docker run -d --name pig-dev -p 5432:5432 \
|
||||
postgres:16-alpine
|
||||
|
||||
export DATABASE_URL=postgres://pig:pig@localhost:5432/pig
|
||||
npm run db:migrate
|
||||
npm run db:seed # sourced, cited people — optional
|
||||
npm run db:demo # a plausible demo book — optional, prefixed "DEMO — "
|
||||
pnpm run db:migrate
|
||||
pnpm run db:seed # sourced, cited people — optional
|
||||
pnpm run db:demo # a plausible demo book — optional, prefixed "DEMO — "
|
||||
|
||||
npm run dev:api # :8920
|
||||
npm run dev:web # :5173, proxies /api to 8920
|
||||
pnpm run dev:api # :8920
|
||||
pnpm run dev:web # :5173, proxies /api to 8920
|
||||
```
|
||||
|
||||
With no `SUPABASE_URL` set, **authentication is disabled in development** and
|
||||
@@ -76,7 +83,7 @@ in production without it, so this cannot leak.
|
||||
Before pushing:
|
||||
|
||||
```bash
|
||||
npm run typecheck && npm test
|
||||
pnpm run typecheck && pnpm test
|
||||
```
|
||||
|
||||
---
|
||||
@@ -169,6 +176,21 @@ document.documentElement.scrollWidth - document.documentElement.clientWidth
|
||||
|
||||
It should be 0 on every route at 393px wide.
|
||||
|
||||
**pnpm needs `CI=true` in any non-interactive build.** When it decides a
|
||||
modules directory is stale it asks before removing it; with no TTY it cannot
|
||||
ask, so it aborts with `ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY`. This reads
|
||||
like a pnpm bug and is not — it is pnpm refusing to delete files nobody
|
||||
confirmed. Both the Dockerfile and CI set it. The usual trigger is a host
|
||||
`node_modules` reaching the build context, which is why `.dockerignore` exists:
|
||||
pnpm's tree is symlinks into a content-addressed store, so copying it into an
|
||||
image yields dangling links and a modules directory pnpm considers corrupt.
|
||||
|
||||
**`tsx` is a production dependency, not a dev one.** The server runs TypeScript
|
||||
directly — `pnpm exec tsx apps/api/src/server.ts` is the container's command —
|
||||
so pruning it away breaks the image. It lives in `dependencies` deliberately;
|
||||
moving it back to `devDependencies` because "it's a build tool" makes
|
||||
`pnpm install --prod` produce an image that cannot start.
|
||||
|
||||
**Drizzle-generated migrations are not always valid SQL.** A `jsonb → integer`
|
||||
cast was emitted without the `USING` clause Postgres requires. Always apply a
|
||||
new migration to a real empty database before pushing — CI does this, but find
|
||||
|
||||
Reference in New Issue
Block a user