mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 08:02:12 +00:00
feat(packaging): ship the orcad server template in desktop builds (#24155)
* build(orcad): merge per-runner prebuild slot trees into one matrix Each node-server lane builds only its own node-pty slot. Release CI needs their union before `build:orcad-prebuilds --require-slots` and the template build can run; merge-orcad-prebuilds.mjs verifies every lane's files against its own manifest, refuses duplicate slots and mismatched node-pty/N-API/Node-header builds, then writes one merged manifest. * build(orcad): keep agent-browser out of the desktop deployment template The template rides inside every desktop build (design D2). Seven ~10 MB agent-browser binaries would be ~76 MB, more than the rest of the template; design D2's package contents never listed it, and a slot without one already reports no headless browser. ORCAD_OMIT_AGENT_BROWSER=1 skips the copy; standalone build:orcad still includes it. * feat(packaging): ship the orcad deployment template in desktop builds Design D2: the server JS and every target's addons ship inside the app, as out/relay does; the ~120 MB Node runtimes stay excluded and are downloaded on demand. electron-builder copies out/orcad-template to Resources/orcad-template on every desktop OS, which is the first path materializeOrcadArtifact tries (process.resourcesPath). Platform signing rewrites native bytes the template manifest hashes: - macOS: the tree is signIgnored (codesign rejects its ELF/PE payloads); afterPack signs the darwin targets' Mach-O files with the app identity, as notarization requires, then reseals only those manifest entries. - Windows: SignPath signs after packaging, so release CI reseals from the inner-signing list (packaged-orcad-template.cjs --reseal-signed). Every other file must still match the build's hashes; afterPack verifies. ORCA_REQUIRE_ORCAD_TEMPLATE=1 makes a missing template fail beforePack and afterPack; without it a build ships none and SSH relays keep the legacy path. verify-packaged-orcad-template.test.mjs's "unused, excluded" contract is reversed on purpose. * ci(release): build the orcad template from qualified lanes and package it node-server-tests.yml becomes callable with a ref and build_template. With build_template, each lane that owns a release slot (macOS, Windows, the glibc 2.28 and Alpine lanes, and the glibc 2.17 compat lane) uploads its qualified out/orcad-prebuilds, the Windows lane also uploads both process-table addons, and desktop_template merges them, gates the full matrix plus the compat slot with --require-slots, runs build:orcad-template and uploads the orcad-template artifact. release-cut calls it at the release tag beside the other gates. The build and build-mac jobs wait for it, download it into out/orcad-template (the mac workflow from the parent run), and require it via ORCA_REQUIRE_ORCAD_TEMPLATE. The Windows signing staging skips the template's Linux/macOS payloads, and a reseal step records SignPath's bytes before the installer rebuild. A template-scoped concurrency group keeps a release call and main's push runs from cancelling each other. * test(orcad): keep the packaged-lookup imports clear of the compat-slot import edits * ci(orcad): let a rerun lane replace its template artifacts upload-artifact v4 refuses a second upload under an existing name in the same run, so rerunning a flaky node-server lane during a release would fail at the upload instead of re-qualifying the slot. * ci(node-server): build the template's Windows addons before the lane switches to Node 18 The addon build script imports TypeScript, which Node 18 cannot load, so every build_template run (release-cut included) failed on windows-2022. * fix(build): ship the orcad template's shared node_modules electron-builder's extraResources filter always drops the root node_modules of a source directory, so packaged apps lost orcad-template/node_modules and the afterPack verify failed. Copy it through its own resource entry. --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
This commit is contained in:
@@ -36,15 +36,33 @@ on:
|
||||
- '.github/actions/install-node-dependencies/**'
|
||||
- '.github/workflows/node-server-tests.yml'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_template:
|
||||
description: Also merge every lane's slot into the desktop orcad template artifact
|
||||
type: boolean
|
||||
default: false
|
||||
# Release packaging calls this to build the orcad template it ships (design D2).
|
||||
workflow_call:
|
||||
inputs:
|
||||
ref:
|
||||
description: Git ref every lane checks out, e.g. the release tag
|
||||
type: string
|
||||
default: ''
|
||||
build_template:
|
||||
description: Upload each lane's release slot and merge them into the orcad-template artifact
|
||||
type: boolean
|
||||
default: false
|
||||
schedule:
|
||||
- cron: '30 11 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Why a run-scoped group for template builds: a release call shares github.ref with main's push
|
||||
# runs, and cancelling either would drop a release's template or a main qualification.
|
||||
concurrency:
|
||||
group: node-server-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
group: node-server-${{ inputs.build_template && format('template-{0}', github.run_id) || github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ !inputs.build_template }}
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
@@ -91,6 +109,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: false
|
||||
- uses: ./.github/actions/install-node-dependencies
|
||||
with:
|
||||
@@ -125,6 +144,24 @@ jobs:
|
||||
echo "ORCA_BUN_ORCAD_SLOT=$RUNNER_TEMP/bun-orcad" >> "$GITHUB_ENV"
|
||||
echo "BUN_EXECUTABLE=$(command -v bun)" >> "$GITHUB_ENV"
|
||||
- run: pnpm test:node-server --artifact ${{ runner.os == 'Linux' && '--cross-runtime' || '' }}
|
||||
# Only a Windows runner compiles it; arm64 cross-compiles here, as release-cut does for the relay.
|
||||
# Before the Node 18 check below: the build script imports TypeScript, which Node 18 cannot load.
|
||||
- name: Build the Windows process-table addons for the desktop template
|
||||
if: inputs.build_template && matrix.os == 'windows-2022'
|
||||
shell: bash
|
||||
run: |
|
||||
node config/scripts/build-windows-process-tree-relay-addon.mjs --arch=x64
|
||||
node config/scripts/build-windows-process-tree-relay-addon.mjs --arch=arm64
|
||||
- name: Keep the Windows process-table addons for the desktop template
|
||||
if: inputs.build_template && matrix.os == 'windows-2022'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: orcad-windows-process-tree
|
||||
path: .build/windows-process-tree/
|
||||
include-hidden-files: true
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
overwrite: true
|
||||
- uses: actions/setup-node@v6
|
||||
if: runner.arch == 'X64'
|
||||
with:
|
||||
@@ -136,6 +173,17 @@ jobs:
|
||||
node out/orcad/orcad.js --orcad-smoke-load-check
|
||||
node out/orcad/orcad.js --orcad-profile-state-preflight 00000000-0000-4000-8000-000000000018 | tee "$RUNNER_TEMP/preflight.json"
|
||||
node -e "const r=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));if(r.runtime!=='node'||!/^24\./.test(r.runtimeVersion))process.exit(1)" "$RUNNER_TEMP/preflight.json"
|
||||
# Linux release slots come from the floor and Alpine lanes; these runners own the rest.
|
||||
- name: Keep this runner's qualified slot for the desktop template
|
||||
if: inputs.build_template && runner.os != 'Linux'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: orcad-prebuild-${{ matrix.os }}
|
||||
path: out/orcad-prebuilds/
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
# A rerun attempt re-uploads under the same name, which v4 otherwise refuses.
|
||||
overwrite: true
|
||||
|
||||
linux_glibc_floor:
|
||||
needs: [changes, persistence]
|
||||
@@ -170,6 +218,7 @@ jobs:
|
||||
run: dnf install -y git procps-ng unzip which xz
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: false
|
||||
- name: Trust the checked-out workspace
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
@@ -181,6 +230,15 @@ jobs:
|
||||
pnpm build:orcad-prebuilds --smoke
|
||||
- run: pnpm build:orcad
|
||||
- run: pnpm test:node-server --artifact
|
||||
- name: Keep this runner's glibc 2.28 slot for the desktop template
|
||||
if: inputs.build_template
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: orcad-prebuild-glibc-${{ matrix.os }}
|
||||
path: out/orcad-prebuilds/
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
overwrite: true
|
||||
|
||||
linux_glibc217_compat:
|
||||
needs: [changes, persistence]
|
||||
@@ -196,6 +254,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: false
|
||||
- uses: ./.github/actions/install-node-dependencies
|
||||
# Design D6 rung B: the opt-in linux-x64-glibc217 slot beside the unofficial glibc-217 Node.
|
||||
@@ -223,6 +282,15 @@ jobs:
|
||||
# The image's devtoolset LD_LIBRARY_PATH must not stand in for a host C++ runtime.
|
||||
env -u LD_LIBRARY_PATH node config/scripts/build-orcad-prebuilds.mjs --slot=linux-x64-glibc217 --smoke
|
||||
GLIBC217_COMPAT_SLOT
|
||||
- name: Keep the glibc 2.17 compat slot for the desktop template
|
||||
if: inputs.build_template
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: orcad-prebuild-glibc217
|
||||
path: out/orcad-prebuilds/
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
overwrite: true
|
||||
|
||||
linux_musl:
|
||||
needs: [changes, persistence]
|
||||
@@ -242,6 +310,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: false
|
||||
- name: Verify native Alpine artifact and persistence
|
||||
run: |
|
||||
@@ -261,3 +330,64 @@ jobs:
|
||||
pnpm build:orcad
|
||||
pnpm test:node-server --artifact
|
||||
NODE_SERVER_QUALIFICATION
|
||||
- name: Keep this runner's musl slot for the desktop template
|
||||
if: inputs.build_template
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: orcad-prebuild-musl-${{ matrix.os }}
|
||||
path: out/orcad-prebuilds/
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
overwrite: true
|
||||
|
||||
# Design D2: the desktop ships every target's addons, merged from the lanes that qualified them.
|
||||
desktop_template:
|
||||
needs: [persistence, linux_glibc_floor, linux_glibc217_compat, linux_musl]
|
||||
if: >-
|
||||
${{ !cancelled() && inputs.build_template &&
|
||||
needs.persistence.result == 'success' && needs.linux_glibc_floor.result == 'success' &&
|
||||
needs.linux_glibc217_compat.result == 'success' && needs.linux_musl.result == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: false
|
||||
- uses: ./.github/actions/install-node-dependencies
|
||||
- name: Collect every lane's slot
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: orcad-prebuild-*
|
||||
path: ${{ runner.temp }}/orcad-prebuild-lanes
|
||||
- name: Collect the Windows process-table addons
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: orcad-windows-process-tree
|
||||
path: .build/windows-process-tree
|
||||
- name: Merge the lanes and gate the full slot matrix
|
||||
shell: bash
|
||||
run: |
|
||||
node config/scripts/merge-orcad-prebuilds.mjs "$RUNNER_TEMP"/orcad-prebuild-lanes/*
|
||||
pnpm build:orcad-prebuilds --require-slots
|
||||
pnpm build:orcad-prebuilds --require-slots linux-x64-glibc217
|
||||
- run: pnpm build:orcad-template
|
||||
- name: Report the template size
|
||||
shell: bash
|
||||
run: |
|
||||
{
|
||||
echo '### orcad template'
|
||||
echo '```'
|
||||
du -sh out/orcad-template
|
||||
du -sh out/orcad-template/targets/*
|
||||
echo '```'
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: orcad-template
|
||||
path: out/orcad-template/
|
||||
# The per-target .server-target and .runtime-node markers are dotfiles.
|
||||
include-hidden-files: true
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
overwrite: true
|
||||
|
||||
@@ -1149,6 +1149,19 @@ jobs:
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
# Design D2: every desktop build ships the orcad template (server JS plus every target's
|
||||
# addons), merged from the node-server lanes that qualified each slot at this tag. It needs no
|
||||
# signing quota, so it runs beside the release gates instead of behind them.
|
||||
orcad-template:
|
||||
needs: cut
|
||||
if: needs.cut.outputs.should_release == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/node-server-tests.yml
|
||||
with:
|
||||
ref: refs/tags/${{ needs.cut.outputs.tag }}
|
||||
build_template: true
|
||||
|
||||
# Why: artifact jobs submit Windows binaries to SignPath. Keep every
|
||||
# quota-consuming build behind all blocking release gates so a late test
|
||||
# failure cannot create signing requests that can never be published.
|
||||
@@ -1175,8 +1188,12 @@ jobs:
|
||||
needs:
|
||||
- cut
|
||||
- create-release
|
||||
- orcad-template
|
||||
- release-preflight
|
||||
if: needs.cut.outputs.should_release == 'true'
|
||||
env:
|
||||
# beforePack and afterPack fail the package when the template is absent.
|
||||
ORCA_REQUIRE_ORCAD_TEMPLATE: '1'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -1435,6 +1452,13 @@ jobs:
|
||||
# the PowerShell scan on every Windows SSH host.
|
||||
ORCA_REQUIRE_RELAY_NATIVE_ADDONS: ${{ matrix.platform == 'win' && 'x64,arm64' || '' }}
|
||||
|
||||
# After the app build so nothing that cleans out/ can drop it; electron-builder ships it.
|
||||
- name: Download the orcad deployment template
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: orcad-template
|
||||
path: out/orcad-template
|
||||
|
||||
- name: Gate runtime file-watcher process isolation
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
@@ -1584,6 +1608,11 @@ jobs:
|
||||
Where-Object { $_.Extension -in '.exe', '.dll', '.node' } |
|
||||
ForEach-Object {
|
||||
$relative = [System.IO.Path]::GetRelativePath($root, $_.FullName)
|
||||
# The orcad template's Linux/macOS addons are data for SSH hosts, not PE files.
|
||||
if ($relative -match '^resources[\\/]orcad-template[\\/]targets[\\/](?!win32-)') {
|
||||
$skipped.Add("$relative <non-Windows orcad template payload>")
|
||||
return
|
||||
}
|
||||
$signature = Get-AuthenticodeSignature -FilePath $_.FullName
|
||||
if ($signature.Status -eq 'Valid') {
|
||||
$skipped.Add("$relative <already signed: $($signature.SignerCertificate.Subject)>")
|
||||
@@ -1764,6 +1793,13 @@ jobs:
|
||||
throw "Signed inner artifact did not round-trip cleanly ($($failures.Count) failures)."
|
||||
}
|
||||
|
||||
# Why: SignPath rewrote the template's Windows binaries, and the client materializer checks
|
||||
# each file against the template manifest, so it must record the signed bytes.
|
||||
- name: Reseal the orcad template over its signed binaries
|
||||
id: reseal-orcad-template
|
||||
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.restore-signed-inner.outcome == 'success'
|
||||
run: node config/scripts/packaged-orcad-template.cjs --reseal-signed dist/win-unpacked inner-signing-list.txt
|
||||
|
||||
# The uninstaller must return signed before rebuilding the installer.
|
||||
- name: Restore signed uninstaller for the installer rebuild
|
||||
id: restore-signed-uninstaller
|
||||
@@ -2257,6 +2293,7 @@ jobs:
|
||||
needs:
|
||||
- cut
|
||||
- create-release
|
||||
- orcad-template
|
||||
- release-preflight
|
||||
if: needs.cut.outputs.should_release == 'true'
|
||||
# Why: SignPath requires every job in this signing workflow to be
|
||||
|
||||
@@ -15,6 +15,8 @@ on:
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
# actions: read downloads the orcad template the parent release-cut run built.
|
||||
actions: read
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
@@ -137,6 +139,15 @@ jobs:
|
||||
ORCA_DIAGNOSTICS_TOKEN_URL: https://www.onorca.dev/diagnostics/token
|
||||
ORCA_POSTHOG_WRITE_KEY: ${{ secrets.ORCA_POSTHOG_WRITE_KEY }}
|
||||
|
||||
# Design D2: the parent release-cut run merged it from every node-server lane at this tag.
|
||||
- name: Download the orcad deployment template from the release run
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: orcad-template
|
||||
path: out/orcad-template
|
||||
run-id: ${{ inputs.release_run_id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Gate runtime file-watcher process isolation
|
||||
run: |
|
||||
# Why: #8212 is a native-process crash contract. Prove both the Node
|
||||
@@ -174,6 +185,7 @@ jobs:
|
||||
command: node config/scripts/ensure-native-runtime.mjs --runtime=electron && ORCA_MAC_RELEASE=1 pnpm exec electron-builder --config config/electron-builder.config.cjs --mac --publish always -c.publish.releaseType=draft
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ORCA_REQUIRE_ORCAD_TEMPLATE: '1'
|
||||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
|
||||
Reference in New Issue
Block a user