Files
PersonalBlog/docs/development/non-container.md
T
implementer 97c5306768
CI / Frozen lockfile install (pull_request) Successful in 43s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 30s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 31s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 24s
test: lock in pg/Kysely import isolation to database-postgres (E00-S03-T02)
- database-postgres-imports.test.mjs: static scan of every workspace package
  source proves pg/kysely import specifiers resolve only to
  packages/database-postgres; owner manifest pins the driver and no other
  package declares it; mutation probes prove the scan catches a driver import
  injected into apps/server/src/index.ts; comment-stripping and specifier
  matcher unit probes; CI-enforcement assertion
- workspace-layout / workspace-config / strict-tsconfig / typescript-pin:
  package-set fixtures updated to include packages/database-postgres
- docs/development/non-container.md: workspace package table and build
  expectations updated for the new package
2026-08-29 11:14:15 +00:00

147 lines
6.5 KiB
Markdown

# Local non-container development
This guide documents the **local, non-container developer path** for the EPPP
workspace: how to install, build and run the project on your machine **without
Docker**. The containerised path (Docker Compose baseline, container smoke
tests) is tracked separately as story [E00-S02] Docker Compose baseline and is
**not** covered here.
> Status: this document tracks the E00-S01 workspace bootstrap state. Commands
> and expected outputs below were verified from a clean clone.
## What you get at bootstrap
The workspace is a pnpm monorepo with three package groups:
| Group | Path | Purpose |
| --- | --- | --- |
| `apps/` | `apps/server` (`@personal-blog/server`) | Public server application. Serves the application health endpoint (E00-S02-T03); the Fastify 5 application shell lands in a later story. |
| `packages/` | `packages/core` (`@personal-blog/core`) | Application core (site identity, content primitives). Bootstrap placeholder. |
| `packages/` | `packages/database-postgres` (`@personal-blog/database-postgres`) | PostgreSQL database adapter package. Single owner of the `pg`/Kysely driver imports (E00-S03-T02); the concrete adapter lands in later stories. |
| `extensions/` | `extensions/example` (`@personal-blog/example-extension`) | Example extension exercising the `extensions/` group. Bootstrap placeholder. |
A dependency-boundary rule (`dependency-boundaries.json`, enforced by
`tests/architecture-import.test.mjs`) keeps the groups from collapsing into an
accidental dependency graph: no `packages/*` package may import a concrete
`extensions/*` package.
## Prerequisites
- **Git** — to clone the repository.
- **Node.js 24.x** — required. The root `package.json` restricts the Node
engine to 24.x (`engines.node`), and the committed `pnpm-workspace.yaml` sets
`engineStrict: true`, so `pnpm install` on any other Node version is
**rejected** (`ERR_PNPM_UNSUPPORTED_ENGINE`) instead of merely warned.
Install Node 24.x via `nvm`, `fnm` or another version manager to match CI.
- **pnpm 11.23.0** — pinned via the `packageManager` field in the root
`package.json`. The easiest way to get exactly this version is Corepack,
which ships with Node.js (`corepack enable`).
No database, no runtime services and no Docker daemon are required at this
stage.
## Install
```sh
# 1. Clone the repository
git clone https://git.stevanovic.co.uk/Fabrika/PersonalBlog.git
cd PersonalBlog
# 2. Activate the pinned pnpm (11.23.0) via Corepack
corepack enable
# 3. Install dependencies against the committed lockfile (reproducible)
pnpm install --frozen-lockfile
```
- The install is **frozen**: `--frozen-lockfile` refuses to run if
`pnpm-lock.yaml` is out of date with the manifests, so every clone gets the
exact same dependency tree. CI uses the same flag.
- If you are adding or changing dependencies, run `pnpm install` (without the
flag) and **commit the resulting `pnpm-lock.yaml`** so the frozen install
keeps working for everyone.
- `node_modules/` and the pnpm content-addressable store are git-ignored and
never committed.
## Build
```sh
# Compile every workspace package (apps, packages, extensions) into dist/
pnpm build
# Typecheck every workspace package (no emit)
pnpm typecheck
```
`pnpm build` runs `tsc -p tsconfig.json` in each package in topological order
and emits `dist/` (JavaScript + type declarations + source maps) per package.
Expected result: `apps/server/dist/`, `packages/core/dist/`,
`packages/database-postgres/dist/` and `extensions/example/dist/` are
produced, all packages report `Done`, exit 0.
`dist/` is git-ignored; rebuild whenever you change `src/`.
## Run
```sh
# Run the compiled public server entrypoint
pnpm --filter @personal-blog/server start
```
This runs the `start` script of `apps/server` (`node dist/index.js`), i.e. the
compiled application entrypoint. Two things to know:
1. The command **must follow `pnpm build`** — the `start` script executes the
compiled artifact in `dist/`, it does not compile first.
2. Since [E00-S02-T03], `apps/server` serves the **application health
endpoint**: starting it opens an HTTP server on port 3000 answering
`GET /health` with HTTP 200 and `{"status":"ok"}`, so the process stays up.
The real Fastify 5 application shell — which turns this into the full
serving API — lands in a later story; the `start` command shape stays the
same once it does.
To run the compiled output of any other workspace package directly:
```sh
node <package-dir>/dist/index.js # e.g. node packages/core/dist/index.js
```
## Test
```sh
pnpm test
```
`pnpm test` runs the `node:test` suites under `tests/` (currently
`tests/architecture-import.test.mjs`, 10 tests) with zero extra dependencies.
This is also the suite that enforces the dependency-boundary rule.
## Smoke check from a clean clone
```sh
git clone https://git.stevanovic.co.uk/Fabrika/PersonalBlog.git && cd PersonalBlog
corepack enable
pnpm install --frozen-lockfile # exit 0, lockfile untouched
pnpm build # 4/4 packages emit dist/, exit 0
pnpm typecheck # 4/4 packages pass --noEmit, exit 0
pnpm test # 10/10 pass, exit 0
pnpm --filter @personal-blog/server start # serves GET /health on port 3000, stays up
```
## Troubleshooting
| Symptom | Cause / fix |
| --- | --- |
| `pnpm: command not found` | Corepack shims not activated — run `corepack enable`, or prefix commands with `corepack pnpm ...`. |
| `ERR_PNPM_OUTDATED_LOCKFILE` | `pnpm-lock.yaml` is out of date with the manifests. Run `pnpm install` (unfrozen) and commit the lockfile update. |
| `ERR_PNPM_UNSUPPORTED_ENGINE` on install | Your Node version is outside the supported 24.x engine line (`engines.node` in the root `package.json`, enforced by `engineStrict: true` in `pnpm-workspace.yaml`). Install Node 24.x (e.g. via `nvm`, `fnm` or another version manager). |
| `start` exits immediately with no output | The server crashed or exited at startup — check the process output. Since [E00-S02-T03] the entrypoint serves `GET /health` on port 3000 and stays up; a missing `pnpm build` (stale/absent `dist/`) is the usual cause (see [Run](#run)). |
| `.env` files | `.env`/`.env.*` are git-ignored; a committed `.env.example` template lands with the environment story (E00-S04). |
## Out of scope
- Root build/test/typecheck **script wiring** — covered by E00-S01-T05; only
usage is documented here.
- **Docker Compose baseline** and container smoke tests — E00-S02.
- PostgreSQL adapter, migration runner and other runtime services — later E00
stories.