mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 18:17:36 +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>
586 lines
26 KiB
JavaScript
586 lines
26 KiB
JavaScript
import { readFileSync } from 'node:fs'
|
|
import { runInNewContext } from 'node:vm'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { parse } from 'yaml'
|
|
import { classifyPrJobs } from './pr-code-change-scope.mjs'
|
|
import { WINDOWS_BOUNDARY_TESTS } from './pr-package-test-paths.mjs'
|
|
|
|
const read = (path) => parse(readFileSync(path, 'utf8'))
|
|
const workflow = (name) => read(`.github/workflows/${name}.yml`)
|
|
const action = read('.github/actions/install-node-dependencies/action.yml')
|
|
|
|
describe('CI dependency download caches', () => {
|
|
it('scopes desktop stores to the root lockfile and lets mixed installs opt in', () => {
|
|
expect(action.inputs['cache-dependency-path'].default).toBe('pnpm-lock.yaml')
|
|
for (const step of action.runs.steps.filter((step) => step.uses === 'actions/setup-node@v6')) {
|
|
expect(step.with['cache-dependency-path']).toBe('${{ inputs.cache-dependency-path }}')
|
|
expect(step.with['package-manager-cache']).toBe(false)
|
|
}
|
|
const install = action.runs.steps.find((step) => step.name === 'Install dependencies')
|
|
expect(install.if).toBeUndefined()
|
|
expect(install.run).toContain('pnpm install --frozen-lockfile --ignore-scripts')
|
|
expect(install.run).toContain(
|
|
'diff --exit-code -- package.json pnpm-lock.yaml pnpm-workspace.yaml'
|
|
)
|
|
const mobile = workflow('mobile').jobs.verify.steps.find((step) =>
|
|
step.uses?.includes('install-node-dependencies')
|
|
)
|
|
expect(mobile.with['cache-pnpm-store']).toBe('false')
|
|
expect(mobile.with['cache-dependency-path'].trim().split('\n')).toEqual([
|
|
'pnpm-lock.yaml',
|
|
'mobile/pnpm-lock.yaml'
|
|
])
|
|
})
|
|
|
|
it('builds internal packages before native preparation after a script-free install', () => {
|
|
const steps = action.runs.steps
|
|
const buildIndex = steps.findIndex((step) => step.name === 'Build internal packages')
|
|
expect(buildIndex).toBeGreaterThan(
|
|
steps.findIndex((step) => step.name === 'Install dependencies')
|
|
)
|
|
expect(buildIndex).toBeLessThan(
|
|
steps.findIndex((step) => step.name === 'Prepare native runtime')
|
|
)
|
|
expect(steps[buildIndex].if).toBeUndefined()
|
|
expect(steps[buildIndex].run).toBe('pnpm run --if-present build:packages')
|
|
})
|
|
|
|
it('restores PR stores except measured Windows, Linux and macOS installs, without a post-job save', () => {
|
|
const resolve = action.runs.steps.find((step) => step.id === 'pnpm-store')
|
|
const restore = action.runs.steps.find(
|
|
(step) => step.name === 'Restore pnpm download store without saving'
|
|
)
|
|
expect(restore.uses).toBe('actions/cache/restore@v5')
|
|
expect(restore.with.path).toBe('${{ steps.pnpm-store.outputs.path }}')
|
|
expect(restore.with.key).toBe(
|
|
'node-cache-${{ runner.os }}-${{ steps.pnpm-store.outputs.arch }}-pnpm-${{ hashFiles(inputs.cache-dependency-path) }}'
|
|
)
|
|
expect(restore.with['restore-keys']).toBeUndefined()
|
|
expect(resolve.env.LOCKFILE_HASH).toBe('${{ hashFiles(inputs.cache-dependency-path) }}')
|
|
expect(action.runs.steps.indexOf(resolve)).toBeLessThan(action.runs.steps.indexOf(restore))
|
|
expect(action.runs.steps.indexOf(restore)).toBeLessThan(
|
|
action.runs.steps.findIndex((step) => step.name === 'Install dependencies')
|
|
)
|
|
const saves = action.runs.steps.filter((step) => step.uses === 'actions/cache/save@v5')
|
|
expect(saves).toHaveLength(1)
|
|
expect(saves[0].name).toBe('Save pnpm verification record on main')
|
|
expect(saves[0].if).toContain("github.ref == 'refs/heads/main'")
|
|
expect(saves[0].if).toContain("github.event_name != 'pull_request'")
|
|
expect(saves[0].with.path).toBe('${{ steps.verification-cache.outputs.path }}')
|
|
const windows = workflow('pr').jobs.package_windows.steps.find((step) =>
|
|
step.uses?.includes('install-node-dependencies')
|
|
)
|
|
expect(windows.with['cache-dependency-path'].trim().split('\n')).toEqual([
|
|
'pnpm-lock.yaml',
|
|
'mobile/pnpm-lock.yaml'
|
|
])
|
|
})
|
|
|
|
it('keeps producer lookup optional and compatible with the existing store archive', () => {
|
|
const lookup = action.runs.steps.find((step) => step.id === 'pnpm-store-lookup')
|
|
const restore = action.runs.steps.find((step) => step.id === 'pnpm-store-restore')
|
|
expect(action.inputs['cache-pnpm-store-lookup-only'].default).toBe('auto')
|
|
expect(lookup.uses).toBe('actions/cache@v5')
|
|
expect(lookup.if).toBe("steps.pnpm-store-mode.outputs.lookup-only == 'true'")
|
|
expect(lookup.with).toEqual({
|
|
path: '${{ env.ORCA_PNPM_STORE_CACHE_PATH }}',
|
|
key: restore.with.key,
|
|
'lookup-only': true
|
|
})
|
|
expect(action.runs.steps.indexOf(lookup)).toBeLessThan(
|
|
action.runs.steps.findIndex((step) => step.name === 'Install dependencies')
|
|
)
|
|
expect(action.outputs['pnpm-store-cache-hit'].value).toBe(
|
|
'${{ steps.pnpm-store-lookup.outputs.cache-hit || steps.pnpm-store-restore.outputs.cache-hit || steps.requested-node.outputs.cache-hit || steps.default-node.outputs.cache-hit }}'
|
|
)
|
|
})
|
|
|
|
it.each([
|
|
['Windows x64 mixed PR', 'pull_request', 'Windows', 'X64', true, false, ''],
|
|
['Windows ARM64 mixed PR', 'pull_request', 'Windows', 'ARM64', true, true, ''],
|
|
['Windows x86 mixed PR', 'pull_request', 'Windows', 'X86', true, true, ''],
|
|
['Windows x64 root-only PR', 'pull_request', 'Windows', 'X64', false, false, ''],
|
|
['Windows ARM64 root-only PR', 'pull_request', 'Windows', 'ARM64', false, false, ''],
|
|
['Windows x86 root-only PR', 'pull_request', 'Windows', 'X86', false, true, ''],
|
|
['Windows x64 custom PR', 'pull_request', 'Windows', 'X64', 'cloud/pnpm-lock.yaml', true, ''],
|
|
[
|
|
'Windows ARM64 custom PR',
|
|
'pull_request',
|
|
'Windows',
|
|
'ARM64',
|
|
'cloud/pnpm-lock.yaml',
|
|
true,
|
|
''
|
|
],
|
|
['Windows ARM64 root-only push', 'push', 'Windows', 'ARM64', false, false, 'pnpm'],
|
|
[
|
|
'Windows ARM64 root-only manual run',
|
|
'workflow_dispatch',
|
|
'Windows',
|
|
'ARM64',
|
|
false,
|
|
false,
|
|
'pnpm'
|
|
],
|
|
['Explicit Linux PR opt-out', 'pull_request', 'Linux', 'X64', false, false, '', 'false'],
|
|
['Explicit Windows push opt-out', 'push', 'Windows', 'ARM64', false, false, '', 'false'],
|
|
[
|
|
'Explicit Windows manual opt-out',
|
|
'workflow_dispatch',
|
|
'Windows',
|
|
'X64',
|
|
false,
|
|
false,
|
|
'',
|
|
'false'
|
|
],
|
|
[
|
|
'Explicit custom-store opt-out',
|
|
'pull_request',
|
|
'Windows',
|
|
'X64',
|
|
'cloud/pnpm-lock.yaml',
|
|
false,
|
|
'',
|
|
'false'
|
|
],
|
|
['Windows x64 root-only push', 'push', 'Windows', 'X64', false, false, 'pnpm'],
|
|
['Linux x64 root-only PR', 'pull_request', 'Linux', 'X64', false, false, ''],
|
|
['Linux ARM64 root-only PR', 'pull_request', 'Linux', 'ARM64', false, false, ''],
|
|
['Linux x86 root-only PR', 'pull_request', 'Linux', 'X86', false, true, ''],
|
|
['Linux ARM root-only PR', 'pull_request', 'Linux', 'ARM', false, true, ''],
|
|
['Linux x64 custom PR', 'pull_request', 'Linux', 'X64', 'cloud/pnpm-lock.yaml', true, ''],
|
|
['Linux x64 root-only push', 'push', 'Linux', 'X64', false, false, 'pnpm'],
|
|
['Linux ARM64 root-only manual', 'workflow_dispatch', 'Linux', 'ARM64', false, false, 'pnpm'],
|
|
['macOS x64 root-only PR', 'pull_request', 'macOS', 'X64', false, false, ''],
|
|
['macOS ARM64 root-only PR', 'pull_request', 'macOS', 'ARM64', false, false, ''],
|
|
['macOS x86 root-only PR', 'pull_request', 'macOS', 'X86', false, true, ''],
|
|
['macOS x64 mixed PR', 'pull_request', 'macOS', 'X64', true, true, ''],
|
|
['macOS ARM64 mixed PR', 'pull_request', 'macOS', 'ARM64', true, true, ''],
|
|
['macOS ARM64 custom PR', 'pull_request', 'macOS', 'ARM64', 'cloud/pnpm-lock.yaml', true, ''],
|
|
['macOS ARM64 opted-out PR', 'pull_request', 'macOS', 'ARM64', true, false, '', 'false'],
|
|
['macOS x64 root-only push', 'push', 'macOS', 'X64', false, false, 'pnpm'],
|
|
['macOS ARM64 root-only manual', 'workflow_dispatch', 'macOS', 'ARM64', false, false, 'pnpm'],
|
|
['Linux x64 mixed PR', 'pull_request', 'Linux', 'X64', true, true, ''],
|
|
['Linux ARM64 mixed PR', 'pull_request', 'Linux', 'ARM64', true, true, ''],
|
|
['Windows x64 mixed push', 'push', 'Windows', 'X64', true, false, 'pnpm'],
|
|
['Windows x64 mixed manual run', 'workflow_dispatch', 'Windows', 'X64', true, false, 'pnpm'],
|
|
['Windows x64 lookup producer', 'push', 'Windows', 'X64', false, false, '', 'true', 'true'],
|
|
[
|
|
'Windows ARM64 lookup producer',
|
|
'schedule',
|
|
'Windows',
|
|
'ARM64',
|
|
false,
|
|
false,
|
|
'',
|
|
'true',
|
|
'true'
|
|
],
|
|
['macOS ARM64 lookup producer', 'push', 'macOS', 'ARM64', false, false, '', 'true', 'true'],
|
|
[
|
|
'Linux x64 lookup producer',
|
|
'workflow_dispatch',
|
|
'Linux',
|
|
'X64',
|
|
false,
|
|
false,
|
|
'',
|
|
'true',
|
|
'true'
|
|
],
|
|
['Opted-out lookup producer', 'push', 'Windows', 'ARM64', false, false, '', 'false', 'true'],
|
|
[
|
|
'macOS root PR lookup flag',
|
|
'pull_request',
|
|
'macOS',
|
|
'ARM64',
|
|
false,
|
|
false,
|
|
'',
|
|
'true',
|
|
'true'
|
|
],
|
|
['macOS mixed PR lookup flag', 'pull_request', 'macOS', 'ARM64', true, true, '', 'true', 'true']
|
|
])(
|
|
'%s keeps its scoped store policy',
|
|
(_name, event, os, arch, mixed, restore, cache, storeCache = 'true', lookupOnly = 'false') => {
|
|
const context = {
|
|
github: { event_name: event },
|
|
runner: { os, arch },
|
|
steps: {
|
|
'pnpm-store-mode': {
|
|
outputs: {
|
|
'lookup-only':
|
|
event !== 'pull_request' && storeCache !== 'false' && lookupOnly === 'true'
|
|
? 'true'
|
|
: ''
|
|
}
|
|
}
|
|
},
|
|
inputs: {
|
|
'cache-pnpm-store': storeCache,
|
|
'cache-pnpm-store-lookup-only': lookupOnly,
|
|
'cache-dependency-path':
|
|
typeof mixed === 'string'
|
|
? mixed
|
|
: mixed
|
|
? 'pnpm-lock.yaml\nmobile/pnpm-lock.yaml'
|
|
: 'pnpm-lock.yaml'
|
|
},
|
|
contains: (value, search) => value.toLowerCase().includes(search.toLowerCase())
|
|
}
|
|
const evaluate = (expression) =>
|
|
runInNewContext(
|
|
expression
|
|
.replaceAll(
|
|
'steps.pnpm-store-mode.outputs.lookup-only',
|
|
'steps["pnpm-store-mode"].outputs["lookup-only"]'
|
|
)
|
|
.replaceAll(
|
|
'inputs.cache-pnpm-store-lookup-only',
|
|
'inputs["cache-pnpm-store-lookup-only"]'
|
|
)
|
|
.replaceAll('inputs.cache-dependency-path', 'inputs["cache-dependency-path"]')
|
|
.replaceAll('inputs.cache-pnpm-store', 'inputs["cache-pnpm-store"]'),
|
|
context
|
|
)
|
|
for (const step of action.runs.steps.filter(
|
|
(step) =>
|
|
step.id === 'pnpm-store' || step.name === 'Restore pnpm download store without saving'
|
|
)) {
|
|
expect(evaluate(step.if)).toBe(
|
|
restore ||
|
|
(step.id === 'pnpm-store' &&
|
|
event !== 'pull_request' &&
|
|
storeCache !== 'false' &&
|
|
lookupOnly === 'true')
|
|
)
|
|
}
|
|
expect(evaluate(action.runs.steps.find((step) => step.id === 'pnpm-store-lookup').if)).toBe(
|
|
event !== 'pull_request' && storeCache !== 'false' && lookupOnly === 'true'
|
|
)
|
|
for (const step of action.runs.steps.filter(
|
|
(step) => step.uses === 'actions/setup-node@v6'
|
|
)) {
|
|
expect(evaluate(step.with.cache.slice(3, -2))).toBe(cache)
|
|
expect(step.with['package-manager-cache']).toBe(false)
|
|
}
|
|
}
|
|
)
|
|
|
|
it('opts Windows preparation out of download stores while retaining native caches', () => {
|
|
const installer = './.github/actions/install-node-dependencies'
|
|
const persistence = workflow('node-server-tests').jobs.persistence.steps.find(
|
|
(step) => step.uses === installer
|
|
)
|
|
const ssh = workflow('ssh-windows-hosts').jobs.hosts.steps.find(
|
|
(step) => step.uses === installer
|
|
)
|
|
const platformJobs = read(
|
|
workflow('ci-cache-warmup-platforms').jobs['warm-platforms'].uses
|
|
).jobs
|
|
const warmer = platformJobs['warm-windows'].steps.find((step) => step.uses === installer)
|
|
expect(action.inputs['cache-pnpm-store'].default).toBe('true')
|
|
expect(persistence.with['cache-pnpm-store']).toBe(
|
|
"${{ !inputs.build_template && runner.os != 'Windows' }}"
|
|
)
|
|
expect(ssh.with['cache-pnpm-store']).toBe('false')
|
|
expect(warmer.with['cache-pnpm-store']).toBe('false')
|
|
expect(warmer.with['native-runtime']).toBe('node')
|
|
expect(persistence.with['cache-pnpm-store-lookup-only']).toBe('true')
|
|
for (const job of [workflow('ci-cache-warmup').jobs.warm, platformJobs['warm-linux-runtime']]) {
|
|
const install = job.steps.find((step) => step.uses?.includes('install-node-dependencies'))
|
|
expect(install.with['cache-pnpm-store-lookup-only']).toBe('true')
|
|
}
|
|
})
|
|
|
|
it('keeps PR warmer validation within the workflow family that runs it', () => {
|
|
const shared = workflow('ci-cache-warmup')
|
|
const platforms = workflow('ci-cache-warmup-platforms')
|
|
expect(shared.on.pull_request.paths).toEqual([
|
|
'.github/workflows/ci-cache-warmup.yml',
|
|
'.github/actions/prepare-headless-compiler/**',
|
|
'config/scripts/headless-detector-compiler-cache.mjs',
|
|
'.github/actions/prepare-wayland-clipboard/**'
|
|
])
|
|
expect(platforms.on.pull_request.paths).toEqual([
|
|
'.github/workflows/ci-cache-warmup-platforms.yml',
|
|
'.github/workflows/ci-cache-warmup-platform-jobs.yml'
|
|
])
|
|
expect(platforms.concurrency).toEqual({
|
|
group: 'ci-cache-warmup-platforms-${{ github.event.pull_request.number || github.ref }}',
|
|
'cancel-in-progress': "${{ github.event_name == 'pull_request' }}"
|
|
})
|
|
expect(shared.jobs['warm-platforms'].uses).toBe(platforms.jobs['warm-platforms'].uses)
|
|
expect(shared.on.schedule).toEqual([{ cron: '41 */6 * * *' }])
|
|
expect(shared.on).toHaveProperty('workflow_dispatch')
|
|
for (const producer of [shared, platforms]) {
|
|
expect(producer.on.push.branches).toEqual(['main'])
|
|
expect(producer.on.push.paths).toEqual(
|
|
expect.arrayContaining([
|
|
'.github/workflows/ci-cache-warmup.yml',
|
|
'.github/workflows/ci-cache-warmup-platforms.yml',
|
|
'.github/workflows/ci-cache-warmup-platform-jobs.yml',
|
|
'.github/actions/install-node-dependencies/**',
|
|
'.github/actions/prepare-native-runtime/**',
|
|
'native/windows-security/**',
|
|
'pnpm-lock.yaml'
|
|
])
|
|
)
|
|
}
|
|
})
|
|
|
|
it.each([
|
|
['pull_request', false, true, false, false],
|
|
['pull_request', true, false, false, false],
|
|
['push', false, true, true, false],
|
|
['schedule', false, true, true, true],
|
|
['workflow_dispatch', false, true, true, true]
|
|
])(
|
|
'retains eligible cache producers and Windows validation on %s (draft %s)',
|
|
(event, draft, admitted, armRuns, sharedPlatforms) => {
|
|
const shared = workflow('ci-cache-warmup')
|
|
const platformWorkflow = workflow('ci-cache-warmup-platforms')
|
|
const platforms = read(platformWorkflow.jobs['warm-platforms'].uses)
|
|
const contextFor = (isDraft) => ({
|
|
github: {
|
|
event_name: event,
|
|
event: event === 'pull_request' ? { pull_request: { draft: isDraft } } : {}
|
|
}
|
|
})
|
|
const context = contextFor(draft)
|
|
expect(runInNewContext(platforms.jobs['warm-linux-runtime'].if, context)).toBe(armRuns)
|
|
expect(runInNewContext(shared.jobs['warm-platforms'].if, context)).toBe(sharedPlatforms)
|
|
for (const guard of [shared.jobs.warm.if, platformWorkflow.jobs['warm-platforms'].if]) {
|
|
expect(runInNewContext(guard, context)).toBe(admitted)
|
|
expect(runInNewContext(guard, contextFor(!draft))).toBe(event !== 'pull_request' || draft)
|
|
}
|
|
expect(platforms.jobs['warm-windows'].if).toBeUndefined()
|
|
expect(platforms.jobs['warm-windows'].strategy.matrix.os).toEqual([
|
|
'windows-2022',
|
|
'windows-11-arm',
|
|
'windows-2025'
|
|
])
|
|
}
|
|
)
|
|
|
|
it.each([
|
|
['windows-2022', 'X64', true],
|
|
['windows-11-arm', 'ARM64', false],
|
|
['windows-2025', 'X64', false]
|
|
])('warms native addons without unused CLI builds on %s', (os, arch, cli) => {
|
|
const platforms = read(workflow('ci-cache-warmup-platforms').jobs['warm-platforms'].uses)
|
|
const warm = platforms.jobs['warm-windows']
|
|
const context = { runner: { arch }, matrix: { os } }
|
|
for (const name of ['Cache Windows CLI launcher', 'Build the Windows native executables']) {
|
|
const step = warm.steps.find((step) => step.name === name)
|
|
expect(step, name).toBeDefined()
|
|
expect(runInNewContext(step.if, context)).toBe(cli)
|
|
}
|
|
const consumer = workflow('wsl-orcad-e2e').jobs.wsl2
|
|
const installer = './.github/actions/install-node-dependencies'
|
|
expect(consumer['runs-on']).toBe('windows-2025')
|
|
expect(warm.steps.find((step) => step.uses === installer).with['native-runtime']).toBe(
|
|
consumer.steps.find((step) => step.uses === installer).with['native-runtime']
|
|
)
|
|
})
|
|
|
|
it('validates the omitted Linux warmer preparation in the real PR consumers', () => {
|
|
const platforms = read(workflow('ci-cache-warmup-platforms').jobs['warm-platforms'].uses)
|
|
const warm = platforms.jobs['warm-linux-runtime']
|
|
const pr = workflow('pr')
|
|
const units = workflow('unit-tests').jobs.test
|
|
const installer = './.github/actions/install-node-dependencies'
|
|
const warmInstall = warm.steps.find((step) => step.uses === installer)
|
|
const unitInstall = units.steps.find((step) => step.uses === installer)
|
|
expect(warm['runs-on']).toBe('${{ matrix.runner }}')
|
|
expect(warm.strategy.matrix.runner).toEqual([
|
|
'blacksmith-4vcpu-ubuntu-2404-arm',
|
|
pr.jobs.test.with.runner
|
|
])
|
|
expect(pr.jobs.preflight['runs-on']).toBe('blacksmith-8vcpu-ubuntu-2404')
|
|
expect(pr.jobs.test.with.runner).toBe('blacksmith-8vcpu-ubuntu-2404')
|
|
expect(units['runs-on']).toBe('${{ inputs.runner }}')
|
|
expect(JSON.parse(pr.jobs.test.with.node_versions)).toEqual(['24'])
|
|
expect(warmInstall.if).toBeUndefined()
|
|
expect(unitInstall.if).toBeUndefined()
|
|
expect(warmInstall.with['native-runtime']).toBe('node')
|
|
expect(unitInstall.with['native-runtime']).toBe(warmInstall.with['native-runtime'])
|
|
expect(warmInstall.with['node-version']).toBe('24')
|
|
expect(unitInstall.with['node-version']).toBe('${{ matrix.node }}')
|
|
expect(unitInstall.with['cache-electron-package']).toBe(
|
|
warmInstall.with['cache-electron-package']
|
|
)
|
|
const preflightInstalls = pr.jobs.preflight.steps.filter((step) => step.uses === installer)
|
|
expect(preflightInstalls.map((step) => step.if)).toEqual([
|
|
"needs.code_paths.outputs.mobile_dependencies != 'true'",
|
|
"needs.code_paths.outputs.mobile_dependencies == 'true'"
|
|
])
|
|
for (const step of preflightInstalls) {
|
|
expect(step.with['native-runtime']).toBe('node')
|
|
expect(step.with['node-version']).toBe('24')
|
|
}
|
|
const populate = 'node config/scripts/install-electron-package-binary.mjs'
|
|
const unitPopulate = units.steps.find((step) => step.run === populate)
|
|
expect(unitPopulate.if).toBeUndefined()
|
|
expect(warm.steps.map((step) => step.uses ?? step.run)).toEqual([
|
|
'actions/checkout@v6',
|
|
installer,
|
|
populate,
|
|
'node config/scripts/ensure-native-runtime.mjs --check-only'
|
|
])
|
|
const native = read('.github/actions/prepare-native-runtime/action.yml')
|
|
const identity = native.runs.steps.find((step) => step.id === 'native-cache-scope')
|
|
const inputs = [...identity.env.NATIVE_SOURCE_HASH.matchAll(/'([^']+)'/g)].map(
|
|
(match) => match[1]
|
|
)
|
|
for (const path of [
|
|
...inputs,
|
|
'.github/actions/install-node-dependencies/action.yml',
|
|
...workflow('ci-cache-warmup-platforms').on.pull_request.paths,
|
|
'.github/workflows/ci-cache-warmup.yml'
|
|
]) {
|
|
expect(classifyPrJobs([path], { allowMobileCiTools: true }), path).toMatchObject({
|
|
static_analysis: true,
|
|
typecheck: true,
|
|
test: true,
|
|
mobile_ci_tools: false
|
|
})
|
|
}
|
|
})
|
|
|
|
it('restores Windows packaging downloads from the release cache without a PR upload', () => {
|
|
const packaging = workflow('pr').jobs.package_windows
|
|
const restore = packaging.steps.find((step) => step.name === 'Cache electron-builder downloads')
|
|
const release = workflow('release-cut').jobs.build
|
|
const windows = release.strategy.matrix.include.find((entry) => entry.platform === 'win')
|
|
const save = release.steps.find((step) => step.name === 'Cache electron-builder downloads')
|
|
|
|
expect(packaging['runs-on']).toBe(windows.os)
|
|
expect(restore.uses).toBe('actions/cache/restore@v5')
|
|
// Cache versions include the path list, so matching key strings alone cannot prove reuse.
|
|
expect(restore.with.path).toBe(windows.eb_cache_path)
|
|
expect(restore.with.key).toBe(save.with.key.replace('${{ matrix.platform }}', 'win'))
|
|
expect(restore.with['restore-keys']).toBe(
|
|
save.with['restore-keys'].replace('${{ matrix.platform }}', 'win')
|
|
)
|
|
expect(save.uses).toBe('actions/cache@v5')
|
|
expect(save.with.path).toBe('${{ matrix.eb_cache_path }}')
|
|
for (const name of ['dev-channel-win-build', 'windows-signing-rehearsal']) {
|
|
const writer = Object.values(workflow(name).jobs)
|
|
.flatMap((job) => job.steps ?? [])
|
|
.find((step) => step.name === 'Cache electron-builder downloads')
|
|
expect(writer.uses, name).toBe('actions/cache@v5')
|
|
expect(writer.with.path, name).toBe(restore.with.path)
|
|
expect(writer.with.key, name).toBe(restore.with.key)
|
|
expect(writer.with['restore-keys'], name).toBe(restore.with['restore-keys'])
|
|
}
|
|
})
|
|
|
|
it('shares Linux PR downloads with main x64 release builds across runner pools', () => {
|
|
const packaging = workflow('pr').jobs.package
|
|
const consumer = packaging.steps.find(
|
|
(step) => step.name === 'Cache electron-builder downloads'
|
|
)
|
|
const release = workflow('release-cut').jobs.build
|
|
const combined = release.steps.find((step) => step.name === 'Cache electron-builder downloads')
|
|
const writer = release.steps.find(
|
|
(step) => step.name === 'Seed shared Linux packaging downloads'
|
|
)
|
|
const linux = release.strategy.matrix.include.find((entry) => entry.platform === 'linux-x64')
|
|
|
|
expect([linux.os, 'blacksmith-4vcpu-ubuntu-2404']).toContain(packaging['runs-on'])
|
|
expect(writer.if).toBe("matrix.platform == 'linux-x64' && github.ref == 'refs/heads/main'")
|
|
expect(writer.uses).toBe('actions/cache@v5')
|
|
expect(writer.with.path).toBe(consumer.with.path)
|
|
expect(writer.with.key).toBe(consumer.with.key)
|
|
expect(writer.with['restore-keys']).toBeUndefined()
|
|
expect(writer.with['lookup-only']).toBe(true)
|
|
expect(release.steps.indexOf(writer)).toBeGreaterThan(release.steps.indexOf(combined))
|
|
expect(consumer.uses).toBe('actions/cache/restore@v5')
|
|
expect(consumer.with['restore-keys'].trim()).toBe('electron-builder-linux-')
|
|
expect(combined.uses).toBe('actions/cache@v5')
|
|
expect(linux.eb_cache_path.trim().split('\n')).toEqual([
|
|
'~/.cache/electron',
|
|
'~/.cache/electron-builder'
|
|
])
|
|
})
|
|
})
|
|
|
|
it('shares Electron archives with PRs without uploading PR-local copies', () => {
|
|
const save = action.runs.steps.find((step) => step.name === 'Cache Electron package archive')
|
|
const restore = action.runs.steps.find(
|
|
(step) => step.name === 'Restore Electron package archive without saving'
|
|
)
|
|
expect(save.if).toContain("github.event_name != 'pull_request' || runner.os != 'Linux'")
|
|
expect(restore.if).toContain("github.event_name == 'pull_request' && runner.os == 'Linux'")
|
|
expect(save.if).toContain("steps.electron-package-cache.outputs.version != ''")
|
|
expect(restore.if).toContain("steps.electron-package-cache.outputs.version != ''")
|
|
expect(save.uses).toBe('actions/cache@v5')
|
|
expect(restore.uses).toBe('actions/cache/restore@v5')
|
|
expect(restore.with).toEqual(save.with)
|
|
})
|
|
|
|
describe('release install targets', () => {
|
|
const macCpuFlag = '--cpu=current,x64,arm64'
|
|
// Both shapes: `run:` steps and steps wrapped in nick-fields/retry (`with.command`).
|
|
const installCommand = (step) => step.with?.command ?? step.run
|
|
const installSteps = (name) =>
|
|
Object.values(workflow(name).jobs)
|
|
.flatMap((job) => job.steps ?? [])
|
|
.filter((step) => installCommand(step)?.includes('pnpm install '))
|
|
const installCommands = (name) => installSteps(name).map(installCommand)
|
|
|
|
it.each(['adhoc-mac-build', 'daily-mac-build', 'hourly-mac-build', 'release-mac-build'])(
|
|
'%s installs both mac CPU variants for the x64+arm64 package config',
|
|
(name) => {
|
|
const installs = installCommands(name)
|
|
expect(installs.length).toBeGreaterThan(0)
|
|
expect(installs.some((command) => command.includes(macCpuFlag))).toBe(true)
|
|
}
|
|
)
|
|
|
|
// Transient Node-header ECONNRESET once failed windows-registry rebuild and blocked the release cut.
|
|
it('retries every release-cut install so one transient download cannot fail a cut', () => {
|
|
const installs = installSteps('release-cut')
|
|
expect(installs.length).toBeGreaterThan(0)
|
|
for (const step of installs) {
|
|
expect(step.uses).toBe('nick-fields/retry@v4')
|
|
expect(step.with.max_attempts).toBeGreaterThan(1)
|
|
}
|
|
})
|
|
|
|
it.each(['release-cut', 'dev-channel-win-build', 'windows-signing-rehearsal'])(
|
|
'%s keeps installs scoped to the runner host',
|
|
(name) => {
|
|
const installs = installCommands(name)
|
|
expect(installs.length).toBeGreaterThan(0)
|
|
for (const command of installs) {
|
|
expect(command).not.toContain('--os=')
|
|
expect(command).not.toContain('--cpu=')
|
|
}
|
|
}
|
|
)
|
|
|
|
it('offers the mac CPU targets for local packaging without touching the lockfile', () => {
|
|
const script = JSON.parse(readFileSync('package.json', 'utf8')).scripts['install:release']
|
|
expect(script).toContain('--frozen-lockfile')
|
|
expect(script).toContain(macCpuFlag)
|
|
})
|
|
|
|
it('keeps installed Windows addon checks in the Windows CI lane', () => {
|
|
const steps = Object.values(workflow('pr').jobs).flatMap((job) => job.steps ?? [])
|
|
const test = steps.find((step) => step.name === 'Test Windows-specific boundaries')
|
|
expect(test.run).toContain('config/scripts/pr-package-test-paths.mjs')
|
|
expect(WINDOWS_BOUNDARY_TESTS).toEqual(
|
|
expect.arrayContaining([
|
|
'config/scripts/windows-process-tree-gyp-path.test.mjs',
|
|
'config/scripts/windows-process-tree-gyp-rebuild.test.mjs',
|
|
'config/scripts/package-electron-runtime-contract.test.mjs',
|
|
'config/scripts/electron-builder-runtime-resources.test.mjs'
|
|
])
|
|
)
|
|
})
|
|
})
|