Files
orca/.github/workflows/release-mac-build.yml
T
Neil b9e3912ff8 Call orca.dev for feedback, plugin lists and update notes; accept orca.dev share and download links (#41380)
* chore(endpoints): call orca.dev for feedback, plugin lists and update notes

The site moved to orca.dev. Feedback, connection diagnostics, plugin install
stats, the plugin kill list, update nudges and the changelog feed now use
orca.dev, as do the diagnostics token URL baked into builds and the release
download mirror (downloads.orca.dev). The old hosts serve the same paths,
so released builds keep working.

Skill share links on share.orca.dev are accepted, and the mobile scanner
recognizes the orca.dev Android download code. The old hosts stay accepted.

* chore(ci): leave the release JavaScript benchmark workflow unchanged

Editing it triggers the benchmark, whose parity check currently fails on
arm64 because max-quality brotli output for the mobile page differs by
architecture. The benchmark only needs some diagnostics URL, and the old
host still serves it.
2026-11-11 21:50:31 -08:00

284 lines
13 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Release macOS Build
run-name: Mac release build ${{ inputs.tag }} (${{ inputs.release_run_id }})
on:
workflow_dispatch:
inputs:
tag:
description: Release tag whose draft should receive macOS artifacts
required: true
type: string
release_run_id:
description: release-cut workflow run that requested this build
required: true
type: string
qualification_only:
description: Sign and notarize both architectures without publishing a release
required: false
default: false
type: boolean
javascript_source_sha:
description: Commit of the shared JavaScript artifact; empty keeps the legacy build
required: false
default: ''
type: string
permissions:
# actions: read downloads the orcad template the parent release-cut run built.
actions: read
contents: write
concurrency:
# Why: a qualification run that reuses a live tag must never queue behind or replace its release build.
group: release-mac-build-${{ inputs.tag }}${{ inputs.qualification_only && '-qualification' || '' }}
cancel-in-progress: false
jobs:
qualification-template:
if: inputs.qualification_only
permissions:
contents: read
actions: read
uses: ./.github/workflows/qualified-orcad-template.yml
with:
ref: ${{ github.sha }}
build-mac:
needs: qualification-template
if: ${{ !cancelled() && (!inputs.qualification_only || needs.qualification-template.result == 'success') && github.repository == 'stablyai/orca' }}
# Keep Blacksmith mac notarization outside SignPath’s GitHub-hosted Windows provenance.
runs-on: blacksmith-6vcpu-macos-15
timeout-minutes: 60
# actions: read fetches the release-cut run's artifacts.
permissions:
actions: read
contents: write
env:
ORCA_BACKGROUND_LAUNCH: '1'
NODE_OPTIONS: --max-old-space-size=4096
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ inputs.qualification_only && github.sha || format('refs/tags/{0}', inputs.tag) }}
# Why: a relay-era ref (before #27215) needs the Windows relay addon this build no longer compiles.
- name: Refuse a ref that still ships the SSH relay
shell: bash
run: |
if [[ -e config/tsconfig.relay.json ]]; then
echo "::error::This ref predates #27215 and still ships the SSH relay; build a commit at or after 2b4b60a87e."
exit 1
fi
- name: Restore draft-publish scripts from the workflow ref
env:
WORKFLOW_SHA: ${{ github.workflow_sha }}
run: |
set -euo pipefail
git fetch --no-tags --depth=1 origin "$WORKFLOW_SHA"
git checkout "$WORKFLOW_SHA" -- config/scripts/release/publication/assert-github-release-is-draft.mjs
- name: Setup pnpm
uses: pnpm/setup@v2
with:
install: false
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: package.json
check-latest: true
cache: pnpm
cache-dependency-path: |
pnpm-lock.yaml
mobile/pnpm-lock.yaml
# Cache bundled host Node by exact pin, without restore keys.
- name: Locate the pinned Node runtime cache
shell: bash
run: echo "ORCA_NODE_RUNTIME_CACHE_DIR=$HOME/Library/Caches/orca-node-runtime" >>"$GITHUB_ENV"
- name: Cache the pinned Node runtime
uses: actions/cache@v5
with:
path: ~/Library/Caches/orca-node-runtime
key: orca-node-runtime-mac-${{ hashFiles('src/shared/node-runtime-pin.ts') }}
# Cache Electron/builder tool downloads to save ~30–90s.
- 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-
# Retry transient Electron CDN 504s; install both x64/arm64 optional natives for dual macOS packages.
- name: Install dependencies
uses: nick-fields/retry@v4
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 30
command: pnpm install --frozen-lockfile --cpu=current,x64,arm64
# Build required out/mobile-web using mobile’s React Native/Expo install.
- uses: ./.github/actions/install-mobile-dependencies
- name: Verify macOS signing environment
run: node config/scripts/desktop-distribution/macos/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 }}
# Check duplicate entitlements before build: plutil lint accepts duplicates but codesign rejects them.
- name: Verify macOS entitlements
run: pnpm verify:macos-entitlements
# Set literal stable/rc telemetry identity through Vite define.
- name: Classify release tag for telemetry build identity
id: tag-classify
shell: bash
env:
TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
# Suffixed vX.Y.Z-rc.N.perf builds keep the same rc-channel telemetry identity.
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+(\.[0-9A-Za-z]+)?$ ]]; 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: Download release JavaScript from the release run
if: inputs.javascript_source_sha != ''
uses: actions/download-artifact@v8
with:
name: release-javascript
path: .build/release-javascript
run-id: ${{ inputs.release_run_id }}
github-token: ${{ github.token }}
- name: Build app
run: |
if [ -n "$ORCA_RELEASE_JAVASCRIPT_SOURCE_SHA" ]; then
RELEASE_JAVASCRIPT_ARTIFACT='config/scripts/release/javascript-artifacts/release-javascript-artifact.mjs'
if [ ! -f "$RELEASE_JAVASCRIPT_ARTIFACT" ]; then
RELEASE_JAVASCRIPT_ARTIFACT='config/scripts/release-javascript-artifact.mjs'
fi
node "$RELEASE_JAVASCRIPT_ARTIFACT" restore
pnpm run build:release:host
else
pnpm run build:release
fi
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_RELEASE_JAVASCRIPT_SOURCE_SHA: ${{ inputs.javascript_source_sha }}
ORCA_DIAGNOSTICS_TOKEN_URL: https://orca.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
# Why: release-cut skips the template for a tag that predates it.
if: hashFiles('config/scripts/orcad-distribution/template/packaged-orcad-template.cjs', 'config/scripts/packaged-orcad-template.cjs') != ''
uses: actions/download-artifact@v8
with:
name: orcad-template
path: out/orcad-template
run-id: ${{ inputs.qualification_only && github.run_id || inputs.release_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Gate runtime file-watcher process isolation
run: |
# #8212: require Node and the exact Electron runtime to survive native SIGSEGV before packaging.
node config/scripts/diagnostics/runtime-watchers/runtime-file-watcher-fault-harness.mjs
ELECTRON_RUN_AS_NODE=1 pnpm exec electron config/scripts/diagnostics/runtime-watchers/runtime-file-watcher-fault-harness.mjs
- name: Abort if the parent release-cut run was cancelled
if: ${{ !inputs.qualification_only }}
# Watch parent cancellation: separately dispatched mac upload otherwise continues (v1.4.206 went public with partial assets).
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PARENT_RUN: ${{ inputs.release_run_id }}
run: |
set -euo pipefail
conclusion="$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$PARENT_RUN" --jq '.conclusion // empty')"
if [[ "$conclusion" == "cancelled" || "$conclusion" == "failure" || "$conclusion" == "timed_out" ]]; then
echo "::error::Parent release-cut run $PARENT_RUN is $conclusion; refusing to publish mac artifacts."
exit 1
fi
- name: Publish release artifacts (macOS)
uses: nick-fields/retry@v4
with:
timeout_minutes: 45
max_attempts: 3
retry_wait_seconds: 30
command: node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON "$(if [ -f config/scripts/orcad-distribution/prebuilds/pinned-node-downloads.mjs ]; then printf %s config/scripts/orcad-distribution/prebuilds/pinned-node-downloads.mjs; else printf %s config/scripts/pinned-node-downloads.mjs; fi)" darwin-arm64 darwin-x64 && node "$(if [ -f config/scripts/native-build/node-addons/ensure-native-runtime.mjs ]; then printf %s config/scripts/native-build/node-addons/ensure-native-runtime.mjs; else printf %s config/scripts/ensure-native-runtime.mjs; fi)" --runtime=electron && if { [ -f config/scripts/desktop-distribution/macos/build-mac-notarization-overlap.mjs ] || [ -f config/scripts/build-mac-notarization-overlap.mjs ]; }; then ORCA_MAC_RELEASE=1 node "$(if [ -f config/scripts/desktop-distribution/macos/build-mac-notarization-overlap.mjs ]; then printf %s config/scripts/desktop-distribution/macos/build-mac-notarization-overlap.mjs; else printf %s config/scripts/build-mac-notarization-overlap.mjs; fi)" --publish "$ORCA_MAC_PUBLISH" --draft; else ORCA_MAC_RELEASE=1 pnpm exec electron-builder --config config/electron-builder.config.cjs --mac --publish "$ORCA_MAC_PUBLISH" -c.publish.releaseType=draft; fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ORCA_REQUIRE_ORCAD_TEMPLATE: '1'
ORCA_MAC_PUBLISH: ${{ inputs.qualification_only && 'never' || 'always' }}
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 }}
# Builder cannot write this field, so older macOS updaters would install a build that won't launch.
- name: Gate the update manifest on the minimum macOS
# Tags cut before the gate keep their manifest unchanged.
if: hashFiles('config/scripts/desktop-distribution/macos/gate-mac-update-manifests.mjs') != ''
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ inputs.tag }}
PUBLISH: ${{ !inputs.qualification_only }}
run: |
set -euo pipefail
node config/scripts/desktop-distribution/macos/gate-mac-update-manifests.mjs dist
if [ "$PUBLISH" = true ]; then
gh release upload "$TAG" dist/*-mac.yml --repo "$GITHUB_REPOSITORY" --clobber
fi
- name: Verify release remains draft after artifact upload
if: ${{ !inputs.qualification_only }}
# Re-draft premature builder publication before failing; latest must not keep an incomplete Windows release.
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ inputs.tag }}
run: node config/scripts/release/publication/assert-github-release-is-draft.mjs "${{ inputs.tag }}"
# Check after publish because builder’s single pack/upload invocation has no cheap interposition point.
- name: Verify telemetry constants present in app.asar
run: node config/scripts/desktop-build/telemetry-contracts/verify-telemetry-constants.mjs
- name: Upload qualification artifacts
if: inputs.qualification_only
uses: actions/upload-artifact@v7
with:
name: mac-notarization-qualification
path: |
dist/*.dmg
dist/*.zip
dist/*.blockmap
dist/*.yml
if-no-files-found: error
retention-days: 7