mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
* 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.
284 lines
13 KiB
YAML
284 lines
13 KiB
YAML
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
|