name: Desktop Release Build Smoke on: workflow_dispatch: inputs: repository: description: Repository to checkout, for example openchamber/openchamber or daveotero/openchamber required: false default: openchamber/openchamber type: string ref: description: Git ref to build (branch, tag, or sha) required: true default: feat/windows-desktop-app type: string build_macos: description: Build signed/notarized macOS Electron artifacts required: false default: true type: boolean build_windows: description: Build Windows Electron installer artifacts required: false default: true type: boolean retention_days: description: Artifact retention days required: false default: "7" type: choice options: - "1" - "3" - "7" - "14" permissions: contents: read jobs: build-macos-electron: if: ${{ inputs.build_macos }} name: Build macOS Electron (${{ matrix.arch }}) runs-on: macos-26 strategy: fail-fast: false matrix: include: - target: aarch64-apple-darwin arch: arm64 platform: darwin-aarch64 - target: x86_64-apple-darwin arch: x64 platform: darwin-x86_64 steps: - name: Checkout selected ref uses: actions/checkout@v6 with: repository: ${{ inputs.repository || github.repository }} ref: ${{ inputs.ref || github.ref }} - name: Setup bun uses: oven-sh/setup-bun@v2 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '20' - name: Install dependencies run: bun install --frozen-lockfile - name: Install Apple Certificate env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} run: | KEYCHAIN_PATH=$RUNNER_TEMP/electron-signing.keychain-db KEYCHAIN_PASSWORD=$(openssl rand -base64 32) security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" echo "$APPLE_CERTIFICATE" | base64 --decode > $RUNNER_TEMP/certificate.p12 security import $RUNNER_TEMP/certificate.p12 \ -P "$APPLE_CERTIFICATE_PASSWORD" \ -A -t cert -f pkcs12 \ -k "$KEYCHAIN_PATH" security list-keychain -d user -s "$KEYCHAIN_PATH" security set-key-partition-list -S apple-tool:,apple:,codesign: \ -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" - name: Build Electron app working-directory: packages/electron env: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} ELECTRON_BUILDER_ARCH: ${{ matrix.arch }} run: | bun run build:web-assets bun run bundle:main # npmRebuild=false in package.json, so electron-builder won't # recompile native deps on its own. Rebuild against the target # Electron ABI before packaging, matching the release workflow. bun run rebuild:native bunx electron-builder --mac --${{ matrix.arch }} --publish=never - name: Verify signature + entitlements + notarization run: | set -euo pipefail APP_DIR="packages/electron/dist/mac" [ -d "packages/electron/dist/mac-arm64" ] && APP_DIR="packages/electron/dist/mac-arm64" APP_PATH=$(find "$APP_DIR" -maxdepth 2 -name "*.app" -print -quit) if [ -z "$APP_PATH" ]; then echo "Error: .app not found under packages/electron/dist/mac*" ls -la packages/electron/dist/ exit 1 fi echo "Verifying $APP_PATH" codesign -vv --deep --strict "$APP_PATH" CS_INFO=$(codesign -dv --verbose=4 "$APP_PATH" 2>&1) echo "$CS_INFO" if ! echo "$CS_INFO" | grep -q "flags=.*runtime"; then echo "Error: hardened runtime flag missing" exit 1 fi xcrun stapler validate "$APP_PATH" ENTITLEMENTS=$(codesign -d --entitlements :- "$APP_PATH" 2>&1 || true) if echo "$ENTITLEMENTS" | grep -q "com.apple.security.app-sandbox"; then echo "Error: app sandbox entitlement is present" exit 1 fi for key in \ com.apple.security.cs.allow-jit \ com.apple.security.cs.allow-unsigned-executable-memory \ com.apple.security.cs.disable-library-validation do if ! echo "$ENTITLEMENTS" | grep -q "$key"; then echo "Error: required entitlement missing: $key" exit 1 fi done - name: Upload macOS installable artifacts uses: actions/upload-artifact@v7 with: name: desktop-release-smoke-macos-${{ matrix.arch }} path: | packages/electron/dist/*.dmg packages/electron/dist/*.zip packages/electron/dist/*.blockmap packages/electron/dist/latest-mac.yml if-no-files-found: error retention-days: ${{ fromJSON(inputs.retention_days) }} build-windows-electron: if: ${{ inputs.build_windows }} name: Build Windows Electron (x64) runs-on: windows-latest strategy: fail-fast: false matrix: include: - arch: x64 target: x86_64-pc-windows-msvc platform: win32-x64 steps: - name: Checkout selected ref uses: actions/checkout@v6 with: repository: ${{ inputs.repository || github.repository }} ref: ${{ inputs.ref || github.ref }} - name: Setup bun uses: oven-sh/setup-bun@v2 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '20' - name: Install dependencies run: bun install --frozen-lockfile - name: Build web assets working-directory: packages/electron run: bun run build:web-assets - name: Bundle main process working-directory: packages/electron run: bun run bundle:main - name: Rebuild native modules working-directory: packages/electron shell: bash # npmRebuild=false in package.json, so electron-builder won't # recompile native deps on its own. Rebuild against the target # Electron ABI before packaging, matching the release workflow. run: node ./scripts/rebuild-native.mjs - name: Build Windows app working-directory: packages/electron shell: bash run: node ./scripts/package.mjs --win --${{ matrix.arch }} --publish=never - name: Upload Windows installable artifacts uses: actions/upload-artifact@v7 with: name: desktop-release-smoke-windows-${{ matrix.arch }} path: | packages/electron/dist/*.exe packages/electron/dist/*.blockmap packages/electron/dist/latest.yml if-no-files-found: error retention-days: ${{ fromJSON(inputs.retention_days) }}