From a07ade7c678c8b283fdddf10f8f8b04d05ce0e37 Mon Sep 17 00:00:00 2001 From: Felipe Rosa Date: Mon, 18 May 2026 15:17:36 -0300 Subject: [PATCH 01/18] =?UTF-8?q?release:=20v3.0.0=20=E2=80=94=20autopg=20?= =?UTF-8?q?org=20transfer=20+=20install.sh=20bootstrap=20repair?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps package.json 2.7.0 → 3.0.0. Merging to main triggers release.yml (push-to-main → prepare resolves 3.0.0 → tags v3.0.0 → build-tarballs → sign-attest → release-publish). No hand tag. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69aded2..1b5ce96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## v3.0.0 — autopg (org transfer + bootstrap repair) + +**Changed:** Repository transferred `namastexlabs/pgserve` → `automagik-dev/autopg` (transfer + rename). Old URLs 301-redirect. `src/cosign/trust-list.js` self-trust regex flipped to `automagik-dev/autopg` — v3+ binaries verify v3+ releases signed under the new org identity. + +**Fixed:** `install.sh` fresh-host bootstrap, broken independent of the transfer: correct `autopg-*` asset names with glibc/musl detection, `gh api` latest-resolution (the unauthenticated `curl|sed` path returned empty), correct extracted layout (`autopg/autopg`) plus a `~/.local/bin/autopg` symlink, and a `cosign verify-blob` fallback with a dual-org identity regexp so hosts on `gh < 2.49` can still cryptographically verify the current `latest` (signed pre-transfer under the old org). + +**Note:** the npm `pgserve` package remains on v2.6.10 as legacy LTS for `@withone/cli` — not deprecated. + ## v2.2.x — Transparent Upgrade **Added:** `autopg upgrade` CLI verb — idempotent migration runner that reconciles port back to canonical 8432, flushes the binary cache against the pinned PG version, re-resolves the plpgsql `.so` path per database, refreshes `~/.autopg/.env` files, signals consumers, and validates final health. diff --git a/package.json b/package.json index abf16dc..3709de0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autopg", - "version": "2.7.0", + "version": "3.0.0", "description": "Embedded PostgreSQL server with true concurrent connections - zero config, auto-provision databases", "main": "src/index.js", "type": "module", From 5c910a1f504a9e34735ee5f4711243b89c89eeca Mon Sep 17 00:00:00 2001 From: Felipe Rosa Date: Mon, 18 May 2026 17:01:38 -0300 Subject: [PATCH 02/18] fix(release): scratch unbound var aborts every build-tarballs job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trap 'rm -rf "$scratch"' RETURN in stage_from_pkg/stage_from_url leaks globally (no set -o functrace) and re-fires in fetch_one's scope where $scratch is undefined → under set -u: 'line 179: scratch: unbound variable' → exit 1 on EVERY platform. Root cause of all v3.0.0 Build failures AND the prior v2.7.0 sign-attest 'artifact not found' (build never produced artifacts). Not org-transfer related. Fix: ${scratch:-} makes the leaked-scope trap set -u-safe; cleanup still works when scratch is set. Proven: set -u + leaked RETURN trap no longer aborts. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/fetch-postgres-bins.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/fetch-postgres-bins.sh b/scripts/fetch-postgres-bins.sh index 1a13089..3325428 100755 --- a/scripts/fetch-postgres-bins.sh +++ b/scripts/fetch-postgres-bins.sh @@ -117,7 +117,7 @@ stage_from_pkg() { # `scratch: unbound variable` and mask the real fetch error # (chatgpt-codex P2 review on PR #84). local scratch="" - trap 'rm -rf "$scratch"' RETURN + trap 'rm -rf "${scratch:-}"' RETURN scratch=$(mktemp -d) || return 1 pushd "$scratch" >/dev/null @@ -155,7 +155,7 @@ stage_from_url() { local scratch scratch=$(mktemp -d) - trap 'rm -rf "$scratch"' RETURN + trap 'rm -rf "${scratch:-}"' RETURN curl -fsSL "$url" -o "${scratch}/pg.tar.gz" tar -xzf "${scratch}/pg.tar.gz" -C "$scratch" From aa4cbfe874e78bc3a7a0756a3a95c8dcdd0b94fa Mon Sep 17 00:00:00 2001 From: Felipe Rosa Date: Mon, 18 May 2026 17:50:42 -0300 Subject: [PATCH 03/18] =?UTF-8?q?release:=20v3.0.1=20=E2=80=94=20first=20p?= =?UTF-8?q?ublished=20v3=20(post=20build-tarballs=20fix)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v3.0.0 tag never produced assets (build-tarballs scratch-unbound bug, fixed in #132). v3.0.1 is the first real v3 release. Merge to main → push tag v3.0.1 → build (now fixed) → sign-attest → release-publish. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 4 ++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b5ce96..af38772 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v3.0.1 — build-tarballs fix + +**Fixed:** `scripts/fetch-postgres-bins.sh` RETURN trap leaked globally (no `set -o functrace`) and aborted every per-platform build under `set -u` (`scratch: unbound variable`). This blocked the v3.0.0 build chain entirely; v3.0.1 is the first published v3 release (v3.0.0 tag never produced assets). + ## v3.0.0 — autopg (org transfer + bootstrap repair) **Changed:** Repository transferred `namastexlabs/pgserve` → `automagik-dev/autopg` (transfer + rename). Old URLs 301-redirect. `src/cosign/trust-list.js` self-trust regex flipped to `automagik-dev/autopg` — v3+ binaries verify v3+ releases signed under the new org identity. diff --git a/package-lock.json b/package-lock.json index 4023445..bdd1faa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pgserve", - "version": "2.0.0", + "version": "3.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pgserve", - "version": "2.0.0", + "version": "3.0.1", "license": "MIT", "dependencies": { "bun": "^1.3.4" diff --git a/package.json b/package.json index 3709de0..3eee86d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autopg", - "version": "3.0.0", + "version": "3.0.1", "description": "Embedded PostgreSQL server with true concurrent connections - zero config, auto-provision databases", "main": "src/index.js", "type": "module", From 5add9bd858233b77402119888eb05a4f7b67a001 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 18 May 2026 21:58:06 +0000 Subject: [PATCH 04/18] [skip ci] release v3.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3eee86d..e25c32a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autopg", - "version": "3.0.1", + "version": "3.0.2", "description": "Embedded PostgreSQL server with true concurrent connections - zero config, auto-provision databases", "main": "src/index.js", "type": "module", From 6dea824ca945818eaad5d2b58eb5e3c236247ff9 Mon Sep 17 00:00:00 2001 From: "Claude (Opus 4.7)" Date: Tue, 19 May 2026 02:09:24 -0300 Subject: [PATCH 05/18] fix(release): repair v3 signed-release pipeline (Bugs #1a/#1b/#2/#3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BRIEF-v3-build-fix — three evidence-backed blockers kept every v3 tag (v2.7.0/v3.0.0/v3.0.2) from publishing anything. Bug #1a — autopg --version exited 1: bin/postgres-server.js (the file bun build --compile turns into the tarball's autopg, and what autopg-wrapper.cjs spawns) had no --version handler -> fell through to printHelp()+exit(1). tarball-smoke.sh swallowed stderr (2>/dev/null) and mislabeled it "binary not executable". Add a --version/-v handler using the bun --define BUILD_VERSION literal with a package.json fallback for the non-compiled wrapper path. Bug #1b — postgres --version exited 127 (libssl.so.1.1 / libicu60 not found): @embedded-postgres ships native/lib/ (libssl.so.1.1, libcrypto.so.1.1, libicu*.so.60, ...) + pg-symlinks.json, but fetch-postgres-bins.sh copied only bin/+share/, dropping lib/ entirely and never replaying the version symlinks postgres' DT_NEEDED resolves through (RUNPATH=$ORIGIN/../lib). Every shipped tarball's postgres was DOA on any modern host. Stage native/lib/ + replay pg-symlinks.json for all three source paths. Bug #2 — darwin-x64 sat queued forever on the unavailable macos-13 runner, holding run-conclusion null and (via the missing artifact) sinking sign-attest's aggregate -> the whole release-publish chain. Dropped from the build + sign matrices and the channel manifest; Intel macOS is a tracked follow-up. Bug #3 — the bump commit's required [skip ci] guard also suppressed the tag-push event, so build-tarballs.yml never fired. The bump job now explicitly gh workflow run build-tarballs.yml --ref (dispatch is skip-ci-immune, GITHUB_TOKEN-permitted) — [skip ci] stays intact. Local proof (linux-x64-glibc, full build->fetch->assemble->smoke --real): 11 passed, 0 failed (was 9 passed, 2 failed) — autopg 3.0.3 + postgres (PostgreSQL) 18.3, 48M tarball (in band). Fixture smoke 11/0 on all 5 platforms, shellcheck -S warning clean, workflows parse. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/build-tarballs.yml | 14 +++++++-- .github/workflows/release-publish.yml | 2 +- .github/workflows/release.yml | 33 ++++++++++++++++++++ .github/workflows/sign-attest.yml | 6 +++- bin/postgres-server.js | 37 +++++++++++++++++++++++ scripts/fetch-postgres-bins.sh | 43 +++++++++++++++++++++++++++ 6 files changed, 131 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-tarballs.yml b/.github/workflows/build-tarballs.yml index 11b6663..cfd4a1e 100644 --- a/.github/workflows/build-tarballs.yml +++ b/.github/workflows/build-tarballs.yml @@ -137,8 +137,18 @@ jobs: # GitHub-hosted ARM64 runners; falls back to QEMU emulation # if the org has not yet enabled ubuntu-24.04-arm runners. runner: ubuntu-24.04-arm - - platform: darwin-x64 - runner: macos-13 + # BRIEF-v3-build-fix Bug #2: darwin-x64 is intentionally NOT in + # this matrix. Its only viable hosted runner is `macos-13` (the + # last Intel macOS image — macos-14/15 are Apple Silicon), and + # the org's macOS runners are chronically unavailable, so the job + # sat `queued` forever holding the run-level conclusion `null`. + # That masked real failures AND, because a missing + # `autopg-*-darwin-x64` artifact fails sign-attest's `aggregate` + # (needs:[sign], no `if: always()`), it silently sank the entire + # signed-release chain. Dropping it lets the 4 buildable + # platforms publish a real signed release. Intel-macOS support is + # a tracked follow-up (re-add with a self-hosted/available Intel + # runner, or cross-build darwin-x64 + sign on darwin-arm64). - platform: darwin-arm64 runner: macos-latest diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 01b9374..fa5b81d 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -271,7 +271,7 @@ jobs: "version": "${VERSION}", "released_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "tarball_base": "https://github.com/${{ github.repository }}/releases/download/v${VERSION}", - "platforms": ["linux-x64-glibc","linux-x64-musl","linux-arm64","darwin-x64","darwin-arm64"] + "platforms": ["linux-x64-glibc","linux-x64-musl","linux-arm64","darwin-arm64"] } EOF done diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dc8c13a..676368b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -89,6 +89,39 @@ jobs: git tag -a "${TAG}" -m "release ${TAG}" git push origin HEAD --follow-tags + # BRIEF-v3-build-fix Bug #3: the bump commit carries `[skip ci]` (a + # required bot-loop guard — the prepare gate filters that same marker + # so the push of this commit does NOT retrigger release.yml). But + # GitHub's skip-ci suppression ALSO applies to the *tag* push event, + # so `build-tarballs.yml` (on: push tags v*) never fires → the + # signed-release chain (build-tarballs → sign-attest → + # release-publish) silently never starts. This hit v2.7.0, v3.0.0, + # v3.0.2 (all tagged, none published anything). + # + # Fix: dispatch build-tarballs explicitly against the freshly-pushed + # tag. workflow_dispatch is NOT subject to skip-ci, GITHUB_TOKEN is + # permitted to start it, and `--ref ${TAG}` makes every job in the + # chain check out the tagged tree. `[skip ci]` stays intact. + - name: Kick build-tarballs for the tag (skip-ci-immune) + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ steps.bump.outputs.version }}" + TAG="${{ steps.bump.outputs.tag }}" + # Tag refs need a beat to register before the API accepts --ref. + for i in 1 2 3 4 5; do + if gh workflow run build-tarballs.yml \ + --ref "${TAG}" \ + -f version="${VERSION}"; then + echo "Dispatched build-tarballs.yml @ ${TAG} (version=${VERSION})" + exit 0 + fi + echo "build-tarballs dispatch attempt ${i} failed; retrying in 10s" + sleep 10 + done + echo "::error::could not dispatch build-tarballs.yml for ${TAG}" + exit 1 + # --------------------------------------------------------------------------- # Prepare: resolve version, skip if tag already exists, build changelog. # diff --git a/.github/workflows/sign-attest.yml b/.github/workflows/sign-attest.yml index 8c6cd53..547acb0 100644 --- a/.github/workflows/sign-attest.yml +++ b/.github/workflows/sign-attest.yml @@ -83,7 +83,11 @@ jobs: - linux-x64-glibc - linux-x64-musl - linux-arm64 - - darwin-x64 + # darwin-x64 dropped in lockstep with build-tarballs.yml — see + # the Bug #2 comment there. A `download-artifact` for a tarball + # build-tarballs never produced would fail this matrix leg, and + # the `aggregate` job (needs:[sign]) would be skipped, taking the + # whole release-publish chain down with it. - darwin-arm64 steps: diff --git a/bin/postgres-server.js b/bin/postgres-server.js index 315fc8b..fe84194 100755 --- a/bin/postgres-server.js +++ b/bin/postgres-server.js @@ -19,6 +19,9 @@ import { PostgresManager } from '../src/postgres.js'; import { resolveSocketDir, ensureSocketDir } from '../src/lib/socket-dir.js'; import { writeRuntimeJson, clearRuntimeJson } from '../src/lib/runtime-json.js'; import { createLogger } from '../src/logger.js'; +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; // Global error handlers — surface unhandled rejections + uncaught errors // loud so a process supervisor (pm2 / systemd-user / launchd) restarts the @@ -33,6 +36,26 @@ process.on('uncaughtException', (error) => { const args = process.argv.slice(2); +// `autopg --version` / `-v` — MUST exit 0 with `autopg `. +// +// This entry point is what `scripts/build-binary.sh` compiles via +// `bun build --compile` (the tarball's `autopg` binary IS this file), +// AND what `bin/autopg-wrapper.cjs` spawns through bun for the npm path. +// Both surfaces previously fell through to `printHelp()` + `exit(1)` — +// `tests/integration/tarball-smoke.sh` swallowed the stderr and reported +// the generic "autopg binary not executable", masking the real cause +// (no `--version` handler ever existed). See BRIEF-v3-build-fix Bug #1a. +// +// Version source: build-binary.sh injects `--define BUILD_VERSION="''"`, +// so in the compiled binary the bare `BUILD_VERSION` token is replaced with +// a string literal. `typeof` on an undeclared identifier is the one safe +// form in JS (returns 'undefined' without throwing), so the non-compiled +// wrapper path falls back to package.json cleanly. +if (args[0] === '--version' || args[0] === '-v') { + process.stdout.write(`autopg ${resolveVersion()}\n`); + process.exit(0); +} + if (args[0] === 'postmaster') { await runPostmasterSubcommand(args.slice(1)); } else if (args[0] === 'serve') { @@ -218,6 +241,20 @@ no router, no bun proxy, no daemon control socket. return opts; } +function resolveVersion() { + // Compiled binary: bun's `--define` already replaced BUILD_VERSION with a + // string literal. `typeof ` is the only reference form that + // can't throw, so the non-compiled (wrapper/dev) path falls through here. + if (typeof BUILD_VERSION !== 'undefined' && BUILD_VERSION) return BUILD_VERSION; + try { + const here = dirname(fileURLToPath(import.meta.url)); + const pkg = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf8')); + return pkg.version || '0.0.0'; + } catch { + return '0.0.0'; + } +} + function printHelp() { process.stdout.write(` pgserve — Embedded PostgreSQL Server (singleton, v2.4+) diff --git a/scripts/fetch-postgres-bins.sh b/scripts/fetch-postgres-bins.sh index 3325428..51589ad 100755 --- a/scripts/fetch-postgres-bins.sh +++ b/scripts/fetch-postgres-bins.sh @@ -31,6 +31,46 @@ DIST_DIR="${AUTOPG_DIST_DIR:-${REPO_ROOT}/dist}" PLATFORMS=(linux-x64-glibc linux-x64-musl linux-arm64 darwin-x64 darwin-arm64) +# Stage the bundled shared-library tree (libssl.so.1.1, libcrypto.so.1.1, +# libicu*.so.60, libxml2, …) that @embedded-postgres ships under native/lib/. +# +# WITHOUT this the postgres binary cannot start on ANY modern host: its +# DT_NEEDED list references libssl.so.1.1 + libicui18n.so.60 which no +# current distro provides, and its RUNPATH is `$ORIGIN/../lib` — so the +# libs MUST sit at /lib/. The previous fetch logic copied only bin/ +# + share/, so every tarball shipped a postgres that died with +# "error while loading shared libraries" (BRIEF-v3-build-fix Bug #1b). +# +# The package also ships pg-symlinks.json: the version symlinks +# (libicui18n.so.60 → libicui18n.so.60.2) that postgres' DT_NEEDED +# resolves through are created by the package's postinstall, which we +# deliberately skip (`npm install --ignore-scripts`). Replay them here so +# the staged tree is self-contained and `postgres --version` works via +# RUNPATH alone (what tests/integration/tarball-smoke.sh --real asserts). +stage_lib_tree() { + local src_lib="$1" out_dir="$2" symlinks_json="${3:-}" + if [[ ! -d "$src_lib" ]]; then + echo " (no lib/ at ${src_lib}; skipping bundled-lib stage)" + return 0 + fi + cp -R "$src_lib" "${out_dir}/lib" + if [[ -n "$symlinks_json" && -f "$symlinks_json" ]]; then + node -e ' + const fs = require("fs"), path = require("path"); + const [json, libDir] = process.argv.slice(1); + const entries = JSON.parse(fs.readFileSync(json, "utf8")); + let n = 0; + for (const { source, target } of entries) { + const linkPath = path.join(libDir, path.basename(target)); + try { fs.unlinkSync(linkPath); } catch {} + fs.symlinkSync(path.basename(source), linkPath); + n++; + } + console.log(` ✓ replayed ${n} pg-symlinks into ${libDir}`); + ' "$symlinks_json" "${out_dir}/lib" + fi +} + # Map autopg platform tag → npm package suffix used by @embedded-postgres. # linux-x64-musl + linux-arm64 currently lack a published @embedded-postgres # package; for those, set AUTOPG_POSTGRES_LOCAL_DIR or AUTOPG_POSTGRES_URL_TEMPLATE. @@ -106,6 +146,7 @@ stage_from_local() { fi cp -R "$local_dir/bin" "$out_dir/bin" cp -R "$local_dir/share" "$out_dir/share" 2>/dev/null || mkdir -p "$out_dir/share" + stage_lib_tree "$local_dir/lib" "$out_dir" } stage_from_pkg() { @@ -142,6 +183,7 @@ EOF cp -R "${native}/bin" "${out_dir}/bin" cp -R "${native}/share" "${out_dir}/share" 2>/dev/null || mkdir -p "${out_dir}/share" + stage_lib_tree "${native}/lib" "${out_dir}" "${native}/pg-symlinks.json" popd >/dev/null } @@ -174,6 +216,7 @@ stage_from_url() { fi cp -R "${root}/bin" "${out_dir}/bin" cp -R "${root}/share" "${out_dir}/share" 2>/dev/null || mkdir -p "${out_dir}/share" + stage_lib_tree "${root}/lib" "${out_dir}" } fetch_one() { From cdecc7a6a057ca6df691624a15a7fff5d8236df0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 19 May 2026 17:02:04 +0000 Subject: [PATCH 06/18] [skip ci] release v3.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e25c32a..b86bc69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autopg", - "version": "3.0.2", + "version": "3.0.3", "description": "Embedded PostgreSQL server with true concurrent connections - zero config, auto-provision databases", "main": "src/index.js", "type": "module", From c37fbe041dd29bdbc18959a9fc7abbe40fad4c67 Mon Sep 17 00:00:00 2001 From: "Claude (Opus 4.7)" Date: Tue, 19 May 2026 14:10:10 -0300 Subject: [PATCH 07/18] fix(release): v3.0.3 build run follow-ups (4 more pipeline blockers) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v3.0.3 build-tarballs run (dispatched after #134 merged) proved the #1a/#1b fixes work — `Build linux-x64-glibc: completed/success` (was 9/2). It exposed 4 further blockers, all pre-existing infra/portability defects, not regressions: (5) release.yml lacked `actions: write`, so the Bug #3 "Kick build-tarballs" step hit `HTTP 403: Resource not accessible by integration` on the workflow_dispatch API (tag v3.0.3 was created + pushed fine, but the chain was never kicked). Grant actions:write. (6) linux-arm64 always failed "no @embedded-postgres pkg for linux-arm64" — yet @embedded-postgres/linux-arm64 IS published on the same 18.3.0-beta line. embedded_pkg_for just never mapped it. Map it + add to optionalDependencies. (CI's linux-arm64 job runs on ubuntu-24.04-arm, so the cpu=arm64 npm install resolves there; the darwin-arm64 leg already fetched fine on its arm64 runner.) (7) linux-x64-musl: @embedded-postgres ships NO musl build and no AUTOPG_POSTGRES_URL_TEMPLATE var is configured, so every musl fetch died and the missing artifact would sink sign-attest's aggregate (same failure mode as darwin-x64). Dropped from build + sign matrices + channel manifest; tracked follow-up. (8) assemble-tarball.sh:168 `tar_flags[@]: unbound variable` — macOS bash 3.2 + `set -u` + an empty array (BSD tar matches none of the GNU-only flags). Killed every darwin-* assemble. Use the `${arr[@]+"${arr[@]}"}` empty-safe expansion. Net published surface: linux-x64-glibc, linux-arm64, darwin-arm64 — all backed by a real @embedded-postgres package. Validation: glibc full build->fetch->assemble->smoke --real 11/0 (no regression); tar_flags idiom correct empty+populated under set -u; shellcheck -S warning clean; fixture smoke 11/0 on the 3 kept platforms; all 4 workflows parse; linux-arm64 npm EBADPLATFORM locally is a host x64 artifact (CI uses an arm64 runner). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/build-tarballs.yml | 10 ++++++++-- .github/workflows/release-publish.yml | 2 +- .github/workflows/release.yml | 6 ++++++ .github/workflows/sign-attest.yml | 4 +++- package.json | 1 + scripts/assemble-tarball.sh | 7 ++++++- scripts/fetch-postgres-bins.sh | 5 ++++- 7 files changed, 29 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-tarballs.yml b/.github/workflows/build-tarballs.yml index cfd4a1e..62fce16 100644 --- a/.github/workflows/build-tarballs.yml +++ b/.github/workflows/build-tarballs.yml @@ -131,8 +131,14 @@ jobs: include: - platform: linux-x64-glibc runner: ubuntu-latest - - platform: linux-x64-musl - runner: ubuntu-latest + # linux-x64-musl dropped: @embedded-postgres publishes NO musl + # build (only glibc linux-x64 + linux-arm64), and no + # AUTOPG_POSTGRES_URL_TEMPLATE repo var is configured, so every + # musl fetch died "no @embedded-postgres pkg ... set + # AUTOPG_POSTGRES_URL_TEMPLATE". A missing artifact also sinks + # sign-attest's aggregate (same failure mode as darwin-x64). + # Re-add when a musl postgres source (URL template / self-hosted + # cache) exists — tracked follow-up. - platform: linux-arm64 # GitHub-hosted ARM64 runners; falls back to QEMU emulation # if the org has not yet enabled ubuntu-24.04-arm runners. diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index fa5b81d..42552be 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -271,7 +271,7 @@ jobs: "version": "${VERSION}", "released_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "tarball_base": "https://github.com/${{ github.repository }}/releases/download/v${VERSION}", - "platforms": ["linux-x64-glibc","linux-x64-musl","linux-arm64","darwin-arm64"] + "platforms": ["linux-x64-glibc","linux-arm64","darwin-arm64"] } EOF done diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 676368b..642a889 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,12 @@ concurrency: permissions: contents: write + actions: write # the bump job's "Kick build-tarballs" step calls + # `gh workflow run build-tarballs.yml` — creating a + # workflow_dispatch event needs actions:write or the + # default GITHUB_TOKEN gets HTTP 403 "Resource not + # accessible by integration" (Bug #3 follow-up: the + # dispatch step failed for v3.0.3 with exactly this). id-token: write # required so the reusable `version.yml` workflow can mint # the OIDC token for npm Trusted Publishing — without this, # GH rejects the workflow at parse time (startup_failure) diff --git a/.github/workflows/sign-attest.yml b/.github/workflows/sign-attest.yml index 547acb0..c21d56d 100644 --- a/.github/workflows/sign-attest.yml +++ b/.github/workflows/sign-attest.yml @@ -81,7 +81,9 @@ jobs: matrix: platform: - linux-x64-glibc - - linux-x64-musl + # linux-x64-musl dropped in lockstep with build-tarballs.yml + # (no @embedded-postgres musl package). A download-artifact for a + # tarball that was never built fails this leg and skips aggregate. - linux-arm64 # darwin-x64 dropped in lockstep with build-tarballs.yml — see # the Bug #2 comment there. A `download-artifact` for a tarball diff --git a/package.json b/package.json index b86bc69..0576541 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "optionalDependencies": { "@embedded-postgres/darwin-arm64": "18.3.0-beta.17", "@embedded-postgres/darwin-x64": "18.3.0-beta.17", + "@embedded-postgres/linux-arm64": "18.3.0-beta.17", "@embedded-postgres/linux-x64": "18.3.0-beta.17", "@embedded-postgres/windows-x64": "18.3.0-beta.17" }, diff --git a/scripts/assemble-tarball.sh b/scripts/assemble-tarball.sh index e3c24be..b6325dd 100755 --- a/scripts/assemble-tarball.sh +++ b/scripts/assemble-tarball.sh @@ -165,7 +165,12 @@ assemble_one() { tar_flags+=(--owner=0 --group=0 --numeric-owner) fi - tar -C "$stage" -czf "$tarball" "${tar_flags[@]}" autopg/ || return 1 + # macOS ships bash 3.2, where `"${arr[@]}"` on an EMPTY array trips + # `set -u` ("unbound variable"). On macOS BSD-tar none of the GNU-only + # flags above match, so tar_flags is empty and every darwin-* assemble + # died here (pre-existing; only surfaced once builds reached this step). + # `${arr[@]+"${arr[@]}"}` expands to nothing when unset, the flags when set. + tar -C "$stage" -czf "$tarball" ${tar_flags[@]+"${tar_flags[@]}"} autopg/ || return 1 echo " ✓ tarball: $tarball ($(du -h "$tarball" | cut -f1))" # 4) outer SHA256 — Group 8 cosign-signs this; Group 9 publishes both. diff --git a/scripts/fetch-postgres-bins.sh b/scripts/fetch-postgres-bins.sh index 51589ad..4587c49 100755 --- a/scripts/fetch-postgres-bins.sh +++ b/scripts/fetch-postgres-bins.sh @@ -78,7 +78,10 @@ embedded_pkg_for() { case "$1" in linux-x64-glibc) echo "linux-x64" ;; linux-x64-musl) echo "" ;; - linux-arm64) echo "" ;; + # @embedded-postgres/linux-arm64 IS published (same 18.3.0-beta.* line + # as linux-x64); the mapping was just never wired, so every arm64 + # release build failed "no @embedded-postgres pkg for linux-arm64". + linux-arm64) echo "linux-arm64" ;; darwin-x64) echo "darwin-x64" ;; darwin-arm64) echo "darwin-arm64" ;; *) return 1 ;; From e84d5b3d1e280118d6313d243973c6aac7644b38 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 19 May 2026 17:18:16 +0000 Subject: [PATCH 08/18] [skip ci] release v3.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0576541..a6a9788 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autopg", - "version": "3.0.3", + "version": "3.0.4", "description": "Embedded PostgreSQL server with true concurrent connections - zero config, auto-provision databases", "main": "src/index.js", "type": "module", From bc3784aa4495e6f04dd038c714699d67dbe809a0 Mon Sep 17 00:00:00 2001 From: "Claude (Opus 4.7)" Date: Tue, 19 May 2026 14:34:19 -0300 Subject: [PATCH 09/18] fix(release): verify provenance with `gh attestation verify`, not slsa-verifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Blocker #9 — the reason no v3 ever published even after signing. The v3.0.4 chain proved #1a/#1b/#6/#8: all 3 Build jobs green, all 3 cosign keyless signatures + SLSA provenances produced. sign-attest's `aggregate` then died in `verify-published-artifacts.sh`: ✗ slsa-verifier FAILED (pass=9 fail=3) Real error (captured with the v3.0.4 bundles + slsa-verifier v2.6.0, not theorized): FAILED: matching bundle entry with content: unexpected tlog entry type: expected intoto:0.0.2, got dsse:0.0.1 `slsa-verifier verify-artifact` only understands slsa-github-generator provenance (Rekor `intoto:0.0.2`). The provenance here is produced by `actions/attest-build-provenance` (predicateType slsa.dev/provenance/v1, buildType actions.github.io/buildtypes/workflow/v1, Rekor `dsse:0.0.1`). slsa-verifier rejects every such bundle — `--builder-id` doesn't help; it's a format/tlog-type incompatibility, not a flag gap. This gate could never have gone green on a real release. Fix: verify with `gh attestation verify` (the first-class verifier for attest-build-provenance), offline via `--bundle`, anchored to the same Fulcio identity regex + OIDC issuer the cosign keyless check enforces so a stray attestation can't satisfy the gate. Drop the now-dead slsa-verifier installer; gh is preinstalled on GitHub runners; give the verify step GH_TOKEN. Proven against the real v3.0.4 signed bundles (download from sign-attest run 26113708890): linux-x64-glibc / linux-arm64 / darwin-arm64 all verify exit 0; a byte-flipped tarball fails exit 1. shellcheck clean; fixture sign-attest path untouched (keyed + --skip-slsa). Note (separate, documented): release.yml's bump dispatches build-tarballs via GITHUB_TOKEN; GitHub does not cascade `workflow_run` from a GITHUB_TOKEN-initiated run, so build→sign→publish must currently be hand-driven with a PAT. Auto-cascade is a tracked follow-up; not blind-implemented here (untestable under the merge constraints). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/sign-attest.yml | 8 ++++++-- scripts/verify-published-artifacts.sh | 26 +++++++++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/.github/workflows/sign-attest.yml b/.github/workflows/sign-attest.yml index c21d56d..e286332 100644 --- a/.github/workflows/sign-attest.yml +++ b/.github/workflows/sign-attest.yml @@ -254,8 +254,11 @@ jobs: with: cosign-release: 'v2.4.1' - - name: Install slsa-verifier - uses: slsa-framework/slsa-verifier/actions/installer@v2.6.0 + # slsa-verifier removed: it only verifies slsa-github-generator + # (intoto:0.0.2) provenance and rejects every actions/attest-build- + # provenance bundle ("unexpected tlog entry type: got dsse:0.0.1"). + # verify-published-artifacts.sh now uses `gh attestation verify` + # (gh is preinstalled on GitHub runners). - name: Aggregate manifest.json shell: bash @@ -269,6 +272,7 @@ jobs: shell: bash env: AUTOPG_SOURCE_URI: github.com/${{ github.repository }} + GH_TOKEN: ${{ github.token }} # gh attestation verify (provenance gate) run: | bash scripts/verify-published-artifacts.sh dist/ diff --git a/scripts/verify-published-artifacts.sh b/scripts/verify-published-artifacts.sh index 2eba2e5..051b75e 100755 --- a/scripts/verify-published-artifacts.sh +++ b/scripts/verify-published-artifacts.sh @@ -112,8 +112,14 @@ require_tools() { exit 2 } if [[ "$SKIP_SLSA" -eq 0 ]]; then - if ! command -v slsa-verifier >/dev/null 2>&1; then - note "slsa-verifier not on PATH — provenance check will be skipped (pass --skip-slsa to silence)" + # Provenance is produced by actions/attest-build-provenance, whose Rekor + # entry is `dsse:0.0.1`. `slsa-verifier verify-artifact` only understands + # slsa-github-generator's `intoto:0.0.2` entries and fails every bundle + # with "unexpected tlog entry type" — that gate is why no v3 ever got + # past signing. `gh attestation verify` is the first-class verifier for + # attest-build-provenance bundles. + if ! command -v gh >/dev/null 2>&1; then + note "gh not on PATH — provenance check will be skipped (pass --skip-slsa to silence)" SKIP_SLSA=1 fi fi @@ -191,13 +197,19 @@ verify_slsa() { bad "$(basename "$tarball"): missing .intoto.jsonl provenance" return 1 fi - if slsa-verifier verify-artifact \ - "$tarball" \ - --provenance-path "$prov" \ - --source-uri "$SOURCE_URI" >/dev/null 2>&1; then + # SOURCE_URI is `github.com//`; gh wants `/`. + local repo="${SOURCE_URI#github.com/}" + # Offline bundle verification, anchored to the same Fulcio identity + + # OIDC issuer the cosign keyless check enforces, so a stray attestation + # from another workflow can't satisfy this gate. + if gh attestation verify "$tarball" \ + --bundle "$prov" \ + --repo "$repo" \ + --cert-identity-regex "$TRUST_REGEX" \ + --cert-oidc-issuer "$OIDC_ISSUER" >/dev/null 2>&1; then ok "$(basename "$tarball"): SLSA provenance verifies" else - bad "$(basename "$tarball"): slsa-verifier FAILED" + bad "$(basename "$tarball"): gh attestation verify FAILED" return 1 fi } From 3a20f40962f896f7dd272477880ec096d6d2ffe9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 19 May 2026 17:42:42 +0000 Subject: [PATCH 10/18] [skip ci] release v3.0.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a6a9788..7c9aa29 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autopg", - "version": "3.0.4", + "version": "3.0.5", "description": "Embedded PostgreSQL server with true concurrent connections - zero config, auto-provision databases", "main": "src/index.js", "type": "module", From c6e432f65cf94d02f237df26481645938354bdfa Mon Sep 17 00:00:00 2001 From: release-bot Date: Tue, 19 May 2026 17:47:12 +0000 Subject: [PATCH 11/18] =?UTF-8?q?chore(release):=20update=20manifests=20(s?= =?UTF-8?q?table)=20=E2=86=92=20v3.0.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .well-known/latest.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .well-known/latest.json diff --git a/.well-known/latest.json b/.well-known/latest.json new file mode 100644 index 0000000..f7888f9 --- /dev/null +++ b/.well-known/latest.json @@ -0,0 +1,8 @@ +{ + "schema_version": 1, + "channel": "stable", + "version": "3.0.5", + "released_at": "2026-05-19T17:47:12Z", + "tarball_base": "https://github.com/automagik-dev/autopg/releases/download/v3.0.5", + "platforms": ["linux-x64-glibc","linux-arm64","darwin-arm64"] +} From 1c15da7481779d5756f937fbf026d75d427fbd35 Mon Sep 17 00:00:00 2001 From: "Claude (Opus 4.7)" Date: Tue, 19 May 2026 16:17:20 -0300 Subject: [PATCH 12/18] fix(release): compile a unified CLI entry so the tarball autopg has all verbs (#10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Blocker #10 (found running the Phase-5 smoke on the published v3.0.5): the tarball `autopg` only knew --version/postmaster/serve. verify, doctor, install, etc. live in bin/autopg-wrapper.cjs + src/cli-install.cjs (the npm bin) and were never compiled in, so: - `autopg verify ` (Phase-5 smoke 2) exited 1 with a help dump - install.sh's own `"$INSTALL_DIR/autopg" install` is broken for the tarball distribution (never exercised — no v3 ever installed) The wrapper can't be the compile entry: it resolves an external bun from node_modules and spawns it on postgres-server.js — neither exists inside a single compiled binary. Fix: new bin/autopg-cli.js, the compile entry (build-binary.sh default ENTRY_POINT bin/postgres-server.js → bin/autopg-cli.js; knip.json entry updated). Mirrors the wrapper's dispatch MINUS the bun-spawn: - --version/-v → autopg (BUILD_VERSION/pkg) - install/operator verbs → src/cli-install.cjs dispatch() in-process, scriptPath=process.execPath so pm2 supervises ` postmaster …` - postmaster/serve/help/… → delegate to bin/postgres-server.js The npm path (wrapper bin) is untouched. Proven on the compiled binary: autopg --version → autopg 3.0.6 (exit 0) autopg verify → exit 0: "verified … as automagik-pgserve-release (cosign_signed)" autopg doctor → real diagnostics (commands/doctor.js) autopg install --help → cli-install usage (in-process, no daemon) autopg postmaster --help → postgres-server.js help Regression: tarball-smoke --real 11/0; fixture 11/0 ×3; shellcheck + eslint + knip clean. (`update` uses a computed __dirname require — pre-existing, non-smoke-critical, documented follow-up.) Co-Authored-By: Claude Opus 4.7 (1M context) --- bin/autopg-cli.js | 123 ++++++++++++++++++++++++++++++++++++++++ knip.json | 1 + scripts/build-binary.sh | 8 ++- 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 bin/autopg-cli.js diff --git a/bin/autopg-cli.js b/bin/autopg-cli.js new file mode 100644 index 0000000..67495b7 --- /dev/null +++ b/bin/autopg-cli.js @@ -0,0 +1,123 @@ +#!/usr/bin/env bun + +/** + * autopg-cli — the unified entry point for the COMPILED single-binary + * distribution (BRIEF-v3-build-fix blocker #10). + * + * `scripts/build-binary.sh` compiles THIS file with `bun build --compile`. + * The tarball's `autopg` IS this module. + * + * Why this exists: the npm package's bin (`bin/autopg-wrapper.cjs`) is a + * node launcher that resolves an external `bun` from node_modules and + * spawns it on `bin/postgres-server.js` for the long-running paths, while + * routing the pure-node operator verbs (install / verify / doctor / …) + * in-process through `src/cli-install.cjs`. That spawn-external-bun model + * cannot exist inside a single compiled binary — there is no node_modules + * and the binary already IS the runtime. Before this entry, the build + * compiled bare `postgres-server.js`, so the tarball `autopg` only knew + * `--version` / `postmaster` / `serve` and every operator verb (and + * install.sh's own final `autopg install`) exited 1 with a help dump. + * + * This entry mirrors the wrapper's dispatch, MINUS the bun-spawn: + * - `--version` / `-v` → print `autopg ` (exit 0) + * - install/operator verbs → src/cli-install.cjs `dispatch()` in + * process. The supervised postmaster + * command is THIS executable + * (`process.execPath`) invoked with + * `postmaster` — pm2 runs it under + * `--interpreter none`, and the binary + * handles `postmaster` natively. + * - postmaster/serve/help/… → delegate to bin/postgres-server.js + * (re-used as a module; it reads argv). + * + * Keep the verb set in sync with bin/autopg-wrapper.cjs's + * __installSubcommands — that file remains the npm-path dispatcher. + */ + +import cliInstall from '../src/cli-install.cjs'; +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; + +const args = process.argv.slice(2); +const sub = args[0]; + +// `autopg --version` / `-v` — MUST exit 0 with `autopg `. +// Same contract + version source as bin/postgres-server.js (the bun +// `--define BUILD_VERSION` literal in the compiled binary, package.json +// fallback otherwise). `typeof` on an undeclared id is the one safe form. +if (sub === '--version' || sub === '-v') { + process.stdout.write(`autopg ${resolveVersion()}\n`); + process.exit(0); +} + +// Mirror of bin/autopg-wrapper.cjs __installSubcommands (the authoritative +// npm-path routing). These are pure node + child_process — no bun, no +// running PG backend — so they run in-process here. +const INSTALL_SUBCOMMANDS = new Set([ + 'install', + 'uninstall', + 'status', + 'url', + 'port', + 'config', + 'update', + 'restart', + 'ui', + 'verify', + 'doctor', + 'trust', + 'gc', + 'provision', + 'create-app', +]); + +if (sub && INSTALL_SUBCOMMANDS.has(sub)) { + // In the compiled binary, the postmaster the supervisor (pm2) must run + // is THIS executable with `postmaster`. process.execPath is the compiled + // binary path; buildPm2StartArgs() does + // pm2 start --interpreter none -- postmaster … + // so pm2 execs ` postmaster …`, which the binary handles. + const self = process.execPath; + const result = cliInstall.dispatch(sub, process.argv.slice(3), { + scriptPath: self, + wrapperPath: self, + }); + + // dispatch() returns either a number (sync verbs) or a Promise (async + // verbs: uninstall/doctor/verify/trust/gc/provision/create-app/update). + // Mirror the wrapper's dual handling + the EADDRINUSE double-print guard. + if (result && typeof result.then === 'function') { + result.then( + (code) => process.exit(typeof code === 'number' ? code : 0), + (err) => { + if (err && err.code !== 'EADDRINUSE') { + process.stderr.write(`autopg: ${err?.message ?? err}\n`); + } + if (process.exitCode === undefined || process.exitCode === 0) { + process.exitCode = 1; + } + }, + ); + } else { + process.exit(typeof result === 'number' ? result : 0); + } +} else { + // postmaster / serve / --help / help / empty / unknown flags → + // bin/postgres-server.js owns this surface (it reads process.argv and + // dispatches, including its own `serve`→`postmaster` alias + the + // EX_USAGE-style unknown-verb exit). Re-used as a module so there is a + // single postmaster implementation. + await import('./postgres-server.js'); +} + +function resolveVersion() { + if (typeof BUILD_VERSION !== 'undefined' && BUILD_VERSION) return BUILD_VERSION; + try { + const here = dirname(fileURLToPath(import.meta.url)); + const pkg = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf8')); + return pkg.version || '0.0.0'; + } catch { + return '0.0.0'; + } +} diff --git a/knip.json b/knip.json index 0939d18..1e407fb 100644 --- a/knip.json +++ b/knip.json @@ -3,6 +3,7 @@ "entry": [ "src/index.js", "bin/postgres-server.js", + "bin/autopg-cli.js", "bin/autopg-wrapper.cjs", "src/update/index.js", "src/commands/**", diff --git a/scripts/build-binary.sh b/scripts/build-binary.sh index 1f49356..f86fbd8 100755 --- a/scripts/build-binary.sh +++ b/scripts/build-binary.sh @@ -28,7 +28,13 @@ set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" -ENTRY_POINT="${AUTOPG_ENTRY_POINT:-bin/postgres-server.js}" +# BRIEF-v3-build-fix #10: compile the UNIFIED CLI entry, not bare +# postgres-server.js. bin/autopg-cli.js routes the operator verbs +# (install/verify/doctor/…) through src/cli-install.cjs in-process and +# delegates postmaster/serve to postgres-server.js, so the tarball +# `autopg` has the full verb surface (and install.sh's own `autopg +# install` works). Was `bin/postgres-server.js` → only --version/postmaster. +ENTRY_POINT="${AUTOPG_ENTRY_POINT:-bin/autopg-cli.js}" DIST_DIR="${AUTOPG_DIST_DIR:-${REPO_ROOT}/dist}" FALLBACK_ENABLED="${AUTOPG_BUILD_FALLBACK:-0}" From b44e54a6e874eb6263df18b8fc40910d75b85d97 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 19 May 2026 19:41:42 +0000 Subject: [PATCH 13/18] [skip ci] release v3.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c9aa29..2e2bc58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autopg", - "version": "3.0.5", + "version": "3.0.6", "description": "Embedded PostgreSQL server with true concurrent connections - zero config, auto-provision databases", "main": "src/index.js", "type": "module", From 4f142e22bd7526f60e5823303e0f6504d82774de Mon Sep 17 00:00:00 2001 From: release-bot Date: Tue, 19 May 2026 19:45:14 +0000 Subject: [PATCH 14/18] =?UTF-8?q?chore(release):=20update=20manifests=20(s?= =?UTF-8?q?table)=20=E2=86=92=20v3.0.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .well-known/latest.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.well-known/latest.json b/.well-known/latest.json index f7888f9..9af5fb1 100644 --- a/.well-known/latest.json +++ b/.well-known/latest.json @@ -1,8 +1,8 @@ { "schema_version": 1, "channel": "stable", - "version": "3.0.5", - "released_at": "2026-05-19T17:47:12Z", - "tarball_base": "https://github.com/automagik-dev/autopg/releases/download/v3.0.5", + "version": "3.0.6", + "released_at": "2026-05-19T19:45:14Z", + "tarball_base": "https://github.com/automagik-dev/autopg/releases/download/v3.0.6", "platforms": ["linux-x64-glibc","linux-arm64","darwin-arm64"] } From 8e0c8cd918d383c5258f77158b1165b0bf340df8 Mon Sep 17 00:00:00 2001 From: namastex888 Date: Fri, 22 May 2026 16:55:30 +0000 Subject: [PATCH 15/18] fix(install): honor runtime.enablePgvector from settings.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pm2 supervisor entry registered by 'pgserve install' / 'autopg install' hardcoded the postmaster CLI args and never read runtime.enablePgvector from settings.json. As a result: autopg config set runtime.enablePgvector true was silently dropped — the postmaster's parsePostmasterArgs() never saw --pgvector, PostgresManager.enablePgvector stayed false, and _doEnsurePgvectorFiles never ran. Fresh installs therefore had no vector.so / vector.control on disk, and any consumer doing CREATE EXTENSION vector hit: ERROR: extension "vector" is not available HINT: The extension must first be installed on the system where PostgreSQL is running. Found by @khal-os/brain v1.61.x dogfood — brain assumes pgvector is available on the autopg socket and could not embed any documents without it. Fix: buildPm2StartArgs() now reads runtime.enablePgvector via the same loadEffectiveConfig() pipeline already used for supervision settings, and appends '--pgvector' to the postmaster args when true. Default behavior unchanged (settings.json absent or false -> no flag). Tests: - Negative: install without enablePgvector setting -> --pgvector NOT in pm2 postmaster args. - Positive: install with settings.json runtime.enablePgvector=true -> --pgvector IS in pm2 postmaster args. Both new tests pass. The 6 pre-existing test failures in pgserve url/port/status are unrelated host-XDG_RUNTIME_DIR leakage (reproduces on main without this change). Workaround for users on existing installs (until they re-run 'autopg install' or upgrade to a release with this fix): autopg config set runtime.enablePgvector true pm2 delete autopg-server autopg install --port --data --socket-dir --- src/cli-install.cjs | 60 +++++++++++++++++++++++++++++---------- tests/cli-install.test.js | 29 +++++++++++++++++++ 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/src/cli-install.cjs b/src/cli-install.cjs index 39c2624..0e5fed1 100644 --- a/src/cli-install.cjs +++ b/src/cli-install.cjs @@ -499,12 +499,56 @@ function getEffectiveSupervision() { } } +/** + * Read the effective `runtime.enablePgvector` setting from settings.json. + * Defaults to `false` (matches HARDENED_DEFAULTS behavior). Returns boolean. + * + * Precedence: defaults < settings.json < env (env wins via loadEffectiveConfig). + * + * This is what propagates `autopg config set runtime.enablePgvector true` into + * the pm2-supervised postmaster's CLI args. Without this read, settings.json's + * `enablePgvector` would be silently ignored and `--pgvector` would never reach + * the postmaster's `parsePostmasterArgs`, so `_doEnsurePgvectorFiles` (which + * downloads + stages vector.so on PG start when `enablePgvector` is true) would + * never run — leaving fresh installs without the extension files. See + * https://github.com/namastexlabs/pgserve/issues/ for the regression + * surfaced by `@khal-os/brain` v1.61.x dogfood. + */ +function getEffectiveEnablePgvector() { + try { + const { loadEffectiveConfig } = require('./settings-loader.cjs'); + const { settings } = loadEffectiveConfig(); + return settings?.runtime?.enablePgvector === true; + } catch { + return false; + } +} + function buildPm2StartArgs({ scriptPath, port, dataDir, socketDir }) { const logs = { out: path.join(getLogsDir(), `${PM2_PROCESS_NAME}-out.log`), error: path.join(getLogsDir(), `${PM2_PROCESS_NAME}-error.log`), }; const supervision = getEffectiveSupervision(); + const enablePgvector = getEffectiveEnablePgvector(); + const postmasterArgs = [ + // pgserve singleton (v2.4): pm2 supervises the postmaster directly via + // the `pgserve postmaster` subcommand — no router, no bun proxy, no + // daemon control socket. Postgres binds the canonical Unix socket + // under AND TCP natively. Operators connect via + // psql -h $XDG_RUNTIME_DIR/pgserve (Unix socket, no -p) + // psql -h 127.0.0.1 -p 5432 (canonical TCP) + 'postmaster', + '--port', + String(port), + '--data', + dataDir, + '--socket-dir', + socketDir, + '--log', + 'warn', + ]; + if (enablePgvector) postmasterArgs.push('--pgvector'); return [ 'start', scriptPath, @@ -532,21 +576,7 @@ function buildPm2StartArgs({ scriptPath, port, dataDir, socketDir }) { '--error', logs.error, '--', - // pgserve singleton (v2.4): pm2 supervises the postmaster directly via - // the `pgserve postmaster` subcommand — no router, no bun proxy, no - // daemon control socket. Postgres binds the canonical Unix socket - // under AND TCP natively. Operators connect via - // psql -h $XDG_RUNTIME_DIR/pgserve (Unix socket, no -p) - // psql -h 127.0.0.1 -p 5432 (canonical TCP) - 'postmaster', - '--port', - String(port), - '--data', - dataDir, - '--socket-dir', - socketDir, - '--log', - 'warn', + ...postmasterArgs, ]; } diff --git a/tests/cli-install.test.js b/tests/cli-install.test.js index 1562fe2..fd9c27e 100644 --- a/tests/cli-install.test.js +++ b/tests/cli-install.test.js @@ -619,6 +619,35 @@ describe('pgserve singleton (v2.4) — socket dir + admin.json supervisor record expect(scriptArgs[sdIdx + 1]).toBe(path.join(tmpXdg, 'pgserve')); }); + test('install does NOT pass --pgvector to the postmaster when settings.json omits it (default off)', () => { + runWithXdg(['install', '--no-ui']); + const calls = readCallLog(stubBin.calls); + const startCall = calls.find((c) => c[0] === 'start' && c.includes('autopg-server')); + const dashIdx = startCall.indexOf('--'); + const scriptArgs = startCall.slice(dashIdx + 1); + expect(scriptArgs).not.toContain('--pgvector'); + }); + + test('install passes --pgvector to the postmaster when settings.json has runtime.enablePgvector=true', () => { + // Pre-seed settings.json before install so loadEffectiveConfig sees it. + fs.mkdirSync(tmpHome, { recursive: true }); + fs.writeFileSync( + path.join(tmpHome, 'settings.json'), + JSON.stringify({ _schemaVersion: 1, runtime: { enablePgvector: true } }), + ); + runWithXdg(['install', '--no-ui']); + const calls = readCallLog(stubBin.calls); + const startCall = calls.find((c) => c[0] === 'start' && c.includes('autopg-server')); + const dashIdx = startCall.indexOf('--'); + const scriptArgs = startCall.slice(dashIdx + 1); + // --pgvector must reach the postmaster so PostgresManager.enablePgvector + // is true and _doEnsurePgvectorFiles runs on PG start. Without this, + // `autopg config set runtime.enablePgvector true` is silently dropped + // and consumers (e.g. @khal-os/brain) hit "type \"vector\" does not exist" + // at CREATE EXTENSION time. + expect(scriptArgs).toContain('--pgvector'); + }); + test('install writes admin.json with supervisor=pm2 + canonical socketDir + port', () => { runWithXdg(['install', '--no-ui']); const adminFile = path.join(tmpHome, 'admin.json'); From 1acd326f00dc1366b89d408858535a470acf8dc0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 22 May 2026 17:01:10 +0000 Subject: [PATCH 16/18] [skip ci] release v3.0.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e2bc58..a5dc58f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autopg", - "version": "3.0.6", + "version": "3.0.7", "description": "Embedded PostgreSQL server with true concurrent connections - zero config, auto-provision databases", "main": "src/index.js", "type": "module", From be5b8a5445ec3f893062dab0cc1914dd91161b02 Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 22 May 2026 17:18:27 +0000 Subject: [PATCH 17/18] =?UTF-8?q?chore(release):=20update=20manifests=20(s?= =?UTF-8?q?table)=20=E2=86=92=20v3.0.7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .well-known/latest.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.well-known/latest.json b/.well-known/latest.json index 9af5fb1..ea32c4f 100644 --- a/.well-known/latest.json +++ b/.well-known/latest.json @@ -1,8 +1,8 @@ { "schema_version": 1, "channel": "stable", - "version": "3.0.6", - "released_at": "2026-05-19T19:45:14Z", - "tarball_base": "https://github.com/automagik-dev/autopg/releases/download/v3.0.6", + "version": "3.0.7", + "released_at": "2026-05-22T17:18:27Z", + "tarball_base": "https://github.com/automagik-dev/autopg/releases/download/v3.0.7", "platforms": ["linux-x64-glibc","linux-arm64","darwin-arm64"] } From 91cef8fa392d2f63b0e4790d69527cb9bd09265d Mon Sep 17 00:00:00 2001 From: Automagik Genie Date: Thu, 4 Jun 2026 16:54:11 +0000 Subject: [PATCH 18/18] fix: route autopg auth through wrapper --- bin/autopg-wrapper.cjs | 2 ++ src/cli-ui.cjs | 7 +++++++ tests/cli-install.test.js | 7 +++++++ tests/console/no-cdn.test.js | 2 ++ 4 files changed, 18 insertions(+) diff --git a/bin/autopg-wrapper.cjs b/bin/autopg-wrapper.cjs index 614ba1a..9b747e5 100644 --- a/bin/autopg-wrapper.cjs +++ b/bin/autopg-wrapper.cjs @@ -52,6 +52,8 @@ const __installSubcommands = new Set([ 'update', 'restart', 'ui', + // Admin Basic Auth password rotation / admin path; pure node, same dispatcher. + 'auth', // pgserve singleton (v2.4) — `pgserve-singleton-no-proxy` wish, Group 4. // `verify` shells out to cosign + writes an HMAC cache token. Pure node // (no bun) so it must skip the bun probe like the install surface above. diff --git a/src/cli-ui.cjs b/src/cli-ui.cjs index cf591bb..e98a5bc 100644 --- a/src/cli-ui.cjs +++ b/src/cli-ui.cjs @@ -431,6 +431,13 @@ function handleStatic(req, res, root) { } fs.stat(target, (statErr, stat) => { if (statErr || !stat.isFile()) { + // Do not SPA-fallback concrete asset requests. If /app.js or a CSS + // file is missing, returning index.html as text/html creates strict + // module MIME failures in the browser and hides the real packaging bug. + if (path.extname(url)) { + sendError(res, 404, 'NOT_FOUND', `no file at ${url}`); + return; + } // SPA fallback: serve index.html on a miss so client routing works. const fallback = path.join(root, 'index.html'); if (fs.existsSync(fallback)) { diff --git a/tests/cli-install.test.js b/tests/cli-install.test.js index fd9c27e..111bb89 100644 --- a/tests/cli-install.test.js +++ b/tests/cli-install.test.js @@ -113,6 +113,13 @@ afterEach(() => { }); describe('pgserve install', () => { + test('autopg auth is routed by the wrapper before the bun probe', () => { + const result = runCli(['auth', 'show-admin-path']); + expect(result.status).toBe(0); + expect(result.stdout.trim()).toBe(path.join(tmpHome, 'admin.json')); + expect(result.stderr).not.toContain('unknown verb "auth"'); + }); + test('first install registers under pm2 and writes config', () => { const result = runCli(['install']); expect(result.status).toBe(0); diff --git a/tests/console/no-cdn.test.js b/tests/console/no-cdn.test.js index ad9ace9..b69429a 100644 --- a/tests/console/no-cdn.test.js +++ b/tests/console/no-cdn.test.js @@ -81,12 +81,14 @@ test('app.js bundle is reachable as static asset', async () => { // Both are valid — the test's job is to confirm if a bundle IS served, it's // local + valid JS. if (res.status === 200) { + expect(res.headers.get('content-type')).toMatch(/javascript/); const body = await res.text(); expect(body.length).toBeGreaterThan(1000); // bundle should be > 1KB expect(body).not.toMatch(/unpkg\.com/); expect(body).not.toMatch(/jsdelivr/); } else { // src/ fallback path — accept 404 as long as src/main.jsx exists for dev mode + expect(res.status).toBe(404); const distPresent = fs.existsSync(path.join(REPO_ROOT, 'console', 'dist', 'app.js')); if (distPresent) { throw new Error(`dist/app.js exists on disk but server returned ${res.status}`);