merge: land the reviewed Linux packaging stack onto main's split updater

This commit is contained in:
Neil
2026-09-02 02:20:22 -07:00
3145 changed files with 336799 additions and 220377 deletions
+27 -3
View File
@@ -401,27 +401,51 @@ jobs:
echo "::warning::Could not discard draft $TAG; remove it manually."
- name: Prune expired adhoc releases
# Only after a live publish: $TAG is then a non-draft this step must not
# delete, and a run that failed before publishing has nothing to retire.
if: steps.publish_live.outcome == 'success'
shell: bash
env:
GH_TOKEN: ${{ steps.app_token.outputs.token }}
# Protect the tag this run just shipped, so no filter mistake can delete
# a build minutes after the person who cut it was told it exists.
TAG: ${{ steps.release.outputs.tag }}
run: |
set -euo pipefail
# Why compute the cutoff in bash rather than with jq's `now`: this runs
# once per dispatch, and a fixed epoch makes the threshold visible in the
# log when someone asks where their build went.
cutoff=$(( $(date -u +%s) - ADHOC_RETAIN_DAYS * 86400 ))
echo "Pruning adhoc releases created before $(date -u -r "$cutoff" '+%Y-%m-%dT%H:%M:%SZ')"
echo "Pruning adhoc releases published before $(date -u -r "$cutoff" '+%Y-%m-%dT%H:%M:%SZ')"
# --cleanup-tag so pruning does not leave orphan tags with no release or
# assets attached. Drafts are excluded: a stale draft is the failure
# path's business, not the retention window's.
stale="$(gh release list --repo "$ADHOC_REPO" --limit 200 --json tagName,createdAt,isDraft \
--jq "map(select(.isDraft | not)) | map(select((.createdAt | fromdateiso8601) < $cutoff)) | .[].tagName")"
#
# Age comes from publishedAt, never createdAt. GitHub reports createdAt
# as the date of the *commit* a release's tag points at, and every tag
# here is cut from this repo's one seed commit — so all of them carry
# that same createdAt, and the day the window rolled past it the entire
# channel expired at once and a single run deleted it. A release with no
# publishedAt is kept rather than aged by guesswork.
jq_filter='map(select(.isDraft | not))'
if [[ -n "${TAG:-}" ]]; then
jq_filter+=" | map(select(.tagName != \"${TAG//\"/\\\"}\"))"
fi
jq_filter+=" | map(select((.publishedAt // \"\") != \"\"))"
jq_filter+=" | map(select((.publishedAt | fromdateiso8601) < $cutoff)) | .[].tagName"
stale="$(gh release list --repo "$ADHOC_REPO" --limit 200 --json tagName,publishedAt,isDraft \
--jq "$jq_filter")"
if [[ -z "$stale" ]]; then
echo "Nothing to prune."
exit 0
fi
while read -r tag; do
[[ -n "$tag" ]] || continue
# Belt-and-suspenders: the filter above should already exclude $TAG.
if [[ -n "${TAG:-}" && "$tag" == "$TAG" ]]; then
echo "::warning::Prune list still included just-published $tag after protect; skipping delete."
continue
fi
echo "Pruning $tag"
gh release delete "$tag" --repo "$ADHOC_REPO" --yes --cleanup-tag || \
echo "::warning::Could not prune $tag"
+17
View File
@@ -14,7 +14,24 @@ permissions:
contents: read
jobs:
# A cold cache would otherwise make all eight Node 26 shards compile the same native addons.
test_native_cache:
name: prepare test native cache node 26
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: node
node-version: '26'
test:
needs: [test_native_cache]
uses: ./.github/workflows/unit-tests.yml
with:
node_versions: '["26"]'
+3
View File
@@ -131,6 +131,9 @@ jobs:
- name: Enforce max-lines ratchet
run: pnpm run check:max-lines-ratchet
- name: Enforce ts-nocheck ratchet
run: pnpm run check:ts-nocheck-ratchet
- name: Enforce runtime Electron-import ratchet
run: pnpm run check:runtime-electron-ratchet
+71 -27
View File
@@ -922,9 +922,7 @@ jobs:
run: |
$env:SKIP_BUILD = '1'
$env:ORCA_E2E_FORWARD_APP_LOGS = '1'
pnpm run --if-present test:e2e:workspace-session-golden
pnpm run --if-present test:e2e:windows-fresh-startup-golden
pnpm run --if-present test:e2e:source-control-golden
- name: Upload Playwright traces
if: failure()
@@ -940,6 +938,9 @@ jobs:
if: needs.cut.outputs.should_release == 'true'
name: skill sharing release gate ${{ matrix.platform }}
runs-on: ${{ matrix.os }}
# The full suite is release-blocking on macOS. Windows still produces the
# same evidence, but intermittent filesystem contention cannot block signing.
continue-on-error: ${{ matrix.platform == 'windows' }}
timeout-minutes: 20
strategy:
fail-fast: false
@@ -1122,10 +1123,33 @@ jobs:
retention-days: 7
if-no-files-found: ignore
# 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.
release-preflight:
needs:
- cut
- terminal-rendering-golden
- skill-sharing-release-gate
- skill-sharing-linux-floor-release-gate
if: >-
always() &&
needs.cut.outputs.should_release == 'true' &&
needs.terminal-rendering-golden.result == 'success' &&
needs.skill-sharing-release-gate.result == 'success' &&
needs.skill-sharing-linux-floor-release-gate.result == 'success'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Confirm blocking release gates passed
run: echo "All blocking release gates passed; artifact builds may start."
build:
needs:
- cut
- create-release
- release-preflight
if: needs.cut.outputs.should_release == 'true'
strategy:
fail-fast: false
@@ -1170,12 +1194,22 @@ jobs:
with:
ref: refs/tags/${{ needs.cut.outputs.tag }}
# GitHub reruns also resume jobs skipped behind a failed gate. Never
# recreate Windows signing requests on a rerun; reuse the assets from the
# original attempt and require a fresh dispatch if they are missing.
- name: Skip Windows artifact rebuild on rerun
if: matrix.platform == 'win' && github.run_attempt != 1
shell: bash
run: |
echo "Windows artifact/signing steps are disabled on reruns (attempt $GITHUB_RUN_ATTEMPT)."
echo "Existing signed release assets must be reused; dispatch a fresh release only when a rebuild is required." >> "$GITHUB_STEP_SUMMARY"
# Why: `uses: ./…` resolves from the checked-out tag, not from the workflow
# ref, so cutting from an older/off-main ref whose tree predates a composite
# action would fail the step with "Can't find 'action.yml'". Restore the
# actions directory from the commit this workflow file itself came from.
- name: Restore composite actions from the workflow ref
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: bash
env:
WORKFLOW_SHA: ${{ github.workflow_sha }}
@@ -1321,6 +1355,7 @@ jobs:
# otherwise undecodable. The main bundle is platform-independent, so one
# leg publishes the maps for the whole release.
- name: Bundle main-process source maps
id: bundle-main-sourcemaps
if: matrix.platform == 'linux-x64'
shell: bash
env:
@@ -1328,9 +1363,17 @@ jobs:
run: |
set -euo pipefail
if [ -z "$(find out/main -name '*.js.map' -print -quit)" ]; then
echo "::error::No main-process source maps in out/main. Did build.sourcemap regress in electron.vite.config.ts?"
exit 1
# Older cut tags predate the hidden-source-map build setting. They
# are valid legacy releases, but have no map bundle to publish.
if grep -Eq "sourcemap:[[:space:]]*['\"]hidden['\"]" electron.vite.config.ts; then
echo "::error::No main-process source maps in out/main despite build.sourcemap='hidden'."
exit 1
fi
echo "has_maps=false" >>"$GITHUB_OUTPUT"
echo "::notice::Cut ref predates hidden main-process source maps; skipping map publication."
exit 0
fi
echo "has_maps=true" >>"$GITHUB_OUTPUT"
# Why: every entry in electron-builder's `files` is a negation, so
# app-builder prepends `**/*` and packs anything left in the workspace
# root into app.asar. Stage the bundle outside the checkout instead.
@@ -1338,7 +1381,7 @@ jobs:
ls -l "$RUNNER_TEMP/orca-sourcemaps-$TAG.zip"
- name: Publish main-process source maps
if: matrix.platform == 'linux-x64'
if: matrix.platform == 'linux-x64' && steps.bundle-main-sourcemaps.outputs.has_maps == 'true'
uses: nick-fields/retry@v4
with:
timeout_minutes: 10
@@ -1373,7 +1416,7 @@ jobs:
# Why: SignPath signs GitHub workflow artifacts, so Windows builds must
# upload only after the production-signed installer has been returned.
- name: Build Windows release artifacts
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
uses: nick-fields/retry@v4
with:
timeout_minutes: 30
@@ -1384,7 +1427,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Verify Windows node-pty ConPTY runtime
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
run: |
$runtimeDir = 'dist/win-unpacked/resources/node_modules/node-pty/build/Release'
@@ -1401,7 +1444,7 @@ jobs:
}
- name: Install SignPath PowerShell module
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
uses: ./.github/actions/install-signpath-module
# ── Windows inner-binary signing (issue #7785) ─────────────────────
@@ -1418,7 +1461,7 @@ jobs:
# valid signature (Microsoft's OpenConsole.exe) must keep their signer.
- name: Stage unsigned inner PE files for signing
id: stage-inner
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
continue-on-error: true
shell: pwsh
run: |
@@ -1457,7 +1500,7 @@ jobs:
- name: Upload unsigned inner binaries for SignPath
id: upload-unsigned-inner
if: matrix.platform == 'win' && steps.stage-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.stage-inner.outcome == 'success'
continue-on-error: true
uses: actions/upload-artifact@v7
with:
@@ -1467,7 +1510,7 @@ jobs:
- name: Submit inner binaries signing request
id: submit-inner-signing
if: matrix.platform == 'win' && steps.upload-unsigned-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.upload-unsigned-inner.outcome == 'success'
continue-on-error: true
uses: signpath/github-action-submit-signing-request@v2
with:
@@ -1481,7 +1524,7 @@ jobs:
- name: Notify Slack that inner-binary signing is waiting for approval
id: notify-inner-signing
if: matrix.platform == 'win' && steps.submit-inner-signing.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.submit-inner-signing.outcome == 'success'
continue-on-error: true
shell: pwsh
env:
@@ -1549,7 +1592,7 @@ jobs:
# falls through to today's unsigned-inner flow rather than blocking.
- name: Download signed inner binaries from SignPath
id: download-signed-inner
if: matrix.platform == 'win' && steps.submit-inner-signing.outcome == 'success' && steps.notify-inner-signing.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.submit-inner-signing.outcome == 'success' && steps.notify-inner-signing.outcome == 'success'
continue-on-error: true
shell: pwsh
env:
@@ -1572,7 +1615,7 @@ jobs:
# shipping a mix of signed and unsigned binaries.
- name: Restore signed inner binaries into unpacked app
id: restore-signed-inner
if: matrix.platform == 'win' && steps.download-signed-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.download-signed-inner.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
@@ -1613,7 +1656,7 @@ jobs:
# unsigned again, which the evidence gate will flag.
- name: Replace cached elevate.exe with the signed copy
id: sign-elevate-cache
if: matrix.platform == 'win' && steps.restore-signed-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.restore-signed-inner.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
@@ -1640,7 +1683,7 @@ jobs:
- name: Rebuild NSIS installer from signed unpacked app
id: rebuild-nsis-signed
if: matrix.platform == 'win' && steps.restore-signed-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.restore-signed-inner.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
@@ -1657,7 +1700,7 @@ jobs:
}
- name: Roll back to original installer after failed rebuild
if: matrix.platform == 'win' && steps.rebuild-nsis-signed.outcome == 'failure'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.rebuild-nsis-signed.outcome == 'failure'
shell: pwsh
run: |
if (Test-Path 'prepack-backup/orca-windows-setup.exe') {
@@ -1667,7 +1710,7 @@ jobs:
}
# ── End Windows inner-binary signing ───────────────────────────────
- name: Upload unsigned Windows installer for SignPath
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
id: upload-unsigned-windows-installer
uses: actions/upload-artifact@v7
with:
@@ -1679,7 +1722,7 @@ jobs:
# so the release job waits while the signing request is approved in UI.
- name: Submit Windows installer signing request
id: submit-signing-request
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
@@ -1691,7 +1734,7 @@ jobs:
wait-for-completion: false
- name: Notify Slack that Windows signing is waiting for approval
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
@@ -1755,7 +1798,7 @@ jobs:
Invoke-RestMethod -Method Post -Uri $env:SLACK_WEBHOOK_URL -ContentType 'application/json' -Body $payload
- name: Download signed Windows installer from SignPath
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
env:
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
@@ -1773,7 +1816,7 @@ jobs:
Expand-Archive -Path signed-windows.zip -DestinationPath signed-windows -Force
- name: Stage signed Windows release assets
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
run: |
$signedInstaller = Get-ChildItem -Path signed-windows -Recurse -File -Filter 'orca-windows-setup.exe' | Select-Object -First 1
@@ -1810,7 +1853,7 @@ jobs:
Get-Item 'dist/orca-windows-setup.exe', 'dist/orca-windows-setup.exe.blockmap', 'dist/latest.yml'
- name: Verify signed Windows installer
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
run: |
$signature = Get-AuthenticodeSignature -FilePath 'dist/orca-windows-setup.exe'
@@ -1828,7 +1871,7 @@ jobs:
# proven on a real release, then flip ORCA_WINDOWS_INNER_SIGNATURE_REQUIRED
# to 'true' so unsigned inner binaries block the release.
- name: Verify Windows inner binary signatures
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
env:
ORCA_WINDOWS_INNER_SIGNATURE_REQUIRED: 'false'
@@ -1960,7 +2003,7 @@ jobs:
if ($policyFailure) { throw $policyFailure }
- name: Upload Windows inner signing evidence
if: always() && matrix.platform == 'win'
if: always() && matrix.platform == 'win' && github.run_attempt == 1
uses: actions/upload-artifact@v7
with:
name: orca-windows-inner-signing-evidence-${{ needs.cut.outputs.tag }}
@@ -1971,7 +2014,7 @@ jobs:
retention-days: 30
- name: Publish signed Windows release artifacts
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
uses: nick-fields/retry@v4
with:
timeout_minutes: 10
@@ -2026,6 +2069,7 @@ jobs:
needs:
- cut
- create-release
- release-preflight
if: needs.cut.outputs.should_release == 'true'
# Why: SignPath requires every job in this signing workflow to be
# GitHub-hosted. The actual mac build runs in release-mac-build.yml so