diff --git a/apps/desktop/src/check-packaging-contract.ts b/apps/desktop/src/check-packaging-contract.ts index 2f6dc61a..e7a4930d 100644 --- a/apps/desktop/src/check-packaging-contract.ts +++ b/apps/desktop/src/check-packaging-contract.ts @@ -12,6 +12,7 @@ const appDir = dirname(distDir); const repoRoot = resolve(appDir, "../.."); const packageJson = JSON.parse(readFileSync(join(appDir, "package.json"), "utf8")) as { scripts?: Record; dependencies?: Record; devDependencies?: Record; description?: string; author?: string }; const rootPackageJson = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8")) as { scripts?: Record }; +const workspaceConfig = readFileSync(join(repoRoot, "pnpm-workspace.yaml"), "utf8"); const builderConfigPath = join(appDir, "electron-builder.yml"); const builderConfig = readFileSync(builderConfigPath, "utf8"); @@ -27,6 +28,10 @@ assert.equal(packageJson.dependencies?.["@open-pets/cursor"], "workspace:*"); assert.equal(packageJson.dependencies?.["@open-pets/mcp"], "workspace:*"); assert.equal(packageJson.dependencies?.["@open-pets/opencode"], "workspace:*"); assert.equal(packageJson.dependencies?.["@open-pets/agent-events"], "workspace:*"); +assert.equal(packageJson.dependencies?.["@img/sharp-win32-x64"], undefined, "sharp platform binaries must stay optional transitive deps, not direct host-breaking dependencies."); +assert.match(workspaceConfig, /supportedArchitectures:[\s\S]*?os:[\s\S]*?- darwin[\s\S]*?- win32[\s\S]*?- linux/, "pnpm must install optional sharp binaries for desktop release OS targets."); +assert.match(workspaceConfig, /supportedArchitectures:[\s\S]*?cpu:[\s\S]*?- x64[\s\S]*?- arm64/, "pnpm must install optional sharp binaries for desktop release CPU targets."); +assert.match(workspaceConfig, /supportedArchitectures:[\s\S]*?libc:[\s\S]*?- glibc/, "pnpm must install optional sharp binaries for Linux glibc release targets."); assert.match(packageJson.devDependencies?.["electron-builder"] ?? "", /^\^26\.(?:9|[1-9]\d)\./, "desktop AppImage packaging must use electron-builder 26.9+ for conditional Linux sandbox handling."); assert.match(builderConfig, /appId:\s*dev\.openpets\.app/); assert.match(builderConfig, /productName:\s*OpenPets/); @@ -277,6 +282,10 @@ function checkPackageOutput(): void { assert.ok(existsSync(join(appContents, "node_modules", "yauzl", "fd-slicer.js")), "packaged yauzl fd-slicer helper is missing."); assert.ok(existsSync(join(appContents, "node_modules", "buffer-crc32", "index.js")), "packaged yauzl transitive dependency buffer-crc32 is missing."); assert.ok(existsSync(join(appContents, "node_modules", "pend", "index.js")), "packaged yauzl transitive dependency pend is missing."); + assert.ok(existsSync(join(appContents, "node_modules", "sharp", "lib", "index.js")), "packaged sharp runtime is missing."); + assert.ok(existsSync(join(appContents, "node_modules", "@img", "sharp-win32-x64", "lib", "sharp-win32-x64.node")), "packaged Windows x64 sharp native binary is missing."); + assert.ok(existsSync(join(appContents, "node_modules", "@img", "sharp-linux-x64", "lib", "sharp-linux-x64.node")), "packaged Linux x64 sharp native binary is missing."); + assert.ok(existsSync(join(appContents, "node_modules", "@img", "sharp-darwin-x64", "lib", "sharp-darwin-x64.node")), "packaged macOS x64 sharp native binary is missing."); assertRegularNonSymlink(join(appContents, "node_modules", "@open-pets", "mcp", "dist", "index.js")); assertRegularNonSymlink(join(appContents, "node_modules", "@open-pets", "cli", "dist", "index.js")); assertRegularNonSymlink(join(appContents, "node_modules", "@open-pets", "opencode", "dist", "plugin.js")); diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d190a089..6cc7c76a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,16 @@ packages: - "apps/*" - "packages/*" +supportedArchitectures: + os: + - darwin + - win32 + - linux + cpu: + - x64 + - arm64 + libc: + - glibc allowBuilds: electron-winstaller: false sharp: true