Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Worktree-isolated local Postgres for Vite+, Nx, and CedarJS, on top of [autopg](

Mental model: autopg is the host; cedar-pg is the lease + naming + policy layer so parallel git worktrees do not share one DB.

Published npm package: `@cedarjs/pg` (CLI binary: `cedarpg`). Beta (`0.2.0-beta.x`, `beta` dist-tag) — public APIs may still change; treat renames and export surface as intentional product decisions, not drive-by churn.
Published npm package: `@cedarjs/pg` (CLI binary: `cedarpg`). Pre-1.0 (`0.x`, `latest` dist-tag; minor bumps may break) — public APIs may still change; treat renames and export surface as intentional product decisions, not drive-by churn.

## Commands

Expand Down
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

## Unreleased

## 0.3.0

First release off the `beta` dist-tag: `npm install @cedarjs/pg` now installs it as a normal 0.x release (`latest`). Public APIs may still change before 1.0. This is a 0.x minor bump because role / clone naming for long worktree names and `cedarpg run` exit codes on signals change (see below).

### Added

- CLI: `cedarpg run --attach --mode=dev|test -- <cmd…>`, attach-only run. It reads the existing lease and sets child `DATABASE_URL` (+ `TEST_DATABASE_URL` in test). It never acquires, never runs role/database DDL, and never starts the host. It fails before the child starts when there is no lease or nothing listens on the leased port.
- `acquire({ fresh: true })` / `acquireIfNeeded({ fresh: true })` drop every database owned by this worktree's role for that mode first. That covers the leased DB, TEMPLATE clones, and leftovers from crashed runs, and it works even when the lease file is gone. The database is then created again empty.
- `cloneWorkerDatabase({ reset })` (Jest / Vitest TEMPLATE) resets the worker clone at the start of every test file. The default `"truncate"` runs `TRUNCATE ... RESTART IDENTITY` over every user table, skipping system schemas, temp tables, and extension-owned tables. Rows from earlier files in the worker are gone, and `serial` / identity IDs restart at 1, so suites that expect `id = 1` work on a reused clone. This also clears rows that `migrate` seeded into the TEMPLATE. Pass `reset: "none"` to keep the clone's rows and reset on your own. A skipped clone (external URL, `CEDAR_PG=0`) is never truncated.
- `cloneFromTemplate({ reuse: true })` keeps an existing `<template>_c_<name>` owned by the lease role instead of failing with `database already exists`. A clone name owned by any other role still fails, with the owner in the message.

- Warn when the installed autopg is older than the pin (`scripts/autopg-version`, inlined at build). The first host attach of each process (`acquire`, `cedarpg run`, TEMPLATE setup) and `postinstall` print the upgrade commands (pinned `install.sh`, then `autopg update`). It is a warning only: cedar-pg never upgrades a local host, because that restarts the host every worktree shares.

### Changed
Expand All @@ -18,7 +21,6 @@
- Nx canonical shape: one `db:ready` acquires + migrates. Children either preload `@cedarjs/pg/dev-env` or use `cedarpg run --attach` (was `cedarpg run --force` per child). Children no longer run DDL, so concurrent dev servers and workers no longer race. Plain `cedarpg run` is unchanged for one-shot acquire + exec. The README Nx section is rewritten as a generic setup guide.
- TEMPLATE setup (`setupTemplateMode`, Jest / Vitest `createGlobalSetup`) acquires with `fresh: true`. Leftover TEMPLATE / clone databases from a crashed run are dropped before `migrate`, so migrate always starts empty and consumers need no pre-cleanup.
- TEMPLATE `cloneWorkerDatabase` truncates at the start of **every** test file by default, the first file on a fresh clone included, not only when it reuses a clone. Each worker clone used to start with the TEMPLATE's rows. Now every file starts with empty user tables, and rows that `migrate` seeded into the TEMPLATE are wiped, including bookkeeping tables such as `_prisma_migrations`. Migration: if your tests depend on seeded rows, either seed them in your worker setup after `cloneWorkerDatabase()`, or pass `cloneWorkerDatabase({ reset: "none" })` and reset on your own.

- autopg pin bumped `v3.0.7` → `v3.2.2` (`scripts/autopg-version`; postinstall, `ci-install-autopg.sh`, and the `setup-autopg` Action follow it). v3.2 fixes a host that hung after days under load (stderr drain), keeps the pm2 registration across `pm2 resurrect`, and makes `autopg install` / `restart` wait for readiness. Postinstall only installs autopg when it is missing (except the forced CI path above); run `autopg update` to move an existing host.
- Local host recovery follows v3.2 `autopg restart`: exit 0 means ready, and a pm2-less or non-pm2 host exits 1, after which cedar-pg revives the registered postmaster as before. The v3.0.x "respawned daemon" exit-0 special case is removed; on a pm2-less host still running autopg v3.0.x, recovery waits up to 10s before the revive.

Expand All @@ -34,8 +36,7 @@
- Concurrent ephemeral (CI) host cold starts no longer delete each other's data dir. With Nx running several `db:ready` / acquire targets in parallel on one runner, a second process saw no listener yet and removed the first one's data dir mid-initdb. Cold start now takes an exclusive lock next to the data dir (`<dataDir>.lock`, taken over when its holder is dead) and prunes only when no live postmaster owns the dir. Every other process waits for the listener instead.
- TEMPLATE `cloneWorkerDatabase` reuses the worker's `<template>_c_<workerId>` in Postgres across Jest test files. The `0.2.0-beta.0` fix cached the clone on `globalThis`, but Jest resets `globalThis` and the module registry for each test file. So the second file in a worker still ran `CREATE DATABASE` and failed with `database already exists`. The in-memory memo is removed; the database is the source of truth. As a result, calling it again with a different `root` / `name` in one process no longer throws: each call clones or reuses its own name. `smoke:pg` now runs two Jest files in one worker, after a seeded crashed run.
- TEMPLATE workers (`cloneWorkerDatabase`, and `cloneFromTemplate` / `markTemplate` without `adminUrl`) attach to the leased port like `cedarpg run --attach`: they read the lease and probe it once. They no longer rediscover the host on every test file (git, `autopg --version`, `autopg status`) and never start or revive it from a worker; a dark leased port fails with a message to re-run acquire. The outdated-autopg warning now prints once per run from setup instead of once per Jest test file.

- Role and TEMPLATE clone names for long worktree names keep the full `_<mode>_<pathHash8>`. To stay within 63 chars, cedar-pg used to cut the end of the database name before adding `_role`, which kept only 3 hash characters. Two worktrees whose names cut to the same prefix then shared one role (about 1 in 4,096), and TEMPLATE setup (`fresh`) or `dispose` in one dropped the other's databases. Clone names with the default `<pid>_<time>` suffix lost the hash and mode entirely. Now the readable `<repo>_<worktree>` part is cut instead. Short names are unchanged. `acquire` (and `fresh`) keeps the role stored in an existing lease for the same database, so a long-named dev database created before this release keeps its role and its data stays readable; only new databases get the new role name. Without that lease file, `acquire` uses the new name: it does not find leftovers created under the old cut role, and an existing database would be handed to the new role while its tables stay owned by the old one. Drop such databases by hand (or restore the lease) before acquiring.
- Role and TEMPLATE clone names for long worktree names keep the full `_<mode>_<pathHash8>`. To stay within 63 chars, cedar-pg used to cut the end of the database name before adding `_role`, which kept only 3 hash characters. Two worktrees whose names cut to the same prefix then shared one role (about 1 in 4,096), and TEMPLATE setup (`fresh`) or `dispose` in one dropped the other's databases. Clone names with the default `<pid>_<time>` suffix lost the hash and mode entirely. Now the readable `<repo>_<worktree>` part is cut instead. Short names are unchanged. `acquire` (and `fresh`) keeps the role stored in an existing lease for the same database, so a long-named dev database created before 0.3.0 keeps its role and its data stays readable; only new databases get the new role name. Without that lease file, `acquire` uses the new name: it does not find leftovers created under the old cut role, and an existing database would be handed to the new role while its tables stay owned by the old one. Drop such databases by hand (or restore the lease) before acquiring.

## 0.2.0-beta.1

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Worktree-isolated local Postgres for Vite+, Nx, and CedarJS. The host is [autopg](https://github.com/automagik-dev/autopg) (embedded PostgreSQL 18). cedar-pg creates one database and role per git worktree so parallel checkouts do not share a DB.

Published as `@cedarjs/pg`. CLI: `cedarpg`. Beta (`0.2.0-beta.1`, `beta` dist-tag). Public APIs may still change.
Published as `@cedarjs/pg`. CLI: `cedarpg`. Pre-1.0 (`0.x`, `latest` dist-tag). Public APIs may still change between minor versions.

| Layer | Owns |
| -------- | --------------------------------------------------------------------------------- |
Expand All @@ -14,9 +14,9 @@ Published as `@cedarjs/pg`. CLI: `cedarpg`. Beta (`0.2.0-beta.1`, `beta` dist-ta
Node `>=24`.

```bash
npm install -D @cedarjs/pg@beta
# pnpm add -D @cedarjs/pg@beta
# yarn add -D @cedarjs/pg@beta
npm install -D @cedarjs/pg
# pnpm add -D @cedarjs/pg
# yarn add -D @cedarjs/pg
```

`postinstall` installs the pinned autopg binary when it is missing. The pin is `scripts/autopg-version`. To bump autopg, change that file.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cedarjs/pg",
"version": "0.2.0-beta.1",
"version": "0.3.0",
"description": "Worktree-isolated local Postgres for Vite+, Nx, and CedarJS (via autopg)",
"keywords": [
"autopg",
Expand Down Expand Up @@ -87,7 +87,7 @@
},
"publishConfig": {
"access": "public",
"tag": "beta"
"tag": "latest"
},
"scripts": {
"build": "vp pack",
Expand Down
2 changes: 1 addition & 1 deletion src/core/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export async function acquire(options: AcquireOptions): Promise<AcquireResult> {
const databaseName = buildDatabaseName(identity, mode);
// A lease for this database pins its role: objects inside belong to that role,
// and handing the DB to a newly derived name (naming changed for long names in
// 0.2.0-beta.2) would leave the app without access to its own tables.
// 0.3.0) would leave the app without access to its own tables.
const leased = readLease(identity.root, mode);
const roleName =
leased?.databaseName === databaseName ? leased.roleName : buildRoleName(databaseName);
Expand Down
Loading