From 0a6660b4470a0077189763806d7ac28252b34801 Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 26 Sep 2026 18:52:04 -0700 Subject: [PATCH] build(orcad): prepare desktop deployment template for convergence --- config/electron-builder.config.cjs | 3 ++ ...lectron-builder-runtime-resources.test.mjs | 3 ++ .../scripts/orcad-template-packaging.test.mjs | 54 +++++++++++++++++++ .../verify-packaged-orcad-template.test.mjs | 6 +-- package.json | 6 +-- 5 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 config/scripts/orcad-template-packaging.test.mjs diff --git a/config/electron-builder.config.cjs b/config/electron-builder.config.cjs index c168a479844a..3186ff78085b 100644 --- a/config/electron-builder.config.cjs +++ b/config/electron-builder.config.cjs @@ -19,6 +19,7 @@ const { assertMobileWebBundleBuilt } = require('./scripts/verify-packaged-mobile-web-bundle.cjs') const { verifyPackagedPluginResources } = require('./scripts/verify-packaged-plugin-resources.cjs') +const { verifyPackagedOrcadTemplate } = require('./scripts/verify-packaged-orcad-template.cjs') const { assertBundledRipgrepInstalled, bundledRipgrepExtraResources, @@ -111,6 +112,7 @@ const emojiShortcodeDatasetResource = { } const commonExtraResources = [ relayExtraResource, + { from: 'out/orcad-template', to: 'orcad-template' }, ...bundledRipgrepExtraResources, bundledPluginResources, skillFreshnessResources, @@ -326,6 +328,7 @@ module.exports = { if (!existsSync(resourcesDir)) { throw new Error(`Missing packaged resources directory: ${resourcesDir}`) } + verifyPackagedOrcadTemplate(resourcesDir) // FpmTarget replaces this with deb/rpm while building those artifacts from the shared app tree. if (context.electronPlatformName === 'linux') { writeFileSync(join(resourcesDir, 'package-type'), 'AppImage') diff --git a/config/scripts/electron-builder-runtime-resources.test.mjs b/config/scripts/electron-builder-runtime-resources.test.mjs index f74c1c09e010..940be5fe79af 100644 --- a/config/scripts/electron-builder-runtime-resources.test.mjs +++ b/config/scripts/electron-builder-runtime-resources.test.mjs @@ -5,6 +5,7 @@ import { tmpdir } from 'node:os' import { delimiter, dirname, join, relative, resolve } from 'node:path' import { describe, expect, it } from 'vitest' import { removeTree } from '../../src/shared/windows-transient-lock-removal.ts' +import { writeOrcadTemplateTestFixture } from './orcad-template-test-fixture.mjs' const require = createRequire(import.meta.url) const projectRoot = resolve(import.meta.dirname, '..', '..') @@ -411,6 +412,7 @@ describe('packaged runtime resources', () => { { recursive: true } ) await seedBundledRipgrep(resourcesDir) + await writeOrcadTemplateTestFixture(resourcesDir) const unpackedMainDir = join(resourcesDir, 'app.asar.unpacked', 'out', 'main') await mkdir(unpackedMainDir, { recursive: true }) @@ -478,6 +480,7 @@ describe('packaged runtime resources', () => { { recursive: true } ) await seedBundledRipgrep(resourcesDir) + await writeOrcadTemplateTestFixture(resourcesDir) await mkdir(join(resourcesDir, 'node_modules', 'zod', 'src'), { recursive: true }) // Why: afterPack now fails hard when the unpacked daemon entry is // missing, so the fixture must carry one like a real package layout. diff --git a/config/scripts/orcad-template-packaging.test.mjs b/config/scripts/orcad-template-packaging.test.mjs new file mode 100644 index 000000000000..e8a27dfbd1d5 --- /dev/null +++ b/config/scripts/orcad-template-packaging.test.mjs @@ -0,0 +1,54 @@ +import { mkdtemp, mkdir, writeFile } from 'node:fs/promises' +import { readFileSync } from 'node:fs' +import { createRequire } from 'node:module' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { removeTree } from '../../src/shared/windows-transient-lock-removal.ts' +import { writeOrcadTemplateTestFixture } from './orcad-template-test-fixture.mjs' + +const require = createRequire(import.meta.url) +const config = require('../electron-builder.config.cjs') +const { scripts } = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')) + +describe('desktop managed server template', () => { + it.each(['mac', 'linux', 'win'])('ships the template outside the %s app archive', (platform) => { + expect(config[platform].extraResources).toContainEqual({ + from: 'out/orcad-template', + to: 'orcad-template' + }) + expect(config.files).toContain('!out/orcad-template{,/**/*}') + }) + + it.each(['build:desktop', 'build:release', 'build:release:parallel'])( + '%s builds the template after the app outputs', + (script) => { + const steps = scripts[script].split(' && ') + expect(steps.filter((step) => step === 'pnpm run build:orcad-template')).toHaveLength(1) + expect(steps.indexOf('pnpm run build:orcad-template')).toBeGreaterThan( + steps.indexOf('pnpm run build:mobile-web') + ) + } + ) + + it.each(['missing', 'corrupt'])('fails packaging with a %s template', async (state) => { + const root = await mkdtemp(join(tmpdir(), 'orca-template-packaging-')) + try { + const resourcesDir = join(root, 'resources') + await mkdir(resourcesDir) + if (state === 'corrupt') { + const templateDir = await writeOrcadTemplateTestFixture(resourcesDir) + await writeFile(join(templateDir, 'orcad.js'), 'corrupt') + } + await expect( + config.afterPack({ + appOutDir: root, + electronPlatformName: 'win32', + packager: { appInfo: { version: '9.9.9' } } + }) + ).rejects.toThrow(state === 'missing' ? /orcad-template/ : /orcad.js checksum mismatch/) + } finally { + await removeTree(root) + } + }) +}) diff --git a/config/scripts/verify-packaged-orcad-template.test.mjs b/config/scripts/verify-packaged-orcad-template.test.mjs index 9be9b4564396..4c86c2216da1 100644 --- a/config/scripts/verify-packaged-orcad-template.test.mjs +++ b/config/scripts/verify-packaged-orcad-template.test.mjs @@ -77,17 +77,17 @@ describe('verifyPackagedOrcadTemplate', () => { ) }) - it('does not ship the unused deployment template in desktop packages', async () => { + it('ships the managed deployment template in desktop packages', async () => { for (const platform of ['win', 'mac', 'linux']) { expect( builderConfig[platform].extraResources.some( (resource) => typeof resource === 'object' && resource.to.startsWith('orcad-template') ) - ).toBe(false) + ).toBe(true) } const { scripts } = JSON.parse(await readFile(join(process.cwd(), 'package.json'), 'utf8')) for (const name of ['build:desktop', 'build:release', 'build:release:parallel']) { - expect(scripts[name]).not.toContain('build:orcad-template') + expect(scripts[name]).toContain('build:orcad-template') } }) }) diff --git a/package.json b/package.json index 9ef609ece1ff..6268f422b595 100644 --- a/package.json +++ b/package.json @@ -96,10 +96,10 @@ "build:web": "node config/scripts/run-vite-web-build.mjs && node config/scripts/verify-web-build.mjs", "build:web-from-renderer": "node config/scripts/project-renderer-web-client.mjs && node config/scripts/verify-web-build.mjs", "build:mobile-web": "node config/scripts/build-mobile-web-app-bundle.mjs && node config/scripts/verify-mobile-web-app-bundle.mjs", - "build:desktop": "pnpm run typecheck && pnpm run build:relay && pnpm run build:cli && pnpm run build:electron-vite && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer && pnpm run build:mobile-web", + "build:desktop": "pnpm run typecheck && pnpm run build:relay && pnpm run build:cli && pnpm run build:electron-vite && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer && pnpm run build:mobile-web && pnpm run build:orcad-template", "build": "pnpm run build:desktop && pnpm run build:native", - "build:release": "pnpm run build:relay && pnpm run build:native && pnpm run verify:computer-native && pnpm run build:cli && pnpm run build:electron-vite && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer && pnpm run build:mobile-web", - "build:release:parallel": "pnpm run build:relay && pnpm run build:native && pnpm run verify:computer-native && pnpm run build:cli && pnpm run build:electron-vite:parallel && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer && pnpm run build:mobile-web", + "build:release": "pnpm run build:relay && pnpm run build:native && pnpm run verify:computer-native && pnpm run build:cli && pnpm run build:electron-vite && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer && pnpm run build:mobile-web && pnpm run build:orcad-template", + "build:release:parallel": "pnpm run build:relay && pnpm run build:native && pnpm run verify:computer-native && pnpm run build:cli && pnpm run build:electron-vite:parallel && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer && pnpm run build:mobile-web && pnpm run build:orcad-template", "postinstall": "node config/scripts/rebuild-native-deps.mjs", "rebuild:electron": "node config/scripts/rebuild-native-deps.mjs", "reclaim:electron-dists": "node config/scripts/reclaim-electron-dists.mjs",