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/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/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 node config/scripts/release-javascript-artifact.mjs 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://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 # Why: release-cut skips the template for a tag that predates it. if: hashFiles('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/runtime-file-watcher-fault-harness.mjs ELECTRON_RUN_AS_NODE=1 pnpm exec electron config/scripts/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 config/scripts/pinned-node-downloads.mjs darwin-arm64 darwin-x64 && node config/scripts/ensure-native-runtime.mjs --runtime=electron && if [ -f config/scripts/build-mac-notarization-overlap.mjs ]; then ORCA_MAC_RELEASE=1 node config/scripts/build-mac-notarization-overlap.mjs --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 }} - 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/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/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