mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
666 lines
26 KiB
JavaScript
666 lines
26 KiB
JavaScript
import { existsSync } from 'node:fs'
|
|
import { chmod, lstat, mkdir, mkdtemp, readFile, realpath, rm, writeFile } from 'node:fs/promises'
|
|
import { createRequire } from 'node:module'
|
|
import { tmpdir } from 'node:os'
|
|
import { dirname, join } from 'node:path'
|
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
|
import { writeMobileWebBundleFixtureTree } from './mobile-web-bundle-fixture-tree.mjs'
|
|
|
|
const REPO_ROOT = join(import.meta.dirname, '..', '..')
|
|
|
|
const require = createRequire(import.meta.url)
|
|
const electronBuilderConfig = require('../electron-builder.config.cjs')
|
|
const { copyFiles, FileMatcher } = require('app-builder-lib/out/fileMatcher')
|
|
const FpmTarget = require('app-builder-lib/out/targets/FpmTarget').default
|
|
const electronBuilderNativeRebuild = require('./electron-builder-native-rebuild.cjs')
|
|
|
|
describe('electron-builder config', () => {
|
|
it('keeps the packaged app identity aligned with local-build validation', () => {
|
|
expect(electronBuilderConfig.appId).toBe(
|
|
require('../../src/shared/local-build-compatibility-contract.json').appId
|
|
)
|
|
})
|
|
|
|
it('excludes repo-only source trees from app.asar', () => {
|
|
const matcher = new FileMatcher('/app', '/dest', (value) => value, electronBuilderConfig.files)
|
|
matcher.prependPattern('**/*')
|
|
const isPacked = matcher.createFilter()
|
|
const packs = (repoPath) => isPacked(join('/app', repoPath), { isDirectory: () => false })
|
|
for (const sourcePath of [
|
|
'src/main/index.ts',
|
|
'config/electron-builder.config.cjs',
|
|
'cloud/apps/relay/index.ts',
|
|
'docs/README.md',
|
|
'mobile/app/index.tsx',
|
|
'native/windows-cli-launcher/main.cpp',
|
|
'skills/release/SKILL.md',
|
|
'skill-guides/release.md',
|
|
'skill-stubs/release.md',
|
|
'resources/skills/current-manifest.json',
|
|
'tests/e2e/example.spec.ts',
|
|
'examples/example.ts',
|
|
'pr-evidence/example.png',
|
|
'Casks/orca.rb',
|
|
'AGENTS.md',
|
|
'out/main/workspace.test.js',
|
|
'resources/plugins/launch/example.ts'
|
|
]) {
|
|
expect(packs(sourcePath), sourcePath).toBe(false)
|
|
}
|
|
expect(packs('out/main/index.js')).toBe(true)
|
|
expect(packs('node_modules/example/docs/index.js')).toBe(true)
|
|
expect(packs('node_modules/example/src/index.js')).toBe(true)
|
|
})
|
|
|
|
it('keeps release build staging out of app.asar while preserving runtime output', () => {
|
|
const matcher = new FileMatcher('/app', '/dest', (value) => value, electronBuilderConfig.files)
|
|
matcher.prependPattern('**/*')
|
|
const isPacked = matcher.createFilter()
|
|
expect(isPacked(join('/app', '.build'), { isDirectory: () => true })).toBe(false)
|
|
for (const stagingPath of [
|
|
'.build/release-javascript/release-javascript.tar.gz',
|
|
'.build/release-javascript/manifest.json',
|
|
'.build/release-javascript-123/out/main/index.js'
|
|
]) {
|
|
expect(isPacked(join('/app', stagingPath), { isDirectory: () => false })).toBe(false)
|
|
}
|
|
for (const runtimePath of [
|
|
'out/main/index.js',
|
|
'out/cli/index.js',
|
|
'out/renderer/index.html'
|
|
]) {
|
|
expect(isPacked(join('/app', runtimePath), { isDirectory: () => false })).toBe(true)
|
|
}
|
|
})
|
|
|
|
it('keeps local agent tooling out of app.asar', () => {
|
|
const matcher = new FileMatcher('/app', '/dest', (value) => value, electronBuilderConfig.files)
|
|
matcher.prependPattern('**/*')
|
|
const isPacked = matcher.createFilter()
|
|
const packs = (repoPath) => isPacked(join('/app', repoPath), { isDirectory: () => false })
|
|
|
|
for (const toolingPath of [
|
|
'.grok/skills/review-and-submit/review-and-submit/SKILL.md',
|
|
'.claude/skills/review-and-submit/review-and-submit/SKILL.md',
|
|
'.agents/skills/electron/SKILL.md',
|
|
'.codex/sessions/session.json'
|
|
]) {
|
|
expect(packs(toolingPath)).toBe(false)
|
|
}
|
|
expect(packs('out/main/index.js')).toBe(true)
|
|
expect(packs('out/web/web-index.html')).toBe(false)
|
|
expect(packs('out/web/assets/shared.js')).toBe(false)
|
|
expect(packs('out/renderer/web-index.html')).toBe(true)
|
|
expect(packs('out/renderer/.orca-web-client-files.json')).toBe(true)
|
|
})
|
|
|
|
it.each(['file', 'directory'])('keeps a root notes %s out of app.asar', async (kind) => {
|
|
const root = await mkdtemp(join(tmpdir(), 'orca-packaging-notes-'))
|
|
const source = join(root, 'app')
|
|
const destination = join(root, 'selected')
|
|
const runtimePaths = [
|
|
'package.json',
|
|
'out/main/index.js',
|
|
'out/renderer/index.html',
|
|
'out/cli/index.js',
|
|
'out/shared/index.js',
|
|
'out/main/notes/index.js',
|
|
'out/renderer/assets/notes/help.md',
|
|
'resources/notes/help.md',
|
|
'notes.txt'
|
|
]
|
|
const notesPaths =
|
|
kind === 'file'
|
|
? ['notes']
|
|
: [
|
|
'notes/build.log',
|
|
'notes/installed-orca-backup/Orca.exe',
|
|
'notes/installed-orca-backup/resources/app.asar',
|
|
'notes/orca-windows-setup.exe',
|
|
'notes/.recovery/state.json'
|
|
]
|
|
try {
|
|
for (const fixturePath of [...runtimePaths, ...notesPaths]) {
|
|
const file = join(source, fixturePath)
|
|
await mkdir(dirname(file), { recursive: true })
|
|
await writeFile(file, 'synthetic fixture\n')
|
|
}
|
|
if (kind === 'directory') {
|
|
await mkdir(join(source, 'notes', 'empty'))
|
|
}
|
|
const matcher = new FileMatcher(
|
|
source,
|
|
destination,
|
|
(value) => value,
|
|
electronBuilderConfig.files
|
|
)
|
|
// copyFiles adds the default include and prunes excluded directories during traversal.
|
|
await copyFiles([matcher])
|
|
for (const runtimePath of runtimePaths) {
|
|
expect(await readFile(join(destination, runtimePath), 'utf8')).toBe('synthetic fixture\n')
|
|
}
|
|
const isPacked = matcher.createFilter()
|
|
for (const notesPath of new Set(['notes', ...notesPaths])) {
|
|
const file = join(source, notesPath)
|
|
expect(isPacked(file, await lstat(file)), notesPath).toBe(false)
|
|
}
|
|
expect(existsSync(join(destination, 'notes'))).toBe(false)
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
// All-negation lists implicitly include **/*; missing examples exclusion shipped hostile-panel in 1.4.160-rc.3. Exercise real matcher, not pattern spelling.
|
|
it('keeps plugin authoring examples out of app.asar', () => {
|
|
const matcher = new FileMatcher('/app', '/dest', (value) => value, electronBuilderConfig.files)
|
|
// copyFiles() prepends this itself once the pattern list is all-negation.
|
|
matcher.prependPattern('**/*')
|
|
const isPacked = matcher.createFilter()
|
|
const packs = (repoPath) => isPacked(join('/app', repoPath), { isDirectory: () => false })
|
|
|
|
for (const authoringOnly of [
|
|
'examples/plugins/hostile-panel/panel.html',
|
|
'examples/plugins/hostile-panel/orca-plugin.json',
|
|
'examples/plugins/hello-orca/main.mjs',
|
|
'examples/plugins/hello-orca/orca-plugin.json'
|
|
]) {
|
|
expect(packs(authoringOnly)).toBe(false)
|
|
}
|
|
// The negation stays anchored at the app root, so nested `examples` segments still ship.
|
|
expect(packs('out/main/examples/index.js')).toBe(true)
|
|
})
|
|
|
|
// CI never creates ~270MB/branch out/electron-dev caches; only local packages exposed this bloat.
|
|
it('keeps cached dev Electron bundles and Vite dependencies out of app.asar', () => {
|
|
const matcher = new FileMatcher('/app', '/dest', (value) => value, electronBuilderConfig.files)
|
|
matcher.prependPattern('**/*')
|
|
const isPacked = matcher.createFilter()
|
|
const packs = (repoPath) => isPacked(join('/app', repoPath), { isDirectory: () => false })
|
|
|
|
for (const devBundlePath of [
|
|
'out/electron-dev/1a2b3c4d5e6f/Orca: dev.app/Contents/MacOS/Electron',
|
|
'out/electron-dev/1a2b3c4d5e6f/orca-dev-electron-app.json',
|
|
'.cache/vite/renderer/deps/react.js',
|
|
'.cache/vite/web/deps/react.js',
|
|
'.cache/vite.lock/owner'
|
|
]) {
|
|
expect(packs(devBundlePath)).toBe(false)
|
|
}
|
|
// The real build outputs sit beside it under out/ and must still ship.
|
|
expect(packs('out/main/index.js')).toBe(true)
|
|
expect(packs('out/renderer/index.html')).toBe(true)
|
|
})
|
|
|
|
// Duplicated relay.js added 14MB to asar and AV quarantine gutted the entire archive (#20966); packaged app uses resources/relay.
|
|
it('keeps the relay bundles out of app.asar and ships them only through extraResources', () => {
|
|
const matcher = new FileMatcher('/app', '/dest', (value) => value, electronBuilderConfig.files)
|
|
matcher.prependPattern('**/*')
|
|
const isPacked = matcher.createFilter()
|
|
const packs = (repoPath) => isPacked(join('/app', repoPath), { isDirectory: () => false })
|
|
|
|
for (const relayPath of [
|
|
'out/relay/linux-x64/relay.js',
|
|
'out/relay/win32-x64/relay.js',
|
|
'out/relay/darwin-arm64/relay-watcher.js',
|
|
'out/relay/wsl/wsl-agent-hook-relay.js'
|
|
]) {
|
|
expect(packs(relayPath)).toBe(false)
|
|
}
|
|
|
|
for (const platform of ['mac', 'linux', 'win']) {
|
|
expect(electronBuilderConfig[platform].extraResources).toContainEqual({
|
|
from: 'out/relay',
|
|
to: 'relay'
|
|
})
|
|
}
|
|
})
|
|
|
|
it('keeps runtime resources available through extraResources', () => {
|
|
const bundledPluginResources = expect.objectContaining({
|
|
from: 'resources/plugins/launch',
|
|
to: 'plugins/launch'
|
|
})
|
|
for (const platform of ['mac', 'linux', 'win']) {
|
|
expect(electronBuilderConfig[platform].extraResources).toContainEqual({
|
|
from: 'resources/skills',
|
|
to: 'skills'
|
|
})
|
|
expect(electronBuilderConfig[platform].extraResources).toEqual(
|
|
expect.arrayContaining([bundledPluginResources])
|
|
)
|
|
}
|
|
expect(electronBuilderConfig.mac.extraResources).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
from: 'native/computer-use-macos/.build/release/Orca Computer Use.app',
|
|
to: 'Orca Computer Use.app'
|
|
})
|
|
])
|
|
)
|
|
expect(electronBuilderConfig.linux.extraResources).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
from: 'native/computer-use-linux/runtime.py',
|
|
to: 'computer-use-linux/runtime.py'
|
|
})
|
|
])
|
|
)
|
|
expect(electronBuilderConfig.win.extraResources).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
from: 'native/computer-use-windows/runtime.ps1',
|
|
to: 'computer-use-windows/runtime.ps1'
|
|
}),
|
|
expect.objectContaining({
|
|
from: 'native/windows-cli-launcher/.build/orca.exe',
|
|
to: 'bin/orca.exe'
|
|
}),
|
|
expect.objectContaining({
|
|
from: 'native/windows-cli-launcher/.build/orca-administrator-restart.exe',
|
|
to: 'bin/orca-administrator-restart.exe'
|
|
})
|
|
])
|
|
)
|
|
})
|
|
|
|
it('ships one macOS serve-sim package through the runtime closure', () => {
|
|
const serveSimResources = electronBuilderConfig.mac.extraResources.filter((resource) =>
|
|
[join('node_modules', 'serve-sim'), 'serve-sim'].includes(resource.to)
|
|
)
|
|
|
|
expect(serveSimResources).toEqual([
|
|
expect.objectContaining({ to: join('node_modules', 'serve-sim') })
|
|
])
|
|
})
|
|
|
|
it.each(['mac', 'linux'])(
|
|
'loads Codex TOML parsing after %s runtime pruning',
|
|
async (platform) => {
|
|
const resourcesDir = await mkdtemp(join(tmpdir(), 'orca-packaged-codex-toml-'))
|
|
try {
|
|
const resource = electronBuilderConfig[platform].extraResources.find(
|
|
(entry) => entry.to === join('node_modules', 'smol-toml')
|
|
)
|
|
expect(resource).toBeDefined()
|
|
await copyFiles([
|
|
new FileMatcher(resource.from, join(resourcesDir, resource.to), (value) => value)
|
|
])
|
|
require('../packaged-runtime-node-modules.cjs').prunePackagedRuntimeNodeModules(
|
|
resourcesDir,
|
|
platform === 'mac' ? 'darwin' : 'linux',
|
|
process.arch
|
|
)
|
|
const stagedRequire = createRequire(join(resourcesDir, 'out', 'main', 'codex', 'trust.cjs'))
|
|
expect(await realpath(stagedRequire.resolve('smol-toml'))).toBe(
|
|
await realpath(join(resourcesDir, resource.to, 'dist', 'index.cjs'))
|
|
)
|
|
const { parse } = stagedRequire('smol-toml')
|
|
expect(parse('[projects."/workspace"]\ntrust_level = "trusted"\n')).toEqual({
|
|
projects: { '/workspace': { trust_level: 'trusted' } }
|
|
})
|
|
} finally {
|
|
await rm(resourcesDir, { recursive: true, force: true })
|
|
}
|
|
}
|
|
)
|
|
|
|
// Why: serve-sim's addon is a Mach-O, and Windows signing rejects every *.node that is not PE.
|
|
it('keeps serve-sim out of the Windows and Linux runtime closures', () => {
|
|
const {
|
|
PACKAGED_RUNTIME_PACKAGE_ROOTS,
|
|
createPackagedRuntimeNodeModuleResources
|
|
} = require('../packaged-runtime-node-modules.cjs')
|
|
expect(PACKAGED_RUNTIME_PACKAGE_ROOTS).not.toContain('serve-sim')
|
|
const serveSimTarget = join('node_modules', 'serve-sim')
|
|
expect(createPackagedRuntimeNodeModuleResources('linux').map((r) => r.to)).not.toContain(
|
|
serveSimTarget
|
|
)
|
|
expect(electronBuilderConfig.linux.extraResources.map((r) => r.to)).not.toContain(
|
|
serveSimTarget
|
|
)
|
|
expect(electronBuilderConfig.win.extraResources.map((r) => r.to)).not.toContain(serveSimTarget)
|
|
})
|
|
|
|
// Source shim unpacked under resources/win32/bin lacks adjacent orca.exe (#7351); only extraResources/bin copy works.
|
|
it('keeps the Windows CLI shim source tree out of app.asar', () => {
|
|
expect(electronBuilderConfig.files).toEqual(
|
|
expect.arrayContaining(['!resources/win32{,/**/*}'])
|
|
)
|
|
// Regression guard: the working shim must still ship via extraResources.
|
|
expect(electronBuilderConfig.win.extraResources).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
from: 'resources/win32/bin/orca.cmd',
|
|
to: 'bin/orca.cmd'
|
|
})
|
|
])
|
|
)
|
|
})
|
|
|
|
// macOS 26 UNUserNotificationCenter aborts helpers launched from Resources; ship under MacOS (#7929).
|
|
it('ships the mac notification-status helper in Contents/MacOS, not Resources', () => {
|
|
expect(electronBuilderConfig.mac.extraFiles).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
from: 'native/notification-status-macos/.build/release/orca-notification-status',
|
|
to: 'MacOS/orca-notification-status'
|
|
})
|
|
])
|
|
)
|
|
expect(electronBuilderConfig.mac.extraResources).not.toEqual(
|
|
expect.arrayContaining([expect.objectContaining({ to: 'orca-notification-status' })])
|
|
)
|
|
})
|
|
|
|
it('ships the mac keyboard-layout helper in Contents/MacOS, not Resources', () => {
|
|
expect(electronBuilderConfig.mac.extraFiles).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
from: 'native/keyboard-layout-macos/.build/release/orca-keyboard-layout',
|
|
to: 'MacOS/orca-keyboard-layout'
|
|
})
|
|
])
|
|
)
|
|
expect(electronBuilderConfig.mac.extraResources).not.toEqual(
|
|
expect.arrayContaining([expect.objectContaining({ to: 'orca-keyboard-layout' })])
|
|
)
|
|
})
|
|
|
|
it('unpacks the compiled CommonJS boundary with CLI runtime files', () => {
|
|
expect(electronBuilderConfig.asarUnpack).toEqual(
|
|
expect.arrayContaining(['out/package.json', 'out/cli/**', 'out/shared/**'])
|
|
)
|
|
})
|
|
|
|
// Missing unpacked entry silently restores in-process watcher and #7547 main crashes.
|
|
it('unpacks the forked parcel-watcher process entry', () => {
|
|
expect(electronBuilderConfig.asarUnpack).toEqual(
|
|
expect.arrayContaining(['out/main/parcel-watcher-process-entry.js'])
|
|
)
|
|
})
|
|
|
|
it('unpacks the replaceable WSL transcript filesystem process entry', async () => {
|
|
const entryFilename = 'wsl-transcript-fs-process-entry.js'
|
|
expect(electronBuilderConfig.asarUnpack).toContain(`out/main/${entryFilename}`)
|
|
})
|
|
|
|
// CLI Node mode cannot read asar; packed entry sends orca serve to Electron (#27307).
|
|
it('unpacks the orcad serve-selection entry the CLI runs', async () => {
|
|
const sharedSource = await readFile(
|
|
join(REPO_ROOT, 'src', 'shared', 'orcad-local-serve-selection.ts'),
|
|
'utf8'
|
|
)
|
|
const entryName = sharedSource.match(/ORCAD_LOCAL_SERVE_SELECTION_ENTRY = '([^']+)'/)?.[1]
|
|
expect(entryName).toBeDefined()
|
|
|
|
const matcher = new FileMatcher(
|
|
'/app',
|
|
'/dest',
|
|
(value) => value,
|
|
electronBuilderConfig.asarUnpack
|
|
)
|
|
const isUnpacked = matcher.createFilter()
|
|
expect(
|
|
isUnpacked(join('/app', 'out', 'main', `${entryName}.js`), { isDirectory: () => false })
|
|
).toBe(true)
|
|
})
|
|
|
|
// Swept one-time /tmp asar extracts cause web-client 500s and phone bundle failures until restart (#27976).
|
|
it('unpacks the web and mobile bundles a host serves by opening their files', () => {
|
|
const matcher = new FileMatcher(
|
|
'/app',
|
|
'/dest',
|
|
(value) => value,
|
|
electronBuilderConfig.asarUnpack
|
|
)
|
|
const isUnpacked = matcher.createFilter()
|
|
for (const webPath of [
|
|
'out/renderer/web-index.html',
|
|
'out/renderer/assets/index-abc123.js',
|
|
'out/renderer/.orca-web-client-files.json',
|
|
'out/mobile-web/manifest.json',
|
|
'out/mobile-web/assets/index-abc123.js'
|
|
]) {
|
|
expect(isUnpacked(join('/app', webPath), { isDirectory: () => false })).toBe(true)
|
|
}
|
|
})
|
|
|
|
it('keeps the worker-thread hang watchdog inside app.asar', () => {
|
|
expect(electronBuilderConfig.asarUnpack).not.toContain(
|
|
'out/main/main-thread-hang-watchdog-entry.js'
|
|
)
|
|
})
|
|
|
|
it('uses the multi-size icon source for Linux packages', () => {
|
|
expect(electronBuilderConfig.linux.icon).toBe('resources/build/icon.icns')
|
|
})
|
|
|
|
it('matches the Linux desktop entry to Electron window class', () => {
|
|
expect(electronBuilderConfig.linux.desktop.entry.StartupWMClass).toBe('orca')
|
|
})
|
|
|
|
it('uses the release artifact set as local Linux targets without changing existing names', () => {
|
|
expect(electronBuilderConfig.linux.target).toEqual(['AppImage', 'deb', 'rpm'])
|
|
expect(electronBuilderConfig.toolsets).toEqual({ appimage: '1.0.3' })
|
|
expect(electronBuilderConfig.appImage.artifactName).toBe('orca-linux.${ext}')
|
|
expect(electronBuilderConfig.deb.artifactName).toBe('orca-ide_${version}_${arch}.${ext}')
|
|
expect(electronBuilderConfig.rpm).toMatchObject({
|
|
packageName: 'orca-ide',
|
|
artifactName: 'orca-ide-${version}.${arch}.${ext}'
|
|
})
|
|
})
|
|
|
|
it('retains electron-builder runtime dependencies in deb and rpm packages', () => {
|
|
for (const target of ['deb', 'rpm']) {
|
|
const dependencies = electronBuilderConfig[target].depends
|
|
expect(dependencies).toEqual(
|
|
expect.arrayContaining(FpmTarget.prototype.getDefaultDepends(target))
|
|
)
|
|
expect(new Set(dependencies).size).toBe(dependencies.length)
|
|
}
|
|
})
|
|
|
|
it('validates each AppImage before electron-builder publishes it', async () => {
|
|
const root = await mkdtemp(join(tmpdir(), 'orca-electron-builder-appimage-'))
|
|
try {
|
|
const appImage = join(root, 'orca-linux.AppImage')
|
|
await writeFile(appImage, 'not an ELF')
|
|
await chmod(appImage, 0o755)
|
|
|
|
expect(() =>
|
|
electronBuilderConfig.artifactBuildCompleted({ file: appImage, arch: 1 })
|
|
).toThrow(/ELF header is outside/)
|
|
expect(() =>
|
|
electronBuilderConfig.artifactBuildCompleted({ file: join(root, 'orca-ide.deb') })
|
|
).not.toThrow()
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
it('uses a distinct AppImage name for Linux arm64 release uploads', () => {
|
|
const configPath = require.resolve('../electron-builder.config.cjs')
|
|
const original = process.env.ORCA_LINUX_ARM64_RELEASE
|
|
try {
|
|
delete require.cache[configPath]
|
|
process.env.ORCA_LINUX_ARM64_RELEASE = '1'
|
|
expect(require('../electron-builder.config.cjs').appImage.artifactName).toBe(
|
|
'orca-linux-arm64.${ext}'
|
|
)
|
|
} finally {
|
|
if (original === undefined) {
|
|
delete process.env.ORCA_LINUX_ARM64_RELEASE
|
|
} else {
|
|
process.env.ORCA_LINUX_ARM64_RELEASE = original
|
|
}
|
|
delete require.cache[configPath]
|
|
require('../electron-builder.config.cjs')
|
|
}
|
|
})
|
|
|
|
it('overrides packaged semver only for local macOS builds', () => {
|
|
const configPath = require.resolve('../electron-builder.config.cjs')
|
|
const original = process.env.ORCA_LOCAL_BUILD_VERSION
|
|
const originalMacRelease = process.env.ORCA_MAC_RELEASE
|
|
try {
|
|
delete require.cache[configPath]
|
|
delete process.env.ORCA_MAC_RELEASE
|
|
process.env.ORCA_LOCAL_BUILD_VERSION = '1.4.159-rc.0.local.123.abc'
|
|
expect(require('../electron-builder.config.cjs').extraMetadata).toEqual({
|
|
version: '1.4.159-rc.0.local.123.abc'
|
|
})
|
|
} finally {
|
|
if (originalMacRelease === undefined) {
|
|
delete process.env.ORCA_MAC_RELEASE
|
|
} else {
|
|
process.env.ORCA_MAC_RELEASE = originalMacRelease
|
|
}
|
|
if (original === undefined) {
|
|
delete process.env.ORCA_LOCAL_BUILD_VERSION
|
|
} else {
|
|
process.env.ORCA_LOCAL_BUILD_VERSION = original
|
|
}
|
|
delete require.cache[configPath]
|
|
require('../electron-builder.config.cjs')
|
|
}
|
|
})
|
|
|
|
it('never applies local semver to release packaging', () => {
|
|
const configPath = require.resolve('../electron-builder.config.cjs')
|
|
const originalLocalVersion = process.env.ORCA_LOCAL_BUILD_VERSION
|
|
const originalMacRelease = process.env.ORCA_MAC_RELEASE
|
|
try {
|
|
delete require.cache[configPath]
|
|
process.env.ORCA_LOCAL_BUILD_VERSION = '1.4.159-local.123.abc'
|
|
process.env.ORCA_MAC_RELEASE = '1'
|
|
expect(require('../electron-builder.config.cjs').extraMetadata).toBeUndefined()
|
|
} finally {
|
|
if (originalLocalVersion === undefined) {
|
|
delete process.env.ORCA_LOCAL_BUILD_VERSION
|
|
} else {
|
|
process.env.ORCA_LOCAL_BUILD_VERSION = originalLocalVersion
|
|
}
|
|
if (originalMacRelease === undefined) {
|
|
delete process.env.ORCA_MAC_RELEASE
|
|
} else {
|
|
process.env.ORCA_MAC_RELEASE = originalMacRelease
|
|
}
|
|
delete require.cache[configPath]
|
|
require('../electron-builder.config.cjs')
|
|
}
|
|
})
|
|
|
|
it('uses Orca native rebuild hook instead of electron-builder default rebuild', () => {
|
|
expect(electronBuilderConfig.beforeBuild).toBe(electronBuilderNativeRebuild)
|
|
expect(electronBuilderConfig.npmRebuild).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('arch-aware packaging guard', () => {
|
|
// electron-builder Arch enum: ia32=0, x64=1, armv7l=2, arm64=3.
|
|
const HOST_ARCH = process.arch === 'arm64' ? 3 : 1
|
|
const OTHER_ARCH = process.arch === 'arm64' ? 1 : 3
|
|
const OTHER_ARCH_NAME = process.arch === 'arm64' ? 'x64' : 'arm64'
|
|
const SHERPA_PLATFORM = process.platform === 'win32' ? 'win' : process.platform
|
|
const otherSherpa = `sherpa-onnx-${SHERPA_PLATFORM}-${OTHER_ARCH_NAME}`
|
|
|
|
// Native tests need valid mobile bundle for beforePack; use temp build, leaving bundle verification to its own suite.
|
|
let scratch
|
|
let bundleDir
|
|
beforeAll(async () => {
|
|
scratch = await mkdtemp(join(tmpdir(), 'orca-electron-builder-guard-'))
|
|
bundleDir = join(scratch, 'mobile-web')
|
|
await writeMobileWebBundleFixtureTree({ outDir: bundleDir })
|
|
})
|
|
afterAll(async () => {
|
|
await rm(scratch, { recursive: true, force: true })
|
|
})
|
|
|
|
const packHost = (arch) =>
|
|
electronBuilderConfig.beforePack(
|
|
{ electronPlatformName: process.platform, arch },
|
|
bundleDir,
|
|
() => undefined
|
|
)
|
|
|
|
it('allows packaging the host platform and architecture', () => {
|
|
expect(() => packHost(HOST_ARCH)).not.toThrow()
|
|
})
|
|
|
|
it('refuses to package a dark layout-owner build', () => {
|
|
const previous = process.env.ORCA_LAYOUT_OWNER
|
|
process.env.ORCA_LAYOUT_OWNER = 'main'
|
|
try {
|
|
expect(() => packHost(HOST_ARCH)).toThrow('ORCA_LAYOUT_OWNER=main')
|
|
} finally {
|
|
if (previous === undefined) {
|
|
delete process.env.ORCA_LAYOUT_OWNER
|
|
} else {
|
|
process.env.ORCA_LAYOUT_OWNER = previous
|
|
}
|
|
}
|
|
})
|
|
|
|
it('refuses to package an orcad template marked by a dark test build', async () => {
|
|
const templateDir = join(scratch, 'orcad-template')
|
|
await mkdir(templateDir, { recursive: true })
|
|
await writeFile(join(templateDir, 'ORCA_LAYOUT_OWNER_MAIN'), 'dark\n')
|
|
try {
|
|
expect(() =>
|
|
electronBuilderConfig.beforePack(
|
|
{ electronPlatformName: process.platform, arch: HOST_ARCH },
|
|
bundleDir,
|
|
() => undefined,
|
|
templateDir
|
|
)
|
|
).toThrow(`${templateDir} holds an ORCA_LAYOUT_OWNER=main build`)
|
|
} finally {
|
|
await rm(templateDir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
it.skipIf(process.platform === 'win32')(
|
|
'requires the other architecture natives to be installed',
|
|
() => {
|
|
const otherSherpaInstalled = existsSync(
|
|
join(REPO_ROOT, 'node_modules', otherSherpa, 'package.json')
|
|
)
|
|
const otherSherpaExpected = Object.hasOwn(
|
|
require('../../package.json').optionalDependencies,
|
|
otherSherpa
|
|
)
|
|
if (otherSherpaExpected && !otherSherpaInstalled) {
|
|
expect(() => packHost(OTHER_ARCH)).toThrow(otherSherpa)
|
|
expect(() => packHost(OTHER_ARCH)).toThrow('pnpm install:release')
|
|
} else {
|
|
expect(() => packHost(OTHER_ARCH)).not.toThrow()
|
|
}
|
|
}
|
|
)
|
|
|
|
// The host's own build is the foreign-arch case: a Windows checkout compiles these for its CPU only.
|
|
it.runIf(process.platform === 'win32')(
|
|
'refuses a Windows workspace addon built for another architecture',
|
|
() => {
|
|
for (const binary of ['orca_windows_registry.node', 'orca_windows_security.node']) {
|
|
expect(() => packHost(OTHER_ARCH)).toThrow(`${binary} for ${OTHER_ARCH_NAME}`)
|
|
}
|
|
}
|
|
)
|
|
|
|
it('requires installed Windows addons for Windows packaging', () => {
|
|
const windowsAddon = electronBuilderConfig.win.extraResources.some(
|
|
(resource) => resource.to === join('node_modules', '@vscode', 'windows-process-tree')
|
|
)
|
|
const packWindows = () =>
|
|
electronBuilderConfig.beforePack({ electronPlatformName: 'win32', arch: 1 }, bundleDir)
|
|
if (process.platform === 'win32' || windowsAddon) {
|
|
expect(packWindows).not.toThrow()
|
|
} else {
|
|
expect(packWindows).toThrow('@vscode/windows-process-tree')
|
|
expect(packWindows).toThrow('Windows packaging requires a Windows host')
|
|
// A workspace link exists everywhere; only its compiled binary proves the addon will load.
|
|
expect(packWindows).toThrow('@orca/windows-security')
|
|
}
|
|
})
|
|
})
|