fix(desktop): harden embedded Postgres packaging
This commit is contained in:
7
.github/workflows/desktop-packaging.yml
vendored
7
.github/workflows/desktop-packaging.yml
vendored
@@ -71,6 +71,13 @@ jobs:
|
|||||||
if: steps.changes.outputs.relevant == 'true'
|
if: steps.changes.outputs.relevant == 'true'
|
||||||
run: pnpm --filter @fusion/desktop build
|
run: pnpm --filter @fusion/desktop build
|
||||||
|
|
||||||
|
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:39:
|
||||||
|
# The advisory Linux packaging lane also executes the real bundled
|
||||||
|
# lifecycle, catching native payload regressions before a release run.
|
||||||
|
- name: Smoke embedded Postgres lifecycle
|
||||||
|
if: steps.changes.outputs.relevant == 'true'
|
||||||
|
run: pnpm --filter @fusion/core test:embedded-postgres
|
||||||
|
|
||||||
# Authoritative check: electron-builder's production-dependency walk over the staged closure.
|
# Authoritative check: electron-builder's production-dependency walk over the staged closure.
|
||||||
# --dir skips installer/signing but still FAILS if any production dependency's declared version
|
# --dir skips installer/signing but still FAILS if any production dependency's declared version
|
||||||
# range is unsatisfied in the closure — which is exactly the aws-sdk skew that broke the release.
|
# range is unsatisfied in the closure — which is exactly the aws-sdk skew that broke the release.
|
||||||
|
|||||||
10
.github/workflows/desktop-windows.yml
vendored
10
.github/workflows/desktop-windows.yml
vendored
@@ -22,6 +22,12 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:39:
|
||||||
|
# The manual Windows installer path must boot the same embedded database
|
||||||
|
# payload used by Local mode before it can publish an installer artifact.
|
||||||
|
- name: Smoke embedded Postgres on Windows
|
||||||
|
run: pnpm --filter @fusion/core test:embedded-postgres
|
||||||
|
|
||||||
# FNXC:WindowsDesktopPackaging 2026-07-01-19:45:
|
# FNXC:WindowsDesktopPackaging 2026-07-01-19:45:
|
||||||
# Mirror release.yml: build every workspace package's tsc dist (incl.
|
# Mirror release.yml: build every workspace package's tsc dist (incl.
|
||||||
# @fusion/core and @fusion/engine, which are gitignored) before packaging.
|
# @fusion/core and @fusion/engine, which are gitignored) before packaging.
|
||||||
@@ -38,7 +44,7 @@ jobs:
|
|||||||
# Code-signing hardening is intentionally deferred to FN-5592.
|
# Code-signing hardening is intentionally deferred to FN-5592.
|
||||||
- name: Package signed Windows EXE
|
- name: Package signed Windows EXE
|
||||||
if: ${{ env.WINDOWS_CERTIFICATE_BASE64 != '' }}
|
if: ${{ env.WINDOWS_CERTIFICATE_BASE64 != '' }}
|
||||||
run: pnpm --filter @fusion/desktop exec electron-builder --projectDir deploy --win --x64 --arm64 --publish never
|
run: pnpm --filter @fusion/desktop exec electron-builder --projectDir deploy --win --x64 --publish never
|
||||||
env:
|
env:
|
||||||
WINDOWS_CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }}
|
WINDOWS_CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }}
|
||||||
CSC_LINK: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }}
|
CSC_LINK: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }}
|
||||||
@@ -48,7 +54,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Package unsigned Windows EXE
|
- name: Package unsigned Windows EXE
|
||||||
if: ${{ env.WINDOWS_CERTIFICATE_BASE64 == '' }}
|
if: ${{ env.WINDOWS_CERTIFICATE_BASE64 == '' }}
|
||||||
run: pnpm --filter @fusion/desktop exec electron-builder --projectDir deploy --win --x64 --arm64 --publish never
|
run: pnpm --filter @fusion/desktop exec electron-builder --projectDir deploy --win --x64 --publish never
|
||||||
env:
|
env:
|
||||||
WINDOWS_CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }}
|
WINDOWS_CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }}
|
||||||
CSC_IDENTITY_AUTO_DISCOVERY: "false"
|
CSC_IDENTITY_AUTO_DISCOVERY: "false"
|
||||||
|
|||||||
19
.github/workflows/release.yml
vendored
19
.github/workflows/release.yml
vendored
@@ -121,7 +121,8 @@ jobs:
|
|||||||
|
|
||||||
# ── Build Windows desktop EXE artifacts ──────────────────────────────
|
# ── Build Windows desktop EXE artifacts ──────────────────────────────
|
||||||
# Code-signing with WINDOWS_CERTIFICATE_BASE64 / WINDOWS_CERTIFICATE_PASSWORD
|
# Code-signing with WINDOWS_CERTIFICATE_BASE64 / WINDOWS_CERTIFICATE_PASSWORD
|
||||||
# is intentionally deferred to FN-5592; ARM64 support is tracked in FN-5594.
|
# is intentionally deferred to FN-5592. Desktop Windows remains x64 because
|
||||||
|
# embedded-postgres does not publish a native Windows ARM64 payload.
|
||||||
build-desktop-windows:
|
build-desktop-windows:
|
||||||
name: Build Desktop Windows EXE
|
name: Build Desktop Windows EXE
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
@@ -133,6 +134,12 @@ jobs:
|
|||||||
- name: Setup Node and install dependencies
|
- name: Setup Node and install dependencies
|
||||||
uses: ./.github/actions/setup-node-pnpm
|
uses: ./.github/actions/setup-node-pnpm
|
||||||
|
|
||||||
|
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:39:
|
||||||
|
# Every Windows desktop release must prove its bundled default database can
|
||||||
|
# initialize, serve SQL, persist across restart, and stop without orphans.
|
||||||
|
- name: Smoke embedded Postgres on Windows
|
||||||
|
run: pnpm --filter @fusion/core test:embedded-postgres
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
@@ -188,6 +195,11 @@ jobs:
|
|||||||
- name: Setup Node and install dependencies
|
- name: Setup Node and install dependencies
|
||||||
uses: ./.github/actions/setup-node-pnpm
|
uses: ./.github/actions/setup-node-pnpm
|
||||||
|
|
||||||
|
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:39:
|
||||||
|
# Exercise the native payload on the release host before signing artifacts.
|
||||||
|
- name: Smoke embedded Postgres on macOS
|
||||||
|
run: pnpm --filter @fusion/core test:embedded-postgres
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
@@ -287,6 +299,11 @@ jobs:
|
|||||||
- name: Setup Node and install dependencies
|
- name: Setup Node and install dependencies
|
||||||
uses: ./.github/actions/setup-node-pnpm
|
uses: ./.github/actions/setup-node-pnpm
|
||||||
|
|
||||||
|
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:39:
|
||||||
|
# Exercise the native payload on the release host before packaging artifacts.
|
||||||
|
- name: Smoke embedded Postgres on Linux
|
||||||
|
run: pnpm --filter @fusion/core test:embedded-postgres
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
|
|||||||
14
.github/workflows/test-release.yml
vendored
14
.github/workflows/test-release.yml
vendored
@@ -113,7 +113,8 @@ jobs:
|
|||||||
|
|
||||||
# ── Build Windows desktop EXE artifacts ──────────────────────────────
|
# ── Build Windows desktop EXE artifacts ──────────────────────────────
|
||||||
# Code-signing with WINDOWS_CERTIFICATE_BASE64 / WINDOWS_CERTIFICATE_PASSWORD
|
# Code-signing with WINDOWS_CERTIFICATE_BASE64 / WINDOWS_CERTIFICATE_PASSWORD
|
||||||
# is intentionally deferred to FN-5592; ARM64 support is tracked in FN-5594.
|
# is intentionally deferred to FN-5592. Desktop Windows remains x64 because
|
||||||
|
# embedded-postgres does not publish a native Windows ARM64 payload.
|
||||||
build-desktop-windows:
|
build-desktop-windows:
|
||||||
name: Build Desktop Windows EXE
|
name: Build Desktop Windows EXE
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
@@ -125,6 +126,11 @@ jobs:
|
|||||||
- name: Setup Node and install dependencies
|
- name: Setup Node and install dependencies
|
||||||
uses: ./.github/actions/setup-node-pnpm
|
uses: ./.github/actions/setup-node-pnpm
|
||||||
|
|
||||||
|
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:39:
|
||||||
|
# Test releases use the same native database proof as production releases.
|
||||||
|
- name: Smoke embedded Postgres on Windows
|
||||||
|
run: pnpm --filter @fusion/core test:embedded-postgres
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
@@ -177,6 +183,9 @@ jobs:
|
|||||||
- name: Setup Node and install dependencies
|
- name: Setup Node and install dependencies
|
||||||
uses: ./.github/actions/setup-node-pnpm
|
uses: ./.github/actions/setup-node-pnpm
|
||||||
|
|
||||||
|
- name: Smoke embedded Postgres on macOS
|
||||||
|
run: pnpm --filter @fusion/core test:embedded-postgres
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
@@ -276,6 +285,9 @@ jobs:
|
|||||||
- name: Setup Node and install dependencies
|
- name: Setup Node and install dependencies
|
||||||
uses: ./.github/actions/setup-node-pnpm
|
uses: ./.github/actions/setup-node-pnpm
|
||||||
|
|
||||||
|
- name: Smoke embedded Postgres on Linux
|
||||||
|
run: pnpm --filter @fusion/core test:embedded-postgres
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"test": "vitest run --silent=passed-only --reporter=dot",
|
"test": "vitest run --silent=passed-only --reporter=dot",
|
||||||
|
"test:embedded-postgres": "vitest run src/__tests__/postgres/embedded-lifecycle.test.ts --silent=passed-only --reporter=dot",
|
||||||
"test:pg-gate": "vitest run src/__tests__/postgres/handoff-to-review-atomicity.pg.test.ts src/__tests__/postgres/store-list.pg.test.ts src/__tests__/postgres/task-lifecycle-e2e.pg.test.ts src/__tests__/postgres/soft-delete-resurrection-FN-5233.pg.test.ts src/__tests__/postgres/agent-logs-and-monitor.pg.test.ts src/__tests__/postgres/todo-store.pg.test.ts src/__tests__/postgres/workflow-definitions.pg.test.ts src/__tests__/postgres/message-store.pg.test.ts src/__tests__/postgres/insight-store.pg.test.ts src/__tests__/postgres/insight-run-execution.pg.test.ts src/__tests__/postgres/research-store.pg.test.ts src/__tests__/postgres/mission-store.pg.test.ts src/__tests__/postgres/goal-store.pg.test.ts src/__tests__/postgres/artifacts-documents-evals.pg.test.ts src/__tests__/postgres/command-center-analytics.pg.test.ts src/__tests__/postgres/command-center-remaining-analytics.pg.test.ts src/__tests__/postgres/research-execution.pg.test.ts src/__tests__/postgres/async-store-events.pg.test.ts src/__tests__/postgres/signal-ingestion.pg.test.ts src/__tests__/postgres/mission-autopilot.pg.test.ts src/__tests__/postgres/workflow-create.pg.test.ts src/__tests__/postgres/monitor-trait-storm-guard.pg.test.ts src/__tests__/postgres/agent-wake-getagent.pg.test.ts --silent=passed-only --reporter=dot"
|
"test:pg-gate": "vitest run src/__tests__/postgres/handoff-to-review-atomicity.pg.test.ts src/__tests__/postgres/store-list.pg.test.ts src/__tests__/postgres/task-lifecycle-e2e.pg.test.ts src/__tests__/postgres/soft-delete-resurrection-FN-5233.pg.test.ts src/__tests__/postgres/agent-logs-and-monitor.pg.test.ts src/__tests__/postgres/todo-store.pg.test.ts src/__tests__/postgres/workflow-definitions.pg.test.ts src/__tests__/postgres/message-store.pg.test.ts src/__tests__/postgres/insight-store.pg.test.ts src/__tests__/postgres/insight-run-execution.pg.test.ts src/__tests__/postgres/research-store.pg.test.ts src/__tests__/postgres/mission-store.pg.test.ts src/__tests__/postgres/goal-store.pg.test.ts src/__tests__/postgres/artifacts-documents-evals.pg.test.ts src/__tests__/postgres/command-center-analytics.pg.test.ts src/__tests__/postgres/command-center-remaining-analytics.pg.test.ts src/__tests__/postgres/research-execution.pg.test.ts src/__tests__/postgres/async-store-events.pg.test.ts src/__tests__/postgres/signal-ingestion.pg.test.ts src/__tests__/postgres/mission-autopilot.pg.test.ts src/__tests__/postgres/workflow-create.pg.test.ts src/__tests__/postgres/monitor-trait-storm-guard.pg.test.ts src/__tests__/postgres/agent-wake-getagent.pg.test.ts --silent=passed-only --reporter=dot"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -362,8 +362,8 @@ Desktop packaging is configured in `electron-builder.yml`.
|
|||||||
|
|
||||||
- Output directory: `packages/desktop/dist-electron`
|
- Output directory: `packages/desktop/dist-electron`
|
||||||
- Targets: macOS (`dmg`, `zip`), Windows (`nsis`, `portable`), Linux (`AppImage`, `deb`, `tar.gz`)
|
- Targets: macOS (`dmg`, `zip`), Windows (`nsis`, `portable`), Linux (`AppImage`, `deb`, `tar.gz`)
|
||||||
- Windows NSIS installer artifacts: `Fusion-<version>-win-x64.exe` and `Fusion-<version>-win-arm64.exe` in `packages/desktop/dist-electron/`
|
- Windows NSIS installer artifact: `Fusion-<version>-win-x64.exe` in `packages/desktop/dist-electron/`
|
||||||
- Windows portable artifacts: `Fusion-<version>-win-x64-portable.exe` and `Fusion-<version>-win-arm64-portable.exe` in `packages/desktop/dist-electron/`
|
- Windows portable artifact: `Fusion-<version>-win-x64-portable.exe` in `packages/desktop/dist-electron/`
|
||||||
- Silent NSIS installs support a custom destination with `/S /D=<absolute path>`; keep `/D=...` as the final installer argument (for example, `Fusion-<version>-win-x64.exe /S /D=C:\\Users\\me\\Tools\\fusion`).
|
- Silent NSIS installs support a custom destination with `/S /D=<absolute path>`; keep `/D=...` as the final installer argument (for example, `Fusion-<version>-win-x64.exe /S /D=C:\\Users\\me\\Tools\\fusion`).
|
||||||
- The Windows packaging workflow verifies `win*-unpacked` contains Electron root runtime resources (`chrome_100_percent.pak`, `chrome_200_percent.pak`, and `resources.pak`) before uploading artifacts.
|
- The Windows packaging workflow verifies `win*-unpacked` contains Electron root runtime resources (`chrome_100_percent.pak`, `chrome_200_percent.pak`, and `resources.pak`) before uploading artifacts.
|
||||||
- Binary GitHub Release workflow (`.github/workflows/release.yml`) now attaches desktop artifacts for all supported platforms:
|
- Binary GitHub Release workflow (`.github/workflows/release.yml`) now attaches desktop artifacts for all supported platforms:
|
||||||
@@ -405,8 +405,8 @@ gpg --import KEYS && gpg --verify Fusion-<version>-linux-x64.AppImage.asc Fusion
|
|||||||
```
|
```
|
||||||
|
|
||||||
The public key (`KEYS`) must be distributed out-of-band. See `docs/CODE_SIGNING.md` for canonical setup, key publication, and troubleshooting guidance.
|
The public key (`KEYS`) must be distributed out-of-band. See `docs/CODE_SIGNING.md` for canonical setup, key publication, and troubleshooting guidance.
|
||||||
- Isolated manual Windows build path: `.github/workflows/desktop-windows.yml` (`workflow_dispatch` on `windows-latest`) runs `electron-builder --win --x64 --arm64 --publish never`.
|
- Isolated manual Windows build path: `.github/workflows/desktop-windows.yml` (`workflow_dispatch` on `windows-latest`) runs `electron-builder --win --x64 --publish never`.
|
||||||
- ARM64 artifacts are cross-built on the `windows-latest` x64 runner; execution/validation still requires a Windows ARM64 device or emulator.
|
- Windows desktop artifacts are x64. Windows ARM64 users run the x64 build through Windows compatibility because upstream `embedded-postgres` does not publish a native Windows ARM64 payload.
|
||||||
- Deep link protocol: `fusion://`
|
- Deep link protocol: `fusion://`
|
||||||
- Publish provider: GitHub (`gsxdsm/fusion`)
|
- Publish provider: GitHub (`gsxdsm/fusion`)
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,12 @@ files:
|
|||||||
- node_modules/tar-fs/**/*
|
- node_modules/tar-fs/**/*
|
||||||
- node_modules/uuid/**/*
|
- node_modules/uuid/**/*
|
||||||
|
|
||||||
asarUnpack: []
|
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:30:
|
||||||
|
# Embedded Postgres launches native initdb/pg_ctl/postgres child processes.
|
||||||
|
# Executables and their shared libraries must live on the real filesystem;
|
||||||
|
# processes cannot execute payloads directly from Electron's virtual app.asar.
|
||||||
|
asarUnpack:
|
||||||
|
- "node_modules/@embedded-postgres/*/native/**/*"
|
||||||
|
|
||||||
extraMetadata:
|
extraMetadata:
|
||||||
main: dist/main.js
|
main: dist/main.js
|
||||||
@@ -116,7 +121,13 @@ mac:
|
|||||||
minimumSystemVersion: "10.15"
|
minimumSystemVersion: "10.15"
|
||||||
target:
|
target:
|
||||||
- target: dmg
|
- target: dmg
|
||||||
|
arch:
|
||||||
|
- x64
|
||||||
|
- arm64
|
||||||
- target: zip
|
- target: zip
|
||||||
|
arch:
|
||||||
|
- x64
|
||||||
|
- arm64
|
||||||
hardenedRuntime: true
|
hardenedRuntime: true
|
||||||
gatekeeperAssess: false
|
gatekeeperAssess: false
|
||||||
entitlements: build/entitlements.mac.plist
|
entitlements: build/entitlements.mac.plist
|
||||||
@@ -124,15 +135,18 @@ mac:
|
|||||||
notarize: true
|
notarize: true
|
||||||
|
|
||||||
win:
|
win:
|
||||||
|
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:30:
|
||||||
|
# embedded-postgres 15.18 publishes only @embedded-postgres/windows-x64 and
|
||||||
|
# throws on win32/arm64. Do not publish an ARM64 installer whose default local
|
||||||
|
# database cannot start; Windows ARM64 can use the supported x64 installer via
|
||||||
|
# the OS compatibility layer until upstream ships a native ARM64 payload.
|
||||||
target:
|
target:
|
||||||
- target: nsis
|
- target: nsis
|
||||||
arch:
|
arch:
|
||||||
- x64
|
- x64
|
||||||
- arm64
|
|
||||||
- target: portable
|
- target: portable
|
||||||
arch:
|
arch:
|
||||||
- x64
|
- x64
|
||||||
- arm64
|
|
||||||
signtoolOptions:
|
signtoolOptions:
|
||||||
signingHashAlgorithms:
|
signingHashAlgorithms:
|
||||||
- sha256
|
- sha256
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { spawn } from "node:child_process";
|
import { spawn } from "node:child_process";
|
||||||
import { dirname, resolve } from "node:path";
|
import { dirname, resolve } from "node:path";
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
import { cp, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
@@ -143,6 +143,72 @@ export const desktopDeployDir = process.env.FUSION_DESKTOP_DEPLOY_DIR
|
|||||||
? resolve(process.env.FUSION_DESKTOP_DEPLOY_DIR)
|
? resolve(process.env.FUSION_DESKTOP_DEPLOY_DIR)
|
||||||
: resolve(packageRoot, "deploy");
|
: resolve(packageRoot, "deploy");
|
||||||
|
|
||||||
|
export type EmbeddedPostgresDesktopPlatform = "darwin" | "linux" | "win32";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FNXC:DesktopEmbeddedPostgres 2026-07-14-09:30:
|
||||||
|
* These are the native Postgres payloads shipped by each desktop release job.
|
||||||
|
* macOS and Linux jobs cross-build x64 and ARM64 from one staged closure;
|
||||||
|
* Windows is x64-only because embedded-postgres has no Windows ARM64 binary.
|
||||||
|
*/
|
||||||
|
export function requiredEmbeddedPostgresPackages(
|
||||||
|
platform: EmbeddedPostgresDesktopPlatform,
|
||||||
|
): readonly string[] {
|
||||||
|
switch (platform) {
|
||||||
|
case "darwin":
|
||||||
|
return ["@embedded-postgres/darwin-x64", "@embedded-postgres/darwin-arm64"];
|
||||||
|
case "linux":
|
||||||
|
return ["@embedded-postgres/linux-x64", "@embedded-postgres/linux-arm64"];
|
||||||
|
case "win32":
|
||||||
|
return ["@embedded-postgres/windows-x64"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FNXC:DesktopEmbeddedPostgres 2026-07-14-09:30:
|
||||||
|
* A desktop build is incomplete unless the staged production closure contains
|
||||||
|
* embedded-postgres plus every native executable needed by the artifacts that
|
||||||
|
* job emits. Fail before electron-builder so a host-only optional-dependency
|
||||||
|
* install cannot silently produce an app that crashes on another architecture.
|
||||||
|
*/
|
||||||
|
export async function verifyEmbeddedPostgresPayloads(
|
||||||
|
deployDir = desktopDeployDir,
|
||||||
|
platform: EmbeddedPostgresDesktopPlatform = process.platform as EmbeddedPostgresDesktopPlatform,
|
||||||
|
): Promise<void> {
|
||||||
|
if (platform !== "darwin" && platform !== "linux" && platform !== "win32") {
|
||||||
|
throw new Error(`Unsupported desktop platform for embedded Postgres: ${platform}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const executableSuffix = platform === "win32" ? ".exe" : "";
|
||||||
|
const requiredPaths = [
|
||||||
|
resolve(deployDir, "node_modules", "embedded-postgres", "package.json"),
|
||||||
|
...requiredEmbeddedPostgresPackages(platform).flatMap((packageName) => {
|
||||||
|
const packageRoot = resolve(deployDir, "node_modules", ...packageName.split("/"));
|
||||||
|
return [
|
||||||
|
resolve(packageRoot, "package.json"),
|
||||||
|
resolve(packageRoot, "native", "bin", `initdb${executableSuffix}`),
|
||||||
|
resolve(packageRoot, "native", "bin", `pg_ctl${executableSuffix}`),
|
||||||
|
resolve(packageRoot, "native", "bin", `postgres${executableSuffix}`),
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const missing: string[] = [];
|
||||||
|
for (const requiredPath of requiredPaths) {
|
||||||
|
try {
|
||||||
|
await stat(requiredPath);
|
||||||
|
} catch {
|
||||||
|
missing.push(requiredPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (missing.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Desktop embedded Postgres payload is incomplete for ${platform}: ${missing.join(", ")}. ` +
|
||||||
|
"Run pnpm install with pnpm-workspace.yaml supportedArchitectures before packaging.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function stageDesktopDeploy(): Promise<void> {
|
export async function stageDesktopDeploy(): Promise<void> {
|
||||||
console.log("[desktop:build] Staging complete production closure via pnpm deploy...");
|
console.log("[desktop:build] Staging complete production closure via pnpm deploy...");
|
||||||
await rm(desktopDeployDir, { recursive: true, force: true });
|
await rm(desktopDeployDir, { recursive: true, force: true });
|
||||||
@@ -164,6 +230,7 @@ export async function stageDesktopDeploy(): Promise<void> {
|
|||||||
? config.replace(/output:\s*dist-electron/, "output: ../dist-electron")
|
? config.replace(/output:\s*dist-electron/, "output: ../dist-electron")
|
||||||
: `directories:\n output: ../dist-electron\n${config}`;
|
: `directories:\n output: ../dist-electron\n${config}`;
|
||||||
await writeFile(stagedConfig, patched);
|
await writeFile(stagedConfig, patched);
|
||||||
|
await verifyEmbeddedPostgresPayloads();
|
||||||
console.log(`[desktop:build] Deploy staged at ${desktopDeployDir}`);
|
console.log(`[desktop:build] Deploy staged at ${desktopDeployDir}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { readFile } from "node:fs/promises";
|
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
import {
|
||||||
|
requiredEmbeddedPostgresPackages,
|
||||||
|
verifyEmbeddedPostgresPayloads,
|
||||||
|
} from "../../scripts/workspace-tools";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
@@ -59,3 +64,55 @@ describe("desktop Electron main bundling", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("desktop embedded Postgres payload", () => {
|
||||||
|
/**
|
||||||
|
* FNXC:DesktopEmbeddedPostgres 2026-07-14-09:31:
|
||||||
|
* Desktop packaging must prove the zero-config database payload for every
|
||||||
|
* emitted architecture, not merely that Electron can assemble an installer.
|
||||||
|
*/
|
||||||
|
it("maps release hosts to supported native database packages", () => {
|
||||||
|
expect(requiredEmbeddedPostgresPackages("darwin")).toEqual([
|
||||||
|
"@embedded-postgres/darwin-x64",
|
||||||
|
"@embedded-postgres/darwin-arm64",
|
||||||
|
]);
|
||||||
|
expect(requiredEmbeddedPostgresPackages("linux")).toEqual([
|
||||||
|
"@embedded-postgres/linux-x64",
|
||||||
|
"@embedded-postgres/linux-arm64",
|
||||||
|
]);
|
||||||
|
expect(requiredEmbeddedPostgresPackages("win32")).toEqual([
|
||||||
|
"@embedded-postgres/windows-x64",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects a staged desktop closure missing a native executable", async () => {
|
||||||
|
const deployDir = await mkdtemp(path.join(tmpdir(), "fusion-desktop-pg-payload-"));
|
||||||
|
try {
|
||||||
|
await mkdir(path.join(deployDir, "node_modules", "embedded-postgres"), { recursive: true });
|
||||||
|
await writeFile(
|
||||||
|
path.join(deployDir, "node_modules", "embedded-postgres", "package.json"),
|
||||||
|
"{}",
|
||||||
|
);
|
||||||
|
|
||||||
|
const packageRoot = path.join(
|
||||||
|
deployDir,
|
||||||
|
"node_modules",
|
||||||
|
"@embedded-postgres",
|
||||||
|
"windows-x64",
|
||||||
|
);
|
||||||
|
await mkdir(path.join(packageRoot, "native", "bin"), { recursive: true });
|
||||||
|
await writeFile(path.join(packageRoot, "package.json"), "{}");
|
||||||
|
await writeFile(path.join(packageRoot, "native", "bin", "initdb.exe"), "");
|
||||||
|
await writeFile(path.join(packageRoot, "native", "bin", "pg_ctl.exe"), "");
|
||||||
|
|
||||||
|
await expect(verifyEmbeddedPostgresPayloads(deployDir, "win32")).rejects.toThrow(
|
||||||
|
/postgres\.exe/,
|
||||||
|
);
|
||||||
|
|
||||||
|
await writeFile(path.join(packageRoot, "native", "bin", "postgres.exe"), "");
|
||||||
|
await expect(verifyEmbeddedPostgresPayloads(deployDir, "win32")).resolves.toBeUndefined();
|
||||||
|
} finally {
|
||||||
|
await rm(deployDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ describe("electron-builder desktop config", () => {
|
|||||||
const extractArchValues = (archBlock: string) =>
|
const extractArchValues = (archBlock: string) =>
|
||||||
Array.from(archBlock.matchAll(/-\s*(x64|arm64)/g), (match) => match[1]).sort();
|
Array.from(archBlock.matchAll(/-\s*(x64|arm64)/g), (match) => match[1]).sort();
|
||||||
|
|
||||||
expect(extractArchValues(nsisArchMatch![1])).toEqual(["arm64", "x64"]);
|
expect(extractArchValues(nsisArchMatch![1])).toEqual(["x64"]);
|
||||||
expect(extractArchValues(portableArchMatch![1])).toEqual(["arm64", "x64"]);
|
expect(extractArchValues(portableArchMatch![1])).toEqual(["x64"]);
|
||||||
|
|
||||||
expect(builderConfig).toMatch(/nsis:\s*[\s\S]*?artifactName:\s*"\$\{productName\}-\$\{version\}-\$\{os\}-\$\{arch\}\.\$\{ext\}"/m);
|
expect(builderConfig).toMatch(/nsis:\s*[\s\S]*?artifactName:\s*"\$\{productName\}-\$\{version\}-\$\{os\}-\$\{arch\}\.\$\{ext\}"/m);
|
||||||
expect(builderConfig).toMatch(/nsis:\s*[\s\S]*?oneClick:\s*false/m);
|
expect(builderConfig).toMatch(/nsis:\s*[\s\S]*?oneClick:\s*false/m);
|
||||||
@@ -76,6 +76,18 @@ describe("electron-builder desktop config", () => {
|
|||||||
expect(builderConfig).toMatch(/mac:\s*[\s\S]*?target:\s*[\s\S]*?-\s*target:\s*dmg/m);
|
expect(builderConfig).toMatch(/mac:\s*[\s\S]*?target:\s*[\s\S]*?-\s*target:\s*dmg/m);
|
||||||
expect(builderConfig).toMatch(/mac:\s*[\s\S]*?target:\s*[\s\S]*?-\s*target:\s*zip/m);
|
expect(builderConfig).toMatch(/mac:\s*[\s\S]*?target:\s*[\s\S]*?-\s*target:\s*zip/m);
|
||||||
|
|
||||||
|
const macSection = builderConfig.match(/mac:\s*([\s\S]*?)(?=\nwin:)/m)?.[1] ?? "";
|
||||||
|
const macArchBlocks = Array.from(
|
||||||
|
macSection.matchAll(/-\s*target:\s*(dmg|zip)\s*arch:\s*([\s\S]*?)(?=\n\s*-\s*target:|\n\w|$)/g),
|
||||||
|
);
|
||||||
|
expect(macArchBlocks).toHaveLength(2);
|
||||||
|
for (const [, , archBlock] of macArchBlocks) {
|
||||||
|
expect(Array.from(archBlock.matchAll(/-\s*(x64|arm64)/g), (entry) => entry[1]).sort()).toEqual([
|
||||||
|
"arm64",
|
||||||
|
"x64",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
const linuxSectionMatch = builderConfig.match(/linux:\s*[\s\S]*$/m);
|
const linuxSectionMatch = builderConfig.match(/linux:\s*[\s\S]*$/m);
|
||||||
expect(linuxSectionMatch?.[0]).toBeDefined();
|
expect(linuxSectionMatch?.[0]).toBeDefined();
|
||||||
|
|
||||||
@@ -120,6 +132,14 @@ describe("electron-builder desktop config", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("unpacks native embedded Postgres processes from app.asar", async () => {
|
||||||
|
const builderConfig = await readDesktopFile("electron-builder.yml");
|
||||||
|
|
||||||
|
expect(builderConfig).toMatch(
|
||||||
|
/asarUnpack:\s*[\s\S]*?-\s*"node_modules\/@embedded-postgres\/\*\/native\/\*\*\/\*"/m,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("packages @fusion/core runtime dependencies used during desktop startup", async () => {
|
it("packages @fusion/core runtime dependencies used during desktop startup", async () => {
|
||||||
const builderConfig = await readDesktopFile("electron-builder.yml");
|
const builderConfig = await readDesktopFile("electron-builder.yml");
|
||||||
const requiredRuntimeDependencyGlobs = [
|
const requiredRuntimeDependencyGlobs = [
|
||||||
|
|||||||
@@ -41,6 +41,28 @@ describe("desktop release workflow wiring", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("boots embedded Postgres on every native desktop release host", async () => {
|
||||||
|
const release = await readRepoFile(".github/workflows/release.yml");
|
||||||
|
const testRelease = await readRepoFile(".github/workflows/test-release.yml");
|
||||||
|
const manualWindows = await readRepoFile(".github/workflows/desktop-windows.yml");
|
||||||
|
const advisoryPackaging = await readRepoFile(".github/workflows/desktop-packaging.yml");
|
||||||
|
|
||||||
|
// FNXC:DesktopEmbeddedPostgres 2026-07-14-09:40:
|
||||||
|
// Installer assembly alone cannot prove native Postgres starts. Keep a real
|
||||||
|
// lifecycle smoke on Windows, macOS, and Linux release hosts, plus the
|
||||||
|
// isolated Windows and advisory packaging paths.
|
||||||
|
for (const workflow of [release, testRelease]) {
|
||||||
|
for (const platform of ["Windows", "macOS", "Linux"]) {
|
||||||
|
expect(workflow).toContain(`Smoke embedded Postgres on ${platform}`);
|
||||||
|
}
|
||||||
|
expect(workflow.match(/pnpm --filter @fusion\/core test:embedded-postgres/g)).toHaveLength(3);
|
||||||
|
}
|
||||||
|
expect(manualWindows).toContain("Smoke embedded Postgres on Windows");
|
||||||
|
expect(manualWindows).toContain("pnpm --filter @fusion/core test:embedded-postgres");
|
||||||
|
expect(advisoryPackaging).toContain("Smoke embedded Postgres lifecycle");
|
||||||
|
expect(advisoryPackaging).toContain("pnpm --filter @fusion/core test:embedded-postgres");
|
||||||
|
});
|
||||||
|
|
||||||
it("wires release aggregation to include desktop assets across platforms", async () => {
|
it("wires release aggregation to include desktop assets across platforms", async () => {
|
||||||
const release = await readRepoFile(".github/workflows/release.yml");
|
const release = await readRepoFile(".github/workflows/release.yml");
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,26 @@ ignoredBuiltDependencies:
|
|||||||
- sharp
|
- sharp
|
||||||
- ssh2
|
- ssh2
|
||||||
onlyBuiltDependencies:
|
onlyBuiltDependencies:
|
||||||
|
- '@embedded-postgres/darwin-arm64'
|
||||||
|
- '@embedded-postgres/darwin-x64'
|
||||||
|
- '@embedded-postgres/linux-arm64'
|
||||||
|
- '@embedded-postgres/linux-x64'
|
||||||
|
- '@embedded-postgres/windows-x64'
|
||||||
- '@homebridge/node-pty-prebuilt-multiarch'
|
- '@homebridge/node-pty-prebuilt-multiarch'
|
||||||
- esbuild
|
- esbuild
|
||||||
- koffi
|
- koffi
|
||||||
- protobufjs
|
- protobufjs
|
||||||
|
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:30:
|
||||||
|
# Desktop release jobs cross-build Intel and ARM64 artifacts on one host. Install
|
||||||
|
# optional native payloads for both CPU families on the current OS so each
|
||||||
|
# packaged macOS/Linux app contains the Postgres binaries selected at runtime.
|
||||||
|
# Upstream publishes Windows x64 only, so unsupported Windows ARM64 is not listed.
|
||||||
|
supportedArchitectures:
|
||||||
|
os:
|
||||||
|
- current
|
||||||
|
cpu:
|
||||||
|
- x64
|
||||||
|
- arm64
|
||||||
packages:
|
packages:
|
||||||
- "packages/*"
|
- "packages/*"
|
||||||
- "plugins/examples/*"
|
- "plugins/examples/*"
|
||||||
|
|||||||
Reference in New Issue
Block a user