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

6.5 KiB

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

# 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

# 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

# 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:

node <package-dir>/dist/index.js   # e.g. node packages/core/dist/index.js

Test

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

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).
.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.