name: Alpha Channel (macOS arm64) # Every app-related push to `dev` publishes a fresh macOS arm64 build to the # single rolling `alpha-macos-latest` release. Each run appends # uniquely-versioned dmg, zip, and blockmap assets, then re-uploads # `latest-mac.yml` last so the manifest never references missing assets. # # Assets older than the retention window are pruned. Per-run GitHub releases # are no longer created. # # See .github/workflows/release-macos-aarch64.yml for the stable channel. on: push: branches: - dev paths: - apps/app/** - apps/desktop/** - apps/server/** - packages/docs/** - packages/enterprise-mcp-client/** - packages/handsfree/** - packages/install-config/** - packages/paths/** - packages/types/** - packages/ui/** - patches/** - scripts/release/** - constants.json - package.json - pnpm-lock.yaml - pnpm-workspace.yaml - .github/workflows/alpha-macos-aarch64.yml workflow_dispatch: inputs: publish: description: "Publish the packaged build to the alpha channel release (leave off for build-only validation)" required: false type: boolean default: false channel_tag: description: "Channel release tag to publish to (override for staging validation)" required: false type: string default: "alpha-macos-latest" permissions: contents: write concurrency: group: alpha-macos-aarch64-${{ github.ref }} cancel-in-progress: true jobs: publish-alpha-macos-aarch64: name: Build + Publish alpha (aarch64-apple-darwin) runs-on: macos-14 timeout-minutes: 180 env: ALPHA_RELEASE_NAME: OpenWork Alpha (macOS arm64) # Apple signing + notarization are required so alpha bundles install # and launch without Gatekeeper friction. Alpha builds are served # from GitHub Releases like stable, just from a different tag. MACOS_NOTARIZE: ${{ vars.MACOS_NOTARIZE || 'true' }} OPENWORK_DESKTOP_SENTRY_DSN: ${{ secrets.OPENWORK_DESKTOP_SENTRY_DSN || vars.OPENWORK_DESKTOP_SENTRY_DSN }} OPENWORK_DESKTOP_SENTRY_TRACES_SAMPLE_RATE: ${{ vars.OPENWORK_DESKTOP_SENTRY_TRACES_SAMPLE_RATE || '0.01' }} steps: - name: Checkout uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: ref: ${{ github.sha }} # Alpha versions are computed from the latest stable v* tag # (package.json holds a permanent 0.0.0-dev placeholder). fetch-depth: 0 - name: Setup Node uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: 24 - name: Setup pnpm uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 with: version: 11.4.0 - name: Setup Bun uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # v1 with: bun-version: "1.3.6" - name: Get pnpm store path id: pnpm-store shell: bash run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT" - name: Cache pnpm store uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5 continue-on-error: true with: path: ${{ steps.pnpm-store.outputs.path }} key: macos-pnpm-${{ hashFiles('pnpm-lock.yaml') }} restore-keys: | macos-pnpm- - name: Install dependencies run: pnpm install --frozen-lockfile --prefer-offline - name: Resolve alpha version id: alpha-version shell: bash env: GITHUB_RUN_NUMBER: ${{ github.run_number }} GITHUB_SHA: ${{ github.sha }} run: | set -euo pipefail # Alpha builds advertise the *next* patch after the latest stable # tag so semver comparison makes the alpha newer than the current # stable (e.g. stable 0.11.207 < alpha 0.11.208-alpha.). Once # stable 0.11.208 ships, its semver beats the alpha prerelease tag # and alpha users cleanly migrate forward. Tags are the version # source of truth — package.json is a permanent placeholder. base="$(node scripts/release/versions.mjs next --bump patch)" run="${GITHUB_RUN_NUMBER:-0}" sha="$(printf '%.7s' "${GITHUB_SHA:-local}")" echo "alpha_version=${base}-alpha.${run}+${sha}" >> "$GITHUB_OUTPUT" echo "base_version=${base}" >> "$GITHUB_OUTPUT" - name: Write alpha Electron package version shell: bash env: ALPHA_VERSION: ${{ steps.alpha-version.outputs.alpha_version }} run: node scripts/release/stamp-version.mjs --version "$ALPHA_VERSION" - name: Write notary API key if: env.MACOS_NOTARIZE == 'true' env: APPLE_NOTARY_API_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARY_API_KEY_P8_BASE64 }} run: | set -euo pipefail NOTARY_KEY_PATH="$RUNNER_TEMP/AuthKey.p8" printf '%s' "$APPLE_NOTARY_API_KEY_P8_BASE64" | base64 --decode > "$NOTARY_KEY_PATH" chmod 600 "$NOTARY_KEY_PATH" echo "NOTARY_KEY_PATH=$NOTARY_KEY_PATH" >> "$GITHUB_ENV" - name: Reject unsigned Electron alpha release if: env.MACOS_NOTARIZE != 'true' shell: bash run: | echo "Electron alpha artifacts must be signed and notarized. Set MACOS_NOTARIZE=true and provide Apple signing secrets." >&2 exit 1 - name: Build Electron alpha app if: env.MACOS_NOTARIZE == 'true' shell: bash env: TARGET: aarch64-apple-darwin run: pnpm --filter @openwork/desktop build:electron - name: Package Electron alpha (macOS, signed + notarized) if: env.MACOS_NOTARIZE == 'true' env: CSC_LINK: ${{ secrets.APPLE_CODESIGN_CERT_P12_BASE64 }} CSC_KEY_PASSWORD: ${{ secrets.APPLE_CODESIGN_CERT_PASSWORD }} MACOS_NOTARIZE: true APPLE_API_KEY: ${{ secrets.APPLE_NOTARY_API_KEY_ID }} APPLE_API_ISSUER: ${{ secrets.APPLE_NOTARY_API_ISSUER_ID }} APPLE_API_KEY_PATH: ${{ env.NOTARY_KEY_PATH }} run: | set -euo pipefail pnpm --dir apps/desktop exec electron-builder \ --config electron-builder.yml \ --mac \ --arm64 \ --publish never - name: Publish alpha build to rolling release if: env.MACOS_NOTARIZE == 'true' && (github.event_name == 'push' || inputs.publish == true) shell: bash env: GH_TOKEN: ${{ github.token }} CHANNEL_TAG: ${{ inputs.channel_tag || 'alpha-macos-latest' }} run: | set -euo pipefail # Alpha assets may only ever land on alpha channel releases. This # guard makes it impossible to clobber a stable release (and its # latest-mac.yml) via a mistyped channel_tag dispatch input. case "$CHANNEL_TAG" in alpha-macos-*) ;; *) echo "Refusing to publish alpha assets to non-alpha tag: $CHANNEL_TAG" >&2 exit 1 ;; esac if ! gh release view "$CHANNEL_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then gh release create "$CHANNEL_TAG" \ --repo "$GITHUB_REPOSITORY" \ --title "$ALPHA_RELEASE_NAME" \ --notes "Rolling alpha channel for OpenWork macOS arm64 builds. Each publish adds versioned updater assets and advances the channel manifest." \ --target "$GITHUB_SHA" \ --prerelease fi shopt -s nullglob assets=( apps/desktop/dist-electron/*.dmg apps/desktop/dist-electron/*.zip apps/desktop/dist-electron/*.blockmap ) if [ ${#assets[@]} -eq 0 ]; then echo "No Electron alpha assets found in apps/desktop/dist-electron" >&2 exit 1 fi gh release upload "$CHANNEL_TAG" "${assets[@]}" \ --repo "$GITHUB_REPOSITORY" \ --clobber # Publish the manifest last so an interrupted upload never points at missing assets. gh release upload "$CHANNEL_TAG" apps/desktop/dist-electron/latest-mac.yml \ --repo "$GITHUB_REPOSITORY" \ --clobber - name: Prune alpha assets beyond retention window if: env.MACOS_NOTARIZE == 'true' && (github.event_name == 'push' || inputs.publish == true) shell: bash env: GH_TOKEN: ${{ github.token }} CHANNEL_TAG: ${{ inputs.channel_tag || 'alpha-macos-latest' }} run: node scripts/release/prune-alpha-assets.mjs --repo "$GITHUB_REPOSITORY" --tag "$CHANNEL_TAG" --keep 100