ci: isolate mac release build from SignPath matrix (#6937)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
e8f29945b7
commit
16de859fdf
|
|
@ -703,21 +703,6 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Why: run the signed/notarized desktop DMG build on a Blacksmith
|
||||
# macOS runner (~2x faster than GitHub macos-15: ~8min vs ~15-18min).
|
||||
# The 6 vCPU tier is chosen over 12 vCPU because this job is
|
||||
# dominated by notarization (an Apple-server round-trip, not CPU),
|
||||
# so 12 vCPU was measured only ~30s faster while costing 2x/min.
|
||||
# OS stays pinned to macos-15 to match the prior toolchain exactly;
|
||||
# only the host changes. Blacksmith reports a genuine macOS host, so
|
||||
# `runner.os == 'macOS'` and `matrix.platform == 'mac'` gates below
|
||||
# still resolve correctly. Other jobs stay GitHub-hosted.
|
||||
- os: blacksmith-6vcpu-macos-15
|
||||
platform: mac
|
||||
release_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
|
||||
eb_cache_path: |
|
||||
~/Library/Caches/electron
|
||||
~/Library/Caches/electron-builder
|
||||
# Why: windows-latest moved to the Windows 2025 / VS 2026 image before
|
||||
# node-gyp could detect VS 18, breaking native dependency install.
|
||||
- os: windows-2022
|
||||
|
|
@ -778,8 +763,8 @@ jobs:
|
|||
npm install -g node-gyp@11.5.0
|
||||
echo "npm_config_node_gyp=$(npm root -g)/node-gyp/bin/node-gyp.js" >> "$GITHUB_ENV"
|
||||
|
||||
# Cache the Electron binary + electron-builder tool downloads (notarytool,
|
||||
# winCodeSign, nsis, squirrel, AppImage). Saves ~30-90s per job, incl. mac.
|
||||
# Cache the Electron binary + electron-builder tool downloads
|
||||
# (winCodeSign, nsis, squirrel, AppImage). Saves ~30-90s per job.
|
||||
- name: Cache electron-builder downloads
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
|
|
@ -809,22 +794,6 @@ jobs:
|
|||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get update && sudo apt-get install -y python3-gi gir1.2-atspi-2.0 at-spi2-core xclip xdotool rpm
|
||||
|
||||
- name: Verify macOS signing environment
|
||||
if: matrix.platform == 'mac'
|
||||
run: node config/scripts/verify-macos-release-env.mjs
|
||||
env:
|
||||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
# Why: `plutil -lint` accepts duplicate plist keys, but `codesign`
|
||||
# rejects duplicate entitlements after the expensive app build.
|
||||
- name: Verify macOS entitlements
|
||||
if: matrix.platform == 'mac'
|
||||
run: pnpm verify:macos-entitlements
|
||||
|
||||
# Why: telemetry's transport gate (`src/main/telemetry/client.ts:IS_OFFICIAL_BUILD`)
|
||||
# requires the build identity to be the literal string `stable` or `rc`,
|
||||
# substituted by electron-vite's `define` block at build time. Derive
|
||||
|
|
@ -875,35 +844,6 @@ jobs:
|
|||
ORCA_DIAGNOSTICS_TOKEN_URL: https://www.onorca.dev/diagnostics/token
|
||||
ORCA_POSTHOG_WRITE_KEY: ${{ secrets.ORCA_POSTHOG_WRITE_KEY }}
|
||||
|
||||
# Why: macOS signing secrets (CSC_LINK, CSC_KEY_PASSWORD) must NOT be
|
||||
# passed to non-macOS builds. electron-builder uses CSC_LINK as the
|
||||
# code-signing certificate on any platform, so leaking the Apple
|
||||
# Developer ID cert to the Windows build causes the NSIS installer to
|
||||
# be signed with an Apple cert whose chain Windows cannot validate,
|
||||
# breaking the auto-updater with "certificate chain could not be built
|
||||
# to a trusted root authority" (issue #631).
|
||||
#
|
||||
# Why retry: electron-builder downloads NSIS/winCodeSign/squirrel
|
||||
# binaries and the Electron runtime from GitHub release assets during
|
||||
# publish. GitHub's download CDN occasionally returns 504s that fail
|
||||
# the whole release. Retry on failure so transient network errors
|
||||
# don't require a manual re-run.
|
||||
- name: Publish release artifacts (macOS)
|
||||
if: matrix.platform == 'mac'
|
||||
uses: nick-fields/retry@v4
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
command: ${{ matrix.release_command }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
- name: Publish release artifacts (Linux)
|
||||
if: matrix.platform == 'linux-x64' || matrix.platform == 'linux-arm64'
|
||||
uses: nick-fields/retry@v4
|
||||
|
|
@ -1140,14 +1080,13 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Why post-publish for macOS/Linux: electron-builder packs and uploads
|
||||
# in a single `--publish always` invocation, so there is no cheap
|
||||
# insertion point between pack and upload without splitting those steps.
|
||||
# Running verify last still blocks the bad release: the
|
||||
# binary is uploaded to the draft, but a failed matrix job blocks
|
||||
# the `publish-release` job (which depends on `build`) from flipping
|
||||
# the release from draft → published, so users never see it. A human
|
||||
# then deletes the draft and re-cuts.
|
||||
# Why post-publish for Linux: electron-builder packs and uploads in a
|
||||
# single `--publish always` invocation, so there is no cheap insertion
|
||||
# point between pack and upload without splitting those steps. Running
|
||||
# verify last still blocks the bad release: the binary is uploaded to the
|
||||
# draft, but a failed matrix job blocks `publish-release` from flipping
|
||||
# the release from draft → published, so users never see it. A human then
|
||||
# deletes the draft and re-cuts.
|
||||
#
|
||||
# Why this guards against: a misconfigured CI run where
|
||||
# `ORCA_POSTHOG_WRITE_KEY` is unset or the tag fails to classify
|
||||
|
|
@ -1158,10 +1097,153 @@ jobs:
|
|||
- name: Verify telemetry constants present in app.asar
|
||||
run: node config/scripts/verify-telemetry-constants.mjs
|
||||
|
||||
build-mac:
|
||||
needs:
|
||||
- cut
|
||||
- create-release
|
||||
if: needs.cut.outputs.should_release == 'true'
|
||||
# Why: SignPath validates all jobs that can influence its artifact. Keep
|
||||
# the non-GitHub-hosted Blacksmith macOS build out of the Windows matrix.
|
||||
runs-on: blacksmith-6vcpu-macos-15
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: refs/tags/${{ needs.cut.outputs.tag }}
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v6
|
||||
with:
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: package.json
|
||||
cache: pnpm
|
||||
|
||||
# Cache the Electron binary + electron-builder tool downloads (notarytool,
|
||||
# winCodeSign, nsis, squirrel, AppImage). Saves ~30-90s per job, incl. mac.
|
||||
- name: Cache electron-builder downloads
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
~/Library/Caches/electron
|
||||
~/Library/Caches/electron-builder
|
||||
key: electron-builder-mac-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
electron-builder-mac-
|
||||
|
||||
# Why: pnpm install triggers electron's postinstall, which downloads the
|
||||
# Electron binary from GitHub release assets. GitHub's download CDN
|
||||
# occasionally returns 504s that fail the whole release. Retry on
|
||||
# failure so transient network errors don't require a manual re-run.
|
||||
- name: Install dependencies
|
||||
uses: nick-fields/retry@v4
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
command: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Verify macOS signing environment
|
||||
run: node config/scripts/verify-macos-release-env.mjs
|
||||
env:
|
||||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
# Why: `plutil -lint` accepts duplicate plist keys, but `codesign`
|
||||
# rejects duplicate entitlements after the expensive app build.
|
||||
- name: Verify macOS entitlements
|
||||
run: pnpm verify:macos-entitlements
|
||||
|
||||
# Why: telemetry's transport gate (`src/main/telemetry/client.ts:IS_OFFICIAL_BUILD`)
|
||||
# requires the build identity to be the literal string `stable` or `rc`,
|
||||
# substituted by electron-vite's `define` block at build time.
|
||||
- name: Classify release tag for telemetry build identity
|
||||
id: tag-classify
|
||||
shell: bash
|
||||
env:
|
||||
TAG: ${{ needs.cut.outputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then
|
||||
identity=rc
|
||||
elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
identity=stable
|
||||
else
|
||||
echo "::error::Tag $TAG does not match stable or rc pattern; refusing to build official artifact"
|
||||
exit 1
|
||||
fi
|
||||
echo "identity=$identity" >>"$GITHUB_OUTPUT"
|
||||
echo "Classified $TAG as $identity"
|
||||
|
||||
- name: Build app
|
||||
run: pnpm build:release
|
||||
env:
|
||||
# Why: Vite's web build crossed Node's default old-space ceiling on
|
||||
# the macOS release runner, leaving v1.4.2-rc.8 as an incomplete draft.
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
ORCA_BUILD_IDENTITY: ${{ steps.tag-classify.outputs.identity }}
|
||||
ORCA_DIAGNOSTICS_TOKEN_URL: https://www.onorca.dev/diagnostics/token
|
||||
ORCA_POSTHOG_WRITE_KEY: ${{ secrets.ORCA_POSTHOG_WRITE_KEY }}
|
||||
|
||||
- name: Publish release artifacts (macOS)
|
||||
uses: nick-fields/retry@v4
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
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
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
- name: Verify release remains draft after artifact upload
|
||||
# Why: the macOS build must never be the actor that exposes a partial
|
||||
# release. If an uploader or GitHub transition flips draft early, fail
|
||||
# this job and block publish-release.
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ needs.cut.outputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
releases_json="$(gh api "repos/$GITHUB_REPOSITORY/releases?per_page=100")"
|
||||
# Why: release upload must validate the draft before it is publicly visible.
|
||||
draft="$(jq -e -r --arg tag "$TAG" '
|
||||
map(select(.tag_name == $tag))
|
||||
| if length == 1 and (.[0].draft | type) == "boolean" then (.[0].draft | tostring) else empty end
|
||||
' <<<"$releases_json")" || {
|
||||
echo "::error::Release $TAG was not found in the draft-aware releases list, or its draft state was missing."
|
||||
exit 1
|
||||
}
|
||||
if [[ "$draft" != "true" ]]; then
|
||||
echo "::error::Release $TAG was published during the mac artifact upload."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Why post-publish for macOS: electron-builder packs and uploads in a
|
||||
# single `--publish always` invocation, so there is no cheap insertion
|
||||
# point between pack and upload without splitting those steps.
|
||||
- name: Verify telemetry constants present in app.asar
|
||||
run: node config/scripts/verify-telemetry-constants.mjs
|
||||
|
||||
publish-release:
|
||||
needs:
|
||||
- cut
|
||||
- build
|
||||
- build-mac
|
||||
- terminal-rendering-golden
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
|
|
|
|||
|
|
@ -66,16 +66,19 @@ describe('Electron runtime package contract', () => {
|
|||
release_command
|
||||
])
|
||||
)
|
||||
const macReleaseCommand = parsedWorkflow.jobs['build-mac'].steps.find(
|
||||
(step) => step.name === 'Publish release artifacts (macOS)'
|
||||
).with.command
|
||||
|
||||
expect([...releaseCommands.keys()].sort()).toEqual(['linux-arm64', 'linux-x64', 'mac', 'win'])
|
||||
for (const command of releaseCommands.values()) {
|
||||
expect([...releaseCommands.keys()].sort()).toEqual(['linux-arm64', 'linux-x64', 'win'])
|
||||
for (const command of [...releaseCommands.values(), macReleaseCommand]) {
|
||||
expect(command).toContain('node config/scripts/ensure-native-runtime.mjs --runtime=electron')
|
||||
expect(command).toContain('electron-builder')
|
||||
expect(command.indexOf('ensure-native-runtime')).toBeLessThan(
|
||||
command.indexOf('electron-builder')
|
||||
)
|
||||
}
|
||||
expect(releaseCommands.get('mac')).toContain(' && ORCA_MAC_RELEASE=1 ')
|
||||
expect(macReleaseCommand).toContain(' && ORCA_MAC_RELEASE=1 ')
|
||||
expect(releaseCommands.get('linux-x64')).toContain(' && pnpm exec electron-builder ')
|
||||
expect(releaseCommands.get('linux-x64')).toContain('--linux AppImage deb rpm --x64')
|
||||
expect(releaseCommands.get('linux-arm64')).toContain('ORCA_LINUX_ARM64_RELEASE=1')
|
||||
|
|
@ -96,6 +99,20 @@ describe('Electron runtime package contract', () => {
|
|||
expect(windowsReleaseEntry.os).toBe('windows-2022')
|
||||
})
|
||||
|
||||
it('keeps the Blacksmith macOS release build out of the SignPath Windows matrix', () => {
|
||||
const releaseWorkflow = parse(
|
||||
readFileSync(join(projectDir, '.github/workflows/release-cut.yml'), 'utf8')
|
||||
)
|
||||
const buildMatrixRunners = releaseWorkflow.jobs.build.strategy.matrix.include.map(
|
||||
({ os }) => os
|
||||
)
|
||||
|
||||
expect(buildMatrixRunners).not.toContain('blacksmith-6vcpu-macos-15')
|
||||
expect(releaseWorkflow.jobs['build-mac']['runs-on']).toBe('blacksmith-6vcpu-macos-15')
|
||||
expect(releaseWorkflow.jobs['publish-release'].needs).toContain('build')
|
||||
expect(releaseWorkflow.jobs['publish-release'].needs).toContain('build-mac')
|
||||
})
|
||||
|
||||
it('preflights SignPath module install before Windows signing side effects', () => {
|
||||
const releaseWorkflow = readFileSync(
|
||||
join(projectDir, '.github/workflows/release-cut.yml'),
|
||||
|
|
|
|||
Loading…
Reference in New Issue