mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
1ff25e5b2b
* fix(daemon): keep packaged MCP discovery out of package managers * fix(packaged): preserve MCP payload identity and require sidecar supervisor * fix(pack): finalize runtime manifests and report entry recovery * ci: allow isolated full Windows smoke on dispatched revisions * test(packaged): derive recovery versions from the release channel * test(win): read portable installer logs and retain rollback diagnostics * test(win): distinguish portable startup reconciliation from installer sync
546 lines
21 KiB
TypeScript
546 lines
21 KiB
TypeScript
import { access, mkdtemp, mkdir, readFile, readdir, rm, symlink, writeFile } from "node:fs/promises";
|
|
import os, { tmpdir } from "node:os";
|
|
import { dirname, join, resolve } from "node:path";
|
|
import process from "node:process";
|
|
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
|
|
import type { ToolPackConfig } from "@/config/index.js";
|
|
import {
|
|
copyMacPrebundleRuntimeDependencies,
|
|
copyResourceTree,
|
|
createMacElectronRebuildOptions,
|
|
renderMacPackagedConfig,
|
|
toRelativeImportSpecifier,
|
|
validateMacNativeRebuildOutput,
|
|
} from "@/mac/app.js";
|
|
import macBuilderSource from "@/mac/builder.ts?raw";
|
|
import { runElectronBuilder } from "@/mac/builder.js";
|
|
import { resolveSeededAppConfigPaths, seedPackagedAppConfig, writeLaunchPackagedConfig } from "@/mac/index.js";
|
|
import { resolveMacPaths } from "@/mac/paths.js";
|
|
|
|
async function pathExists(path: string): Promise<boolean> {
|
|
try {
|
|
await access(path);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function makeConfig(root: string, overrides: Partial<ToolPackConfig> = {}): ToolPackConfig {
|
|
return {
|
|
containerized: false,
|
|
electronBuilderCliPath: "/x/electron-builder/cli.js",
|
|
electronDistPath: "/x/electron/dist",
|
|
electronVersion: "41.3.0",
|
|
macCompression: "normal",
|
|
namespace: "local-test",
|
|
platform: "mac",
|
|
portable: false,
|
|
removeData: false,
|
|
removeLogs: false,
|
|
removeProductUserData: false,
|
|
removeSidecars: false,
|
|
requireVelaCli: false,
|
|
roots: {
|
|
output: {
|
|
appBuilderRoot: join(root, ".tmp", "tools-pack", "out", "mac", "namespaces", "local-test", "builder"),
|
|
namespaceRoot: join(root, ".tmp", "tools-pack", "out", "mac", "namespaces", "local-test"),
|
|
platformRoot: join(root, ".tmp", "tools-pack", "out", "mac"),
|
|
root: join(root, ".tmp", "tools-pack", "out"),
|
|
},
|
|
runtime: {
|
|
namespaceBaseRoot: join(root, ".tmp", "tools-pack", "runtime", "mac", "namespaces"),
|
|
namespaceRoot: join(root, ".tmp", "tools-pack", "runtime", "mac", "namespaces", "local-test"),
|
|
},
|
|
cacheRoot: join(root, ".tmp", "tools-pack", "cache"),
|
|
toolPackRoot: join(root, ".tmp", "tools-pack"),
|
|
},
|
|
silent: true,
|
|
signed: false,
|
|
to: "app",
|
|
webOutputMode: "standalone",
|
|
workspaceRoot: root,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
const envState = { odDataDir: process.env.OD_DATA_DIR };
|
|
|
|
afterEach(() => {
|
|
if (envState.odDataDir == null) {
|
|
delete process.env.OD_DATA_DIR;
|
|
} else {
|
|
process.env.OD_DATA_DIR = envState.odDataDir;
|
|
}
|
|
});
|
|
|
|
describe("mac prebundle entrypoints", () => {
|
|
it("canonicalizes symlinked roots before rendering relative imports", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "od-mac-prebundle-path-"));
|
|
const physicalRoot = join(root, "physical");
|
|
const linkedRoot = join(root, "linked");
|
|
const fromDirectory = join(physicalRoot, "entrypoints");
|
|
const targetPath = join(physicalRoot, "dist", "entry.js");
|
|
await mkdir(fromDirectory, { recursive: true });
|
|
await mkdir(dirname(targetPath), { recursive: true });
|
|
await writeFile(targetPath, "export {};\n", "utf8");
|
|
await symlink(physicalRoot, linkedRoot, "dir");
|
|
try {
|
|
await expect(toRelativeImportSpecifier(join(linkedRoot, "entrypoints"), targetPath))
|
|
.resolves.toBe("../dist/entry.js");
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("resolveSeededAppConfigPaths", () => {
|
|
it("declares the Workspace invite URL scheme in the packaged app metadata", () => {
|
|
expect(macBuilderSource).toContain("protocols: [");
|
|
expect(macBuilderSource).toContain('schemes: ["opendesign"]');
|
|
});
|
|
|
|
it("uses workspace .od by default", () => {
|
|
const config = makeConfig("/work");
|
|
expect(resolveSeededAppConfigPaths(config)).toEqual({
|
|
sourcePath: join("/work", ".od", "app-config.json"),
|
|
targetPath: join("/work", ".tmp", "tools-pack", "runtime", "mac", "namespaces", "local-test", "data", "app-config.json"),
|
|
});
|
|
});
|
|
|
|
it("prefers OD_DATA_DIR when provided", () => {
|
|
process.env.OD_DATA_DIR = "/custom/data";
|
|
const config = makeConfig("/work");
|
|
expect(resolveSeededAppConfigPaths(config)).toEqual({
|
|
sourcePath: join("/custom/data", "app-config.json"),
|
|
targetPath: join("/work", ".tmp", "tools-pack", "runtime", "mac", "namespaces", "local-test", "data", "app-config.json"),
|
|
});
|
|
});
|
|
|
|
it("resolves relative OD_DATA_DIR against the workspace root", () => {
|
|
process.env.OD_DATA_DIR = "e2e/ui/.od-data";
|
|
const config = makeConfig("/work");
|
|
expect(resolveSeededAppConfigPaths(config)).toEqual({
|
|
sourcePath: resolve("/work", "e2e", "ui", ".od-data", "app-config.json"),
|
|
targetPath: join("/work", ".tmp", "tools-pack", "runtime", "mac", "namespaces", "local-test", "data", "app-config.json"),
|
|
});
|
|
});
|
|
|
|
it("expands $HOME-style OD_DATA_DIR values", () => {
|
|
process.env.OD_DATA_DIR = "$HOME/.open-design";
|
|
const config = makeConfig("/work");
|
|
expect(resolveSeededAppConfigPaths(config)).toEqual({
|
|
sourcePath: join(os.homedir(), ".open-design", "app-config.json"),
|
|
targetPath: join("/work", ".tmp", "tools-pack", "runtime", "mac", "namespaces", "local-test", "data", "app-config.json"),
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("seedPackagedAppConfig", () => {
|
|
it("copies the current app-config into the packaged runtime namespace", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const config = makeConfig(root);
|
|
const sourceDir = join(root, ".od");
|
|
await mkdir(sourceDir, { recursive: true });
|
|
await writeFile(
|
|
join(sourceDir, "app-config.json"),
|
|
`${JSON.stringify({ onboardingCompleted: true, agentId: "codex", agentCliEnv: { codex: { CODEX_BIN: "/Applications/Codex.app/Contents/Resources/codex" } } }, null, 2)}\n`,
|
|
"utf8",
|
|
);
|
|
|
|
await seedPackagedAppConfig(config);
|
|
|
|
await expect(
|
|
readFile(join(config.roots.runtime.namespaceRoot, "data", "app-config.json"), "utf8"),
|
|
).resolves.toContain('"agentId": "codex"');
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("skips seeding for portable builds", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const config = makeConfig(root, { portable: true });
|
|
const sourceDir = join(root, ".od");
|
|
await mkdir(sourceDir, { recursive: true });
|
|
await writeFile(join(sourceDir, "app-config.json"), "{\n \"agentId\": \"codex\"\n}\n", "utf8");
|
|
|
|
await seedPackagedAppConfig(config);
|
|
|
|
await expect(
|
|
readFile(join(config.roots.runtime.namespaceRoot, "data", "app-config.json"), "utf8"),
|
|
).rejects.toMatchObject({ code: "ENOENT" });
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("copyResourceTree", () => {
|
|
it("does not embed the build machine Node launcher into mac resources", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const config = makeConfig(root);
|
|
const paths = resolveMacPaths(config);
|
|
const resourceNames = [
|
|
"skills",
|
|
"design-templates",
|
|
"design-systems",
|
|
"craft",
|
|
"plugins/_official",
|
|
"plugins/registry",
|
|
"assets/frames",
|
|
"assets/community-pets",
|
|
"prompt-templates",
|
|
"data/plugin-previews",
|
|
];
|
|
|
|
for (const name of resourceNames) {
|
|
await mkdir(join(root, name), { recursive: true });
|
|
}
|
|
const dshRuntimeRoot = join(root, "packages", "dsh-runtime");
|
|
await mkdir(join(dshRuntimeRoot, "dist", "types"), { recursive: true });
|
|
await writeFile(
|
|
join(dshRuntimeRoot, "package.json"),
|
|
`${JSON.stringify({
|
|
name: "@open-design/dsh-runtime",
|
|
version: "0.1.0",
|
|
files: ["dist"],
|
|
}, null, 2)}\n`,
|
|
"utf8",
|
|
);
|
|
await writeFile(join(dshRuntimeRoot, "dist", "index.js"), "export {};\n", "utf8");
|
|
await writeFile(join(dshRuntimeRoot, "dist", "types", "index.d.ts"), "export {};\n", "utf8");
|
|
|
|
await copyResourceTree(config, paths);
|
|
|
|
expect(await pathExists(join(paths.resourceRoot, "bin", "node"))).toBe(false);
|
|
const dshRuntimeResourceRoot = join(paths.resourceRoot, "agent-runtimes", "deepseek-harness");
|
|
await expect(readFile(join(dshRuntimeResourceRoot, "manifest.json"), "utf8")).resolves.toContain(
|
|
'"packageName": "@open-design/dsh-runtime"',
|
|
);
|
|
expect((await readdir(dshRuntimeResourceRoot)).filter((entry) => entry.endsWith(".tgz"))).toHaveLength(1);
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("copyMacPrebundleRuntimeDependencies", () => {
|
|
it("copies the pinned prebuilt fsevents binding into the assembled app", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const config = makeConfig(root);
|
|
const chokidarRoot = join(root, "apps", "daemon", "node_modules", "chokidar");
|
|
const sourceRoot = join(chokidarRoot, "node_modules", "fsevents");
|
|
const appRoot = join(root, "assembled", "app");
|
|
await mkdir(sourceRoot, { recursive: true });
|
|
await writeFile(join(chokidarRoot, "package.json"), '{"name":"chokidar","version":"3.6.0"}\n', "utf8");
|
|
await writeFile(join(sourceRoot, "package.json"), '{"name":"fsevents","version":"2.3.3"}\n', "utf8");
|
|
await writeFile(join(sourceRoot, "fsevents.js"), "module.exports = {};\n", "utf8");
|
|
await writeFile(join(sourceRoot, "fsevents.node"), "prebuilt-native-binding", "utf8");
|
|
|
|
await copyMacPrebundleRuntimeDependencies(config, appRoot);
|
|
|
|
await expect(readFile(join(appRoot, "node_modules", "fsevents", "fsevents.node"), "utf8")).resolves.toBe(
|
|
"prebuilt-native-binding",
|
|
);
|
|
await expect(readFile(join(appRoot, "node_modules", "fsevents", "fsevents.js"), "utf8")).resolves.toBe(
|
|
"module.exports = {};\n",
|
|
);
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("rejects a workspace fsevents version that drifted from the assembly contract", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const config = makeConfig(root);
|
|
const chokidarRoot = join(root, "apps", "daemon", "node_modules", "chokidar");
|
|
const sourceRoot = join(chokidarRoot, "node_modules", "fsevents");
|
|
await mkdir(sourceRoot, { recursive: true });
|
|
await writeFile(join(chokidarRoot, "package.json"), '{"name":"chokidar","version":"3.6.0"}\n', "utf8");
|
|
await writeFile(join(sourceRoot, "package.json"), '{"name":"fsevents","version":"2.3.2"}\n', "utf8");
|
|
|
|
await expect(copyMacPrebundleRuntimeDependencies(config, join(root, "assembled", "app"))).rejects.toThrow(
|
|
/fsevents expected 2\.3\.3, found 2\.3\.2/,
|
|
);
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("renderMacPackagedConfig", () => {
|
|
it("omits nodeCommandRelative so packaged mac sidecars use Electron as Node", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const config = makeConfig(root);
|
|
|
|
const packagedConfig = JSON.parse(
|
|
renderMacPackagedConfig({
|
|
appVersion: "1.2.3",
|
|
config,
|
|
usePrebundledStandaloneWeb: true,
|
|
}),
|
|
) as Record<string, unknown>;
|
|
expect(packagedConfig).not.toHaveProperty("nodeCommandRelative");
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("bakes the configured updater metadata URL for mac beta validation", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const config = makeConfig(root, {
|
|
updateMetadataUrl: "http://127.0.0.1:4567/beta/latest/metadata.json",
|
|
});
|
|
|
|
const packagedConfig = JSON.parse(
|
|
renderMacPackagedConfig({
|
|
appVersion: "1.2.3-beta.0",
|
|
config,
|
|
usePrebundledStandaloneWeb: true,
|
|
}),
|
|
) as Record<string, unknown>;
|
|
|
|
expect(packagedConfig.updateMetadataUrl).toBe("http://127.0.0.1:4567/beta/latest/metadata.json");
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
// The vela web origin is the workspace-team console link the daemon derives
|
|
// its settings / members / dashboard URLs from. It arrives from a CI secret
|
|
// rather than the source tree, so packaging has to carry it into the bundle
|
|
// (same chain as posthogKey) or the feature stays dark in the packaged app.
|
|
it("bakes the injected vela web origin for a workspace-team build", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const config = makeConfig(root, {
|
|
amrProfile: "feature-test",
|
|
velaWebUrl: "https://vela.example.invalid",
|
|
});
|
|
|
|
const packagedConfig = JSON.parse(
|
|
renderMacPackagedConfig({
|
|
appVersion: "1.2.3-beta.0",
|
|
config,
|
|
usePrebundledStandaloneWeb: true,
|
|
}),
|
|
) as Record<string, unknown>;
|
|
|
|
expect(packagedConfig.velaWebUrl).toBe("https://vela.example.invalid");
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("omits the vela web origin when the build was given none", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const packagedConfig = JSON.parse(
|
|
renderMacPackagedConfig({
|
|
appVersion: "1.2.3",
|
|
config: makeConfig(root),
|
|
usePrebundledStandaloneWeb: true,
|
|
}),
|
|
) as Record<string, unknown>;
|
|
|
|
expect(packagedConfig).not.toHaveProperty("velaWebUrl");
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("runElectronBuilder", () => {
|
|
async function prepareElectronBuilderConfig(root: string, overrides: Partial<ToolPackConfig>) {
|
|
const cliPath = join(root, "fake-electron-builder.mjs");
|
|
|
|
const config = makeConfig(root, {
|
|
appVersion: "1.2.3-prerelease.4",
|
|
electronBuilderCliPath: cliPath,
|
|
signed: true,
|
|
webOutputMode: "server",
|
|
...overrides,
|
|
});
|
|
const paths = resolveMacPaths(config);
|
|
const nodePtyPrebuildRoot = join(
|
|
paths.appPath,
|
|
"Contents",
|
|
"Resources",
|
|
"app",
|
|
"node_modules",
|
|
"node-pty",
|
|
"prebuilds",
|
|
`darwin-${process.arch}`,
|
|
);
|
|
await writeFile(
|
|
cliPath,
|
|
[
|
|
'import { chmod, mkdir, writeFile } from "node:fs/promises";',
|
|
`const prebuildRoot = ${JSON.stringify(nodePtyPrebuildRoot)};`,
|
|
`const appRoot = ${JSON.stringify(join(paths.appPath, "Contents", "Resources", "app"))};`,
|
|
'for (const name of ["sidecar", "platform"]) {',
|
|
' const root = appRoot + "/node_modules/@open-design/" + name;',
|
|
' await mkdir(root + "/dist", { recursive:true });',
|
|
' await writeFile(root + "/package.json", JSON.stringify({name:"@open-design/"+name, main:"dist/index.mjs"}));',
|
|
' await writeFile(root + "/dist/index.mjs", "export {};");',
|
|
' if (name === "sidecar") await writeFile(root + "/dist/supervisor.mjs", "export {};");',
|
|
'}',
|
|
'await writeFile(appRoot + "/main.cjs", "");',
|
|
'await writeFile(appRoot + "/package.json", "{}");',
|
|
"await mkdir(prebuildRoot, { recursive: true });",
|
|
'await writeFile(new URL("pty.node", `file://${prebuildRoot}/`), Buffer.alloc(32 * 1024, 1));',
|
|
'await writeFile(new URL("spawn-helper", `file://${prebuildRoot}/`), "#!/bin/sh\\nexit 0\\n", "utf8");',
|
|
'await chmod(new URL("spawn-helper", `file://${prebuildRoot}/`), 0o755);',
|
|
"",
|
|
].join("\n"),
|
|
"utf8",
|
|
);
|
|
|
|
await runElectronBuilder(config, paths, ["dir"]);
|
|
|
|
return JSON.parse(await readFile(paths.appBuilderConfigPath, "utf8")) as {
|
|
afterSign?: string;
|
|
mac?: {
|
|
notarize?: boolean;
|
|
};
|
|
};
|
|
}
|
|
|
|
it("does not explicitly disable electron-builder notarization for notarized mac builds", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const builderConfig = await prepareElectronBuilderConfig(root, { macNotarize: true });
|
|
|
|
expect(builderConfig.afterSign).toContain("notarize.cjs");
|
|
expect(builderConfig.mac).not.toHaveProperty("notarize");
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("keeps signed-only mac builds from invoking electron-builder notarization", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const builderConfig = await prepareElectronBuilderConfig(root, { macNotarize: false });
|
|
|
|
expect(builderConfig.afterSign).toBeUndefined();
|
|
expect(builderConfig.mac?.notarize).toBe(false);
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("createMacElectronRebuildOptions", () => {
|
|
it("targets the packaged Electron ABI for required native modules", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const config = makeConfig(root, { electronVersion: "41.3.0" });
|
|
const appRoot = join(root, "assembled", "app");
|
|
|
|
expect(createMacElectronRebuildOptions(config, appRoot)).toMatchObject({
|
|
arch: process.arch,
|
|
buildFromSource: false,
|
|
buildPath: appRoot,
|
|
electronVersion: "41.3.0",
|
|
force: true,
|
|
mode: "sequential",
|
|
onlyModules: ["better-sqlite3"],
|
|
platform: "darwin",
|
|
projectRootPath: appRoot,
|
|
});
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("validateMacNativeRebuildOutput", () => {
|
|
it("reports a missing rebuilt native module as missing output", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
await expect(validateMacNativeRebuildOutput(root)).resolves.toBe(
|
|
`native module output is missing: ${join(root, "node_modules", "better-sqlite3", "build", "Release", "better_sqlite3.node")}`,
|
|
);
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("preserves non-ENOENT filesystem diagnostics from stat failures", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const buildPath = join(root, "node_modules", "better-sqlite3", "build");
|
|
const nativePath = join(buildPath, "Release", "better_sqlite3.node");
|
|
await mkdir(dirname(buildPath), { recursive: true });
|
|
await writeFile(buildPath, "not a directory", "utf8");
|
|
|
|
const result = await validateMacNativeRebuildOutput(root);
|
|
if (process.platform === "win32") {
|
|
await expect(Promise.resolve(result)).resolves.toBe(
|
|
`native module output is missing: ${nativePath}`,
|
|
);
|
|
} else {
|
|
await expect(Promise.resolve(result)).resolves.toContain(
|
|
`native module output could not be inspected: ${nativePath}: ENOTDIR: not a directory, stat '${nativePath}'`,
|
|
);
|
|
}
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("writeLaunchPackagedConfig", () => {
|
|
it("injects the tools-pack runtime namespace root without mutating the packaged app config", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-mac-"));
|
|
try {
|
|
const config = makeConfig(root, { namespace: "release-beta", portable: true });
|
|
const appPath = join(root, "Open Design.app");
|
|
const embeddedConfigPath = join(appPath, "Contents", "Resources", "open-design-config.json");
|
|
await mkdir(dirname(embeddedConfigPath), { recursive: true });
|
|
await writeFile(
|
|
embeddedConfigPath,
|
|
`${JSON.stringify(
|
|
{
|
|
appVersion: "0.5.1-beta.2",
|
|
namespace: "packaged-default",
|
|
nodeCommandRelative: "open-design/bin/node",
|
|
webOutputMode: "standalone",
|
|
},
|
|
null,
|
|
2,
|
|
)}\n`,
|
|
"utf8",
|
|
);
|
|
|
|
const launchConfigPath = await writeLaunchPackagedConfig(config, appPath);
|
|
const launchConfig = JSON.parse(await readFile(launchConfigPath, "utf8")) as Record<string, unknown>;
|
|
const embeddedConfig = JSON.parse(await readFile(embeddedConfigPath, "utf8")) as Record<string, unknown>;
|
|
|
|
expect(launchConfigPath).toBe(join(config.roots.runtime.namespaceRoot, "runtime", "open-design-config.json"));
|
|
expect(launchConfig).toMatchObject({
|
|
appVersion: "0.5.1-beta.2",
|
|
namespace: "release-beta",
|
|
namespaceBaseRoot: config.roots.runtime.namespaceBaseRoot,
|
|
nodeCommandRelative: "open-design/bin/node",
|
|
webOutputMode: "standalone",
|
|
});
|
|
expect(embeddedConfig).not.toHaveProperty("namespaceBaseRoot");
|
|
expect(embeddedConfig.namespace).toBe("packaged-default");
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|