From c862cd1319c0668080ae9444260d011776dbece1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sat, 23 May 2026 19:12:03 -0400 Subject: [PATCH] Guard desktop release targets --- AgentCompany-New/package.json | 9 +- .../contract/desktop-release-contract.test.ts | 96 +++++++++++++++++++ 2 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 AgentCompany-New/tests/contract/desktop-release-contract.test.ts diff --git a/AgentCompany-New/package.json b/AgentCompany-New/package.json index 2bed2b1c..2de25bf9 100644 --- a/AgentCompany-New/package.json +++ b/AgentCompany-New/package.json @@ -23,10 +23,11 @@ "verify": "pnpm verify:ci", "verify:ci": "pnpm typecheck && pnpm lint && pnpm test:unit && pnpm test:contract && pnpm test:integration && pnpm test:replay-eval && pnpm test:e2e", "package:dir": "pnpm build && electron-builder --dir --publish never", - "package:win": "pnpm build && electron-builder --win nsis --x64 --publish never", - "package:mac:arm": "pnpm build && electron-builder --mac dmg --arm64 --publish never", - "package:mac:x64": "pnpm build && electron-builder --mac dmg --x64 --publish never", - "package:linux": "pnpm build && electron-builder --linux AppImage deb --x64 --publish never" + "package:win": "pnpm build && electron-builder --win --x64 --publish never", + "package:mac:arm": "pnpm build && electron-builder --mac --arm64 --publish never", + "package:mac:x64": "pnpm build && electron-builder --mac --x64 --publish never", + "package:mac": "pnpm build && electron-builder --mac --x64 --arm64 --publish never", + "package:linux": "pnpm build && electron-builder --linux --x64 --publish never" }, "dependencies": { "lucide-react": "0.468.0", diff --git a/AgentCompany-New/tests/contract/desktop-release-contract.test.ts b/AgentCompany-New/tests/contract/desktop-release-contract.test.ts new file mode 100644 index 00000000..9783ec18 --- /dev/null +++ b/AgentCompany-New/tests/contract/desktop-release-contract.test.ts @@ -0,0 +1,96 @@ +import { existsSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, test } from "vitest"; + +interface PackageManifest { + main: string; + scripts: Record; +} + +const projectRoot = join(dirname(fileURLToPath(import.meta.url)), "..", ".."); + +function readPackageManifest(): PackageManifest { + return JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf8")) as PackageManifest; +} + +function readBuilderConfig(): string { + return readFileSync(join(projectRoot, "electron-builder.yml"), "utf8"); +} + +function topLevelValue(yaml: string, key: string): string { + const match = new RegExp(`^${key}:\\s*(.+)$`, "m").exec(yaml); + if (!match) throw new Error(`Builder config key is missing: ${key}`); + return match[1].trim(); +} + +function topLevelSection(yaml: string, key: string): string { + const lines = yaml.split(/\r?\n/); + const start = lines.findIndex((line) => line === `${key}:`); + if (start < 0) throw new Error(`Builder config section is missing: ${key}`); + const section: string[] = []; + for (const line of lines.slice(start + 1)) { + if (/^\S/.test(line)) break; + section.push(line); + } + return `${section.join("\n")}\n`; +} + +function topLevelList(yaml: string, key: string): string[] { + return topLevelSection(yaml, key) + .split(/\r?\n/) + .filter((line) => line.trim().startsWith("- ")) + .map((line) => line.replace(/^\s*-\s+/, "").trim()); +} + +function targetMatrix(section: string): Record { + const targets: Record = {}; + const targetPattern = /^\s{4}- target:\s+(.+)\r?\n\s{6}arch:\r?\n((?:\s{8}-\s+.+\r?\n)+)/gm; + for (const match of section.matchAll(targetPattern)) { + targets[match[1].trim()] = match[2].trim().split(/\r?\n/).map((line) => line.replace(/^\s*-\s+/, "").trim()); + } + return targets; +} + +describe("desktop release contract", () => { + test("declares production Electron entrypoints and packaged resources", () => { + const manifest = readPackageManifest(); + const config = readBuilderConfig(); + const directories = topLevelSection(config, "directories"); + + expect(manifest.main).toBe("out/main/index.js"); + expect(topLevelValue(config, "appId")).toBe("ai.agentcompany.desktop"); + expect(topLevelValue(config, "productName")).toBe("AgentCompany"); + expect(topLevelValue(config, "asar")).toBe("true"); + expect(directories).toContain("output: release"); + expect(directories).toContain("buildResources: resources"); + expect(topLevelList(config, "files")).toEqual(["out/**/*", "package.json"]); + expect(existsSync(join(projectRoot, "resources", "icon.png"))).toBe(true); + expect(existsSync(join(projectRoot, "resources", "entitlements.mac.plist"))).toBe(true); + }); + + test("locks the desktop target matrix required by the PRD", () => { + const config = readBuilderConfig(); + + expect(targetMatrix(topLevelSection(config, "win"))).toEqual({ nsis: ["x64"] }); + expect(targetMatrix(topLevelSection(config, "mac"))).toEqual({ + dmg: ["x64", "arm64"], + zip: ["x64", "arm64"], + }); + expect(targetMatrix(topLevelSection(config, "linux"))).toEqual({ + AppImage: ["x64"], + deb: ["x64"], + }); + }); + + test("keeps package scripts aligned with configured desktop targets", () => { + const { scripts } = readPackageManifest(); + + expect(scripts["package:dir"]).toBe("pnpm build && electron-builder --dir --publish never"); + expect(scripts["package:win"]).toBe("pnpm build && electron-builder --win --x64 --publish never"); + expect(scripts["package:mac:arm"]).toBe("pnpm build && electron-builder --mac --arm64 --publish never"); + expect(scripts["package:mac:x64"]).toBe("pnpm build && electron-builder --mac --x64 --publish never"); + expect(scripts["package:mac"]).toBe("pnpm build && electron-builder --mac --x64 --arm64 --publish never"); + expect(scripts["package:linux"]).toBe("pnpm build && electron-builder --linux --x64 --publish never"); + }); +});