From 9d45ab62b1cc7d7e31322e121ec4a4b80156fd86 Mon Sep 17 00:00:00 2001 From: Mateo Cerquetella Date: Fri, 7 Aug 2026 10:46:08 -0400 Subject: [PATCH 1/3] Build desktop app for Intel Macs --- .github/workflows/build-desktop.yml | 16 ++++++++-------- .github/workflows/publish-bb-app.yml | 13 +++++++------ README.md | 2 +- apps/desktop/README.md | 8 +++++--- apps/desktop/electron-builder.config.json | 4 ++-- apps/desktop/package.json | 4 ++-- apps/desktop/scripts/smoke-packaged-app.mjs | 11 ++++++++++- .../desktop/test/electron-builder-config.test.ts | 13 +++++++++++++ 8 files changed, 48 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 38c586b81..b8b7a7e45 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -31,9 +31,9 @@ concurrency: jobs: macos: - name: macOS arm64 desktop artifacts - runs-on: macos-15 - timeout-minutes: 45 + name: macOS arm64 + x64 desktop artifacts + runs-on: macos-15-intel + timeout-minutes: 60 steps: - name: Checkout repository @@ -51,8 +51,8 @@ jobs: node-version: ${{ env.NODE_VERSION }} cache: pnpm - - name: Verify arm64 runner - run: test "$(uname -m)" = "arm64" + - name: Verify Intel runner + run: test "$(uname -m)" = "x86_64" - name: Install dependencies run: pnpm install --frozen-lockfile --prefer-offline @@ -103,7 +103,7 @@ jobs: exit 1 fi - - name: Package arm64 desktop artifacts + - name: Package arm64 and x64 desktop artifacts env: CSC_LINK: ${{ secrets.MACOS_CERTIFICATE_P12 }} CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} @@ -166,7 +166,7 @@ jobs: - name: Upload desktop workflow artifacts uses: actions/upload-artifact@v4 with: - name: bb-desktop-macos-arm64 + name: bb-desktop-macos-arm64-x64 path: | apps/desktop/release/*.dmg apps/desktop/release/*.zip @@ -250,7 +250,7 @@ jobs: { echo "## bb desktop artifacts" echo - echo "- arm64 macOS .dmg, .zip, latest-mac.yml, and desktop-version.json uploaded as workflow artifacts" + echo "- arm64 and x64 macOS .dmg/.zip artifacts, latest-mac.yml, and desktop-version.json uploaded as workflow artifacts" echo "- version: ${VERSION}" echo "- prerelease version: ${IS_PRERELEASE}" echo "- stable release publication requested and allowed: ${SHOULD_PUBLISH}" diff --git a/.github/workflows/publish-bb-app.yml b/.github/workflows/publish-bb-app.yml index 717d9ced2..b8bea0ea6 100644 --- a/.github/workflows/publish-bb-app.yml +++ b/.github/workflows/publish-bb-app.yml @@ -222,8 +222,8 @@ jobs: name: Publish bb Nightly desktop if: ${{ github.event_name == 'schedule' || (inputs.npm_tag == 'nightly' && inputs.dry_run == false) }} needs: publish - runs-on: macos-15 - timeout-minutes: 45 + runs-on: macos-15-intel + timeout-minutes: 60 permissions: contents: write env: @@ -246,8 +246,8 @@ jobs: node-version: 22.x cache: pnpm - - name: Verify arm64 runner - run: test "$(uname -m)" = "arm64" + - name: Verify Intel runner + run: test "$(uname -m)" = "x86_64" - name: Install dependencies run: pnpm install --frozen-lockfile --prefer-offline @@ -298,7 +298,7 @@ jobs: exit 1 fi - - name: Package signed arm64 nightly desktop artifacts + - name: Package signed arm64 and x64 nightly desktop artifacts env: CSC_LINK: ${{ secrets.MACOS_CERTIFICATE_P12 }} CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} @@ -317,7 +317,7 @@ jobs: - name: Upload nightly desktop workflow artifacts uses: actions/upload-artifact@v4 with: - name: bb-nightly-desktop-macos-arm64-${{ steps.nightly_version.outputs.version }} + name: bb-nightly-desktop-macos-arm64-x64-${{ steps.nightly_version.outputs.version }} retention-days: 14 path: | apps/desktop/release/*.dmg @@ -380,6 +380,7 @@ jobs: echo echo "- version: \`$VERSION\`" echo "- app identity: \`dev.bb.desktop.nightly\`" + echo "- architectures: \`arm64\`, \`x64\`" echo "- release: https://github.com/get-bb/bb/releases/tag/desktop-nightly" echo "- update feed: https://github.com/get-bb/bb/releases/download/desktop-nightly/desktop-version.json" echo "- retention for workflow artifacts: 14 days" diff --git a/README.md b/README.md index dc4e0545c..93e9eecad 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The recommended way to start using bb is the desktop app: **[Download the latest desktop app](https://github.com/get-bb/bb/releases/tag/desktop-latest)** -The desktop build is currently macOS Apple Silicon (arm64) only. Intel Mac and +The desktop build supports macOS on Apple Silicon (arm64) and Intel (x64). Linux users should run bb with `npx` instead. On Windows, run bb inside [WSL2 (Windows Subsystem for Linux)](https://learn.microsoft.com/windows/wsl/install): install WSL2 first, then run the same `npx` command below from your WSL2 (Linux) diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 552c20e40..6e72ed507 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -73,7 +73,8 @@ pnpm exec turbo run smoke:packaged --filter=@bb/desktop ``` Artifacts are written under `apps/desktop/release/`. The desktop build is -macOS-only and Apple Silicon arm64-only. Without signing secrets, local builds +macOS-only and produces separate Apple Silicon arm64 and Intel x64 artifacts. +Without signing secrets, local builds sign with a code-signing identity auto-discovered from the keychain and skip notarization. A valid signature matters even for local builds: macOS provenance-tracks unsigned apps, forcing syspolicyd to evaluate every exec in @@ -161,8 +162,9 @@ GitHub Actions secrets: | `APPLE_TEAM_ID` | Developer Team ID from `developer.apple.com/account` membership details. | Once those secrets are present, the next `Build Desktop` workflow run with -`publish=true` and `release_channel=stable` signs the `.app`, notarizes it, and -publishes the signed `.dmg` / `.zip` assets to `desktop-latest`. If no required +`publish=true` and `release_channel=stable` signs and notarizes both the arm64 +and x64 `.app` bundles, then publishes their `.dmg` / `.zip` assets to +`desktop-latest`. If no required signing secrets are configured, the workflow still builds unsigned artifacts, but the release job publishes only `desktop-version.json` and withholds unsigned binaries from `desktop-latest`. If only some required signing secrets are set, diff --git a/apps/desktop/electron-builder.config.json b/apps/desktop/electron-builder.config.json index d84d1ee08..02223ecd4 100644 --- a/apps/desktop/electron-builder.config.json +++ b/apps/desktop/electron-builder.config.json @@ -40,11 +40,11 @@ "target": [ { "target": "dmg", - "arch": ["arm64"] + "arch": ["arm64", "x64"] }, { "target": "zip", - "arch": ["arm64"] + "arch": ["arm64", "x64"] } ] }, diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 46fb40b44..7b7fe36af 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -10,11 +10,11 @@ "scripts": { "build": "node scripts/build.mjs", "clean": "rimraf dist release", - "desktop:build": "pnpm run build && node scripts/run-electron-builder.mjs --mac --arm64 --publish always", + "desktop:build": "pnpm run build && node scripts/run-electron-builder.mjs --mac --publish always", "desktop:version-feed": "tsx scripts/generate-version-feed.mts", "dev": "pnpm run prepare-runtime && pnpm run build && node --conditions=source --import tsx scripts/run-electron-dev.mjs", "dist": "pnpm run prepare-runtime && pnpm run desktop:build", - "package": "pnpm run prepare-runtime && pnpm run build && node scripts/run-electron-builder.mjs --mac --dir --arm64", + "package": "pnpm run prepare-runtime && pnpm run build && node scripts/run-electron-builder.mjs --mac --dir", "prepare-runtime": "pnpm exec turbo run build --filter=bb-app --output-logs=new-only", "smoke:packaged": "node scripts/smoke-packaged-app.mjs", "start": "pnpm run package && node scripts/run-packaged-app.mjs", diff --git a/apps/desktop/scripts/smoke-packaged-app.mjs b/apps/desktop/scripts/smoke-packaged-app.mjs index 26a3433b3..98b7cf578 100644 --- a/apps/desktop/scripts/smoke-packaged-app.mjs +++ b/apps/desktop/scripts/smoke-packaged-app.mjs @@ -125,10 +125,19 @@ async function readDesktopPackageVersion() { async function resolvePackagedAppBinary() { const entries = await readdir(releaseDir, { withFileTypes: true }); + const nativeOutputDirectory = process.arch === "arm64" ? "mac-arm64" : "mac"; const macOutputDirectories = entries .filter((entry) => entry.isDirectory() && entry.name.startsWith("mac")) .map((entry) => entry.name) - .sort(); + .sort((left, right) => { + if (left === nativeOutputDirectory) { + return -1; + } + if (right === nativeOutputDirectory) { + return 1; + } + return left.localeCompare(right); + }); for (const directory of macOutputDirectories) { const appBinary = join(releaseDir, directory, appBinaryRelativePath); diff --git a/apps/desktop/test/electron-builder-config.test.ts b/apps/desktop/test/electron-builder-config.test.ts index aba19b27b..3a2bb23d3 100644 --- a/apps/desktop/test/electron-builder-config.test.ts +++ b/apps/desktop/test/electron-builder-config.test.ts @@ -285,6 +285,19 @@ describe("electron-builder signing config", () => { }); }); + it("packages macOS installers and updater archives for arm64 and x64", async () => { + const configText = await readFile( + resolve(desktopPackageRoot, "electron-builder.config.json"), + "utf8", + ); + const config = electronBuilderConfigSchema.parse(JSON.parse(configText)); + + expect(config.mac.target).toEqual([ + { target: "dmg", arch: ["arm64", "x64"] }, + { target: "zip", arch: ["arm64", "x64"] }, + ]); + }); + it("disables in-place native rebuilds so the shared pnpm store is not mutated", async () => { // electron-builder's npmRebuild rebuilds better-sqlite3 through the // workspace symlink into the shared content-addressed store, flipping the From 9e588671d6f4f7ac66d90da018d60cbc5c1a4510 Mon Sep 17 00:00:00 2001 From: Mateo Cerquetella Date: Fri, 7 Aug 2026 12:03:50 -0400 Subject: [PATCH 2/3] Fix native Intel desktop packaging --- .github/workflows/build-desktop.yml | 90 ++++++++++-- .github/workflows/publish-bb-app.yml | 132 ++++++++++++++++-- apps/desktop/electron-builder.config.json | 11 +- apps/desktop/package.json | 5 + .../desktop/scripts/merge-update-metadata.mts | 83 +++++++++++ .../test/electron-builder-config.test.ts | 22 ++- .../test/merge-update-metadata.test.ts | 42 ++++++ pnpm-lock.yaml | 7 + 8 files changed, 361 insertions(+), 31 deletions(-) create mode 100644 apps/desktop/scripts/merge-update-metadata.mts create mode 100644 apps/desktop/test/merge-update-metadata.test.ts diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index b8b7a7e45..a448ea8b0 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -4,12 +4,12 @@ on: workflow_dispatch: inputs: publish: - description: "Publish stable desktop-version.json release assets" + description: 'Publish stable desktop-version.json release assets' required: true type: boolean default: false release_channel: - description: "Release channel. Only stable can update desktop-latest." + description: 'Release channel. Only stable can update desktop-latest.' required: true type: choice default: qa @@ -18,9 +18,9 @@ on: - stable env: - CSC_IDENTITY_AUTO_DISCOVERY: "false" - NODE_VERSION: "22.x" - PNPM_VERSION: "9.15.0" + CSC_IDENTITY_AUTO_DISCOVERY: 'false' + NODE_VERSION: '22.x' + PNPM_VERSION: '9.15.0' permissions: contents: write @@ -30,8 +30,8 @@ concurrency: cancel-in-progress: true jobs: - macos: - name: macOS arm64 + x64 desktop artifacts + macos-intel: + name: macOS x64 desktop artifacts runs-on: macos-15-intel timeout-minutes: 60 @@ -66,6 +66,67 @@ jobs: - name: Test desktop and launcher run: pnpm exec turbo run test --filter=@bb/desktop --filter=bb-app --force --output-logs=new-only + - name: Package x64 desktop artifacts + env: + CSC_LINK: ${{ secrets.MACOS_CERTIFICATE_P12 }} + CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} + CSC_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + run: pnpm exec turbo run desktop:build --filter=@bb/desktop --force --output-logs=new-only + + - name: Smoke test packaged desktop app + run: pnpm exec turbo run smoke:packaged --filter=@bb/desktop --force --output-logs=new-only + + - name: Upload x64 staging artifacts + uses: actions/upload-artifact@v4 + with: + name: bb-desktop-macos-x64-staging + path: | + apps/desktop/release/*.dmg + apps/desktop/release/*.zip + apps/desktop/release/*.blockmap + apps/desktop/release/latest-mac.yml + if-no-files-found: error + + macos: + name: macOS arm64 + x64 desktop artifacts + needs: macos-intel + runs-on: macos-15 + timeout-minutes: 60 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + run_install: false + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: pnpm + + - name: Verify arm64 runner + run: test "$(uname -m)" = "arm64" + + - name: Install dependencies + run: pnpm install --frozen-lockfile --prefer-offline + + - name: Typecheck desktop and launcher + run: pnpm exec turbo run typecheck --filter=@bb/desktop --filter=bb-app --output-logs=new-only + + - name: Build desktop + run: pnpm exec turbo run build --filter=@bb/desktop --output-logs=new-only + + - name: Test desktop and launcher + run: pnpm exec turbo run test --filter=@bb/desktop --filter=bb-app --force --output-logs=new-only + - name: Validate macOS signing secrets env: MACOS_CERTIFICATE_P12: ${{ secrets.MACOS_CERTIFICATE_P12 }} @@ -103,7 +164,7 @@ jobs: exit 1 fi - - name: Package arm64 and x64 desktop artifacts + - name: Package arm64 desktop artifacts env: CSC_LINK: ${{ secrets.MACOS_CERTIFICATE_P12 }} CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} @@ -116,6 +177,19 @@ jobs: - name: Smoke test packaged desktop app run: pnpm exec turbo run smoke:packaged --filter=@bb/desktop --force --output-logs=new-only + - name: Download x64 staging artifacts + uses: actions/download-artifact@v4 + with: + name: bb-desktop-macos-x64-staging + path: apps/desktop/release/x64 + + - name: Combine arm64 and x64 artifacts + run: | + mv apps/desktop/release/x64/*.dmg apps/desktop/release/ + mv apps/desktop/release/x64/*.zip apps/desktop/release/ + mv apps/desktop/release/x64/*.blockmap apps/desktop/release/ + pnpm --dir apps/desktop run desktop:merge-update-metadata -- release/x64/latest-mac.yml + - name: Generate desktop version feed run: pnpm --dir apps/desktop run desktop:version-feed diff --git a/.github/workflows/publish-bb-app.yml b/.github/workflows/publish-bb-app.yml index b8bea0ea6..c83c54e01 100644 --- a/.github/workflows/publish-bb-app.yml +++ b/.github/workflows/publish-bb-app.yml @@ -4,8 +4,8 @@ name: Publish bb-app on: schedule: - - cron: "0 3 * * *" - timezone: "America/Los_Angeles" + - cron: '0 3 * * *' + timezone: 'America/Los_Angeles' workflow_dispatch: inputs: npm_tag: @@ -218,17 +218,15 @@ jobs: echo "- commit: \`${GITHUB_SHA}\`" } >> "$GITHUB_STEP_SUMMARY" - nightly-desktop: - name: Publish bb Nightly desktop + nightly-desktop-intel: + name: Build bb Nightly desktop x64 if: ${{ github.event_name == 'schedule' || (inputs.npm_tag == 'nightly' && inputs.dry_run == false) }} needs: publish runs-on: macos-15-intel timeout-minutes: 60 - permissions: - contents: write env: BB_DESKTOP_RELEASE_CHANNEL: nightly - CSC_IDENTITY_AUTO_DISCOVERY: "false" + CSC_IDENTITY_AUTO_DISCOVERY: 'false' steps: - name: Checkout repository @@ -298,7 +296,7 @@ jobs: exit 1 fi - - name: Package signed arm64 and x64 nightly desktop artifacts + - name: Package signed x64 nightly desktop artifacts env: CSC_LINK: ${{ secrets.MACOS_CERTIFICATE_P12 }} CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} @@ -311,6 +309,124 @@ jobs: - name: Smoke test packaged nightly desktop app run: pnpm exec turbo run smoke:packaged --filter=@bb/desktop --force --output-logs=new-only + - name: Upload x64 nightly staging artifacts + uses: actions/upload-artifact@v4 + with: + name: bb-nightly-desktop-macos-x64-staging-${{ steps.nightly_version.outputs.version }} + retention-days: 14 + path: | + apps/desktop/release/*.dmg + apps/desktop/release/*.zip + apps/desktop/release/*.blockmap + apps/desktop/release/nightly-mac.yml + if-no-files-found: error + + nightly-desktop: + name: Publish bb Nightly desktop arm64 + x64 + if: ${{ github.event_name == 'schedule' || (inputs.npm_tag == 'nightly' && inputs.dry_run == false) }} + needs: [publish, nightly-desktop-intel] + runs-on: macos-15 + timeout-minutes: 60 + permissions: + contents: write + env: + BB_DESKTOP_RELEASE_CHANNEL: nightly + CSC_IDENTITY_AUTO_DISCOVERY: 'false' + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up pnpm + uses: pnpm/action-setup@v4 + with: + version: 9.15.0 + run_install: false + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.x + cache: pnpm + + - name: Verify arm64 runner + run: test "$(uname -m)" = "arm64" + + - name: Install dependencies + run: pnpm install --frozen-lockfile --prefer-offline + + - name: Prepare nightly version + id: nightly_version + run: | + nightly_version="$(node scripts/prepare-nightly-version.mjs)" + echo "version=${nightly_version}" >> "$GITHUB_OUTPUT" + echo "Prepared bb-app and desktop version ${nightly_version}." + + - name: Typecheck desktop and launcher + run: pnpm exec turbo run typecheck --filter=@bb/desktop --filter=bb-app --output-logs=new-only + + - name: Build desktop + run: pnpm exec turbo run build --filter=@bb/desktop --output-logs=new-only + + - name: Test desktop and launcher + run: pnpm exec turbo run test --filter=@bb/desktop --filter=bb-app --force --output-logs=new-only + + - name: Require macOS signing and notarization secrets + env: + MACOS_CERTIFICATE_P12: ${{ secrets.MACOS_CERTIFICATE_P12 }} + MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + run: | + set -euo pipefail + + required_secret_names=( + "MACOS_CERTIFICATE_P12" + "MACOS_CERTIFICATE_PASSWORD" + "APPLE_ID" + "APPLE_APP_PASSWORD" + "APPLE_TEAM_ID" + ) + missing_secret_names=() + + for secret_name in "${required_secret_names[@]}"; do + if [[ -z "${!secret_name:-}" ]]; then + missing_secret_names+=("$secret_name") + fi + done + + if [[ "${#missing_secret_names[@]}" -gt 0 ]]; then + echo "::error::Nightly desktop auto-updates require signed and notarized artifacts. Missing: ${missing_secret_names[*]}." + exit 1 + fi + + - name: Package signed arm64 nightly desktop artifacts + env: + CSC_LINK: ${{ secrets.MACOS_CERTIFICATE_P12 }} + CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} + CSC_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + run: pnpm exec turbo run desktop:build --filter=@bb/desktop --force --output-logs=new-only + + - name: Smoke test packaged nightly desktop app + run: pnpm exec turbo run smoke:packaged --filter=@bb/desktop --force --output-logs=new-only + + - name: Download x64 nightly staging artifacts + uses: actions/download-artifact@v4 + with: + name: bb-nightly-desktop-macos-x64-staging-${{ steps.nightly_version.outputs.version }} + path: apps/desktop/release/x64 + + - name: Combine arm64 and x64 nightly artifacts + run: | + mv apps/desktop/release/x64/*.dmg apps/desktop/release/ + mv apps/desktop/release/x64/*.zip apps/desktop/release/ + mv apps/desktop/release/x64/*.blockmap apps/desktop/release/ + pnpm --dir apps/desktop run desktop:merge-update-metadata -- release/x64/nightly-mac.yml + - name: Generate nightly desktop version feed run: pnpm --dir apps/desktop run desktop:version-feed diff --git a/apps/desktop/electron-builder.config.json b/apps/desktop/electron-builder.config.json index 02223ecd4..12854b61e 100644 --- a/apps/desktop/electron-builder.config.json +++ b/apps/desktop/electron-builder.config.json @@ -37,16 +37,7 @@ "icon": "assets/icon.icns", "identity": null, "notarize": false, - "target": [ - { - "target": "dmg", - "arch": ["arm64", "x64"] - }, - { - "target": "zip", - "arch": ["arm64", "x64"] - } - ] + "target": ["dmg", "zip"] }, "dmg": { "sign": false diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 7b7fe36af..00eb771d4 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -11,6 +11,7 @@ "build": "node scripts/build.mjs", "clean": "rimraf dist release", "desktop:build": "pnpm run build && node scripts/run-electron-builder.mjs --mac --publish always", + "desktop:merge-update-metadata": "tsx scripts/merge-update-metadata.mts", "desktop:version-feed": "tsx scripts/generate-version-feed.mts", "dev": "pnpm run prepare-runtime && pnpm run build && node --conditions=source --import tsx scripts/run-electron-dev.mjs", "dist": "pnpm run prepare-runtime && pnpm run desktop:build", @@ -28,6 +29,10 @@ "semver": "^7.7.0", "zod": "^4.3.6" }, + "optionalDependencies": { + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6" + }, "devDependencies": { "@bb/config": "workspace:*", "@bb/desktop-contract": "workspace:*", diff --git a/apps/desktop/scripts/merge-update-metadata.mts b/apps/desktop/scripts/merge-update-metadata.mts new file mode 100644 index 000000000..44b921f45 --- /dev/null +++ b/apps/desktop/scripts/merge-update-metadata.mts @@ -0,0 +1,83 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { resolve } from "node:path"; +import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; +import { z } from "zod"; +import { + createDesktopReleaseConfig, + resolveDesktopReleaseChannel, +} from "./desktop-release-channel.mjs"; + +const updateFileSchema = z.object({ + url: z.string().min(1), + sha512: z.string().min(1), + size: z.number().int().nonnegative(), +}); + +const updateMetadataSchema = z + .object({ + version: z.string().min(1), + files: z.array(updateFileSchema).min(1), + path: z.string().min(1), + sha512: z.string().min(1), + releaseDate: z.string().min(1), + }) + .passthrough(); + +type UpdateMetadata = z.infer; + +export function mergeUpdateMetadata( + primaryValue: unknown, + secondaryValue: unknown, +): UpdateMetadata { + const primary = updateMetadataSchema.parse(primaryValue); + const secondary = updateMetadataSchema.parse(secondaryValue); + + if (primary.version !== secondary.version) { + throw new Error( + `Cannot merge macOS update metadata for different versions: ${primary.version} and ${secondary.version}`, + ); + } + + const filesByUrl = new Map( + [...primary.files, ...secondary.files].map((file) => [file.url, file]), + ); + + return { + ...primary, + files: [...filesByUrl.values()], + }; +} + +async function main(): Promise { + const secondaryPathArgument = process.argv[2]; + if (secondaryPathArgument === undefined) { + throw new Error( + "Usage: pnpm run desktop:merge-update-metadata -- ", + ); + } + + const packageRoot = process.cwd(); + const releaseChannel = resolveDesktopReleaseChannel(process.env); + const releaseConfig = createDesktopReleaseConfig(releaseChannel); + const primaryPath = resolve( + packageRoot, + "release", + releaseConfig.updateMetadataFileName, + ); + const secondaryPath = resolve(packageRoot, secondaryPathArgument); + const [primaryText, secondaryText] = await Promise.all([ + readFile(primaryPath, "utf8"), + readFile(secondaryPath, "utf8"), + ]); + const merged = mergeUpdateMetadata( + parseYaml(primaryText), + parseYaml(secondaryText), + ); + + await writeFile(primaryPath, stringifyYaml(merged), "utf8"); + process.stdout.write(`Merged ${secondaryPath} into ${primaryPath}\n`); +} + +if (resolve(process.argv[1] ?? "") === resolve(import.meta.filename)) { + await main(); +} diff --git a/apps/desktop/test/electron-builder-config.test.ts b/apps/desktop/test/electron-builder-config.test.ts index 3a2bb23d3..37624f12a 100644 --- a/apps/desktop/test/electron-builder-config.test.ts +++ b/apps/desktop/test/electron-builder-config.test.ts @@ -245,6 +245,21 @@ describe("electron-builder signing config", () => { ); }); + it("ships native Parcel watcher packages for arm64 and x64", async () => { + const packageJsonText = await readFile( + resolve(desktopPackageRoot, "package.json"), + "utf8", + ); + const packageJson = desktopPackageJsonSchema.parse( + JSON.parse(packageJsonText), + ); + + expect(packageJson.optionalDependencies).toMatchObject({ + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + }); + }); + it("unpacks the ESM bb-app bridge with an explicit module extension", async () => { const configText = await readFile( resolve(desktopPackageRoot, "electron-builder.config.json"), @@ -285,17 +300,14 @@ describe("electron-builder signing config", () => { }); }); - it("packages macOS installers and updater archives for arm64 and x64", async () => { + it("packages macOS installers and updater archives for the requested architecture", async () => { const configText = await readFile( resolve(desktopPackageRoot, "electron-builder.config.json"), "utf8", ); const config = electronBuilderConfigSchema.parse(JSON.parse(configText)); - expect(config.mac.target).toEqual([ - { target: "dmg", arch: ["arm64", "x64"] }, - { target: "zip", arch: ["arm64", "x64"] }, - ]); + expect(config.mac.target).toEqual(["dmg", "zip"]); }); it("disables in-place native rebuilds so the shared pnpm store is not mutated", async () => { diff --git a/apps/desktop/test/merge-update-metadata.test.ts b/apps/desktop/test/merge-update-metadata.test.ts new file mode 100644 index 000000000..cb3a64bb5 --- /dev/null +++ b/apps/desktop/test/merge-update-metadata.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it } from "vitest"; +import { mergeUpdateMetadata } from "../scripts/merge-update-metadata.mjs"; + +function metadata(version: string, arch: "arm64" | "x64") { + const zip = `bb-${version}-${arch}.zip`; + return { + version, + files: [ + { url: zip, sha512: `${arch}-zip-sha`, size: 10 }, + { + url: `bb-${version}-${arch}.dmg`, + sha512: `${arch}-dmg-sha`, + size: 20, + }, + ], + path: zip, + sha512: `${arch}-zip-sha`, + releaseDate: "2026-08-07T00:00:00.000Z", + }; +} + +describe("mergeUpdateMetadata", () => { + it("combines updater files from native arm64 and x64 builds", () => { + const merged = mergeUpdateMetadata( + metadata("1.2.3", "arm64"), + metadata("1.2.3", "x64"), + ); + + expect(merged.files.map((file) => file.url)).toEqual([ + "bb-1.2.3-arm64.zip", + "bb-1.2.3-arm64.dmg", + "bb-1.2.3-x64.zip", + "bb-1.2.3-x64.dmg", + ]); + }); + + it("rejects metadata from different versions", () => { + expect(() => + mergeUpdateMetadata(metadata("1.2.3", "arm64"), metadata("1.2.4", "x64")), + ).toThrow("different versions"); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9030116d5..18db4cfdc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -545,6 +545,13 @@ importers: zod: specifier: 4.3.6 version: 4.3.6 + optionalDependencies: + '@parcel/watcher-darwin-arm64': + specifier: 2.5.6 + version: 2.5.6 + '@parcel/watcher-darwin-x64': + specifier: 2.5.6 + version: 2.5.6 devDependencies: '@bb/config': specifier: workspace:* From 5d3f1bd1a33bf43f22e1fe7d3b3a03e91b5223f2 Mon Sep 17 00:00:00 2001 From: Mateo Cerquetella Date: Sun, 9 Aug 2026 20:21:43 -0300 Subject: [PATCH 3/3] Simplify Intel desktop packaging --- .github/workflows/build-desktop.yml | 92 ++---------- .github/workflows/publish-bb-app.yml | 135 ++---------------- apps/desktop/README.md | 5 +- apps/desktop/electron-builder.config.json | 11 +- apps/desktop/package.json | 5 - .../desktop/scripts/merge-update-metadata.mts | 83 ----------- apps/desktop/scripts/smoke-packaged-app.mjs | 11 +- .../test/electron-builder-config.test.ts | 25 ---- .../test/merge-update-metadata.test.ts | 42 ------ pnpm-lock.yaml | 7 - 10 files changed, 31 insertions(+), 385 deletions(-) delete mode 100644 apps/desktop/scripts/merge-update-metadata.mts delete mode 100644 apps/desktop/test/merge-update-metadata.test.ts diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index a448ea8b0..9e557a001 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -4,12 +4,12 @@ on: workflow_dispatch: inputs: publish: - description: 'Publish stable desktop-version.json release assets' + description: "Publish stable desktop-version.json release assets" required: true type: boolean default: false release_channel: - description: 'Release channel. Only stable can update desktop-latest.' + description: "Release channel. Only stable can update desktop-latest." required: true type: choice default: qa @@ -18,9 +18,9 @@ on: - stable env: - CSC_IDENTITY_AUTO_DISCOVERY: 'false' - NODE_VERSION: '22.x' - PNPM_VERSION: '9.15.0' + CSC_IDENTITY_AUTO_DISCOVERY: "false" + NODE_VERSION: "22.x" + PNPM_VERSION: "9.15.0" permissions: contents: write @@ -30,10 +30,10 @@ concurrency: cancel-in-progress: true jobs: - macos-intel: - name: macOS x64 desktop artifacts + macos: + name: macOS arm64 + x64 desktop artifacts runs-on: macos-15-intel - timeout-minutes: 60 + timeout-minutes: 45 steps: - name: Checkout repository @@ -66,67 +66,6 @@ jobs: - name: Test desktop and launcher run: pnpm exec turbo run test --filter=@bb/desktop --filter=bb-app --force --output-logs=new-only - - name: Package x64 desktop artifacts - env: - CSC_LINK: ${{ secrets.MACOS_CERTIFICATE_P12 }} - CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} - CSC_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }} - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - run: pnpm exec turbo run desktop:build --filter=@bb/desktop --force --output-logs=new-only - - - name: Smoke test packaged desktop app - run: pnpm exec turbo run smoke:packaged --filter=@bb/desktop --force --output-logs=new-only - - - name: Upload x64 staging artifacts - uses: actions/upload-artifact@v4 - with: - name: bb-desktop-macos-x64-staging - path: | - apps/desktop/release/*.dmg - apps/desktop/release/*.zip - apps/desktop/release/*.blockmap - apps/desktop/release/latest-mac.yml - if-no-files-found: error - - macos: - name: macOS arm64 + x64 desktop artifacts - needs: macos-intel - runs-on: macos-15 - timeout-minutes: 60 - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up pnpm - uses: pnpm/action-setup@v4 - with: - version: ${{ env.PNPM_VERSION }} - run_install: false - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - cache: pnpm - - - name: Verify arm64 runner - run: test "$(uname -m)" = "arm64" - - - name: Install dependencies - run: pnpm install --frozen-lockfile --prefer-offline - - - name: Typecheck desktop and launcher - run: pnpm exec turbo run typecheck --filter=@bb/desktop --filter=bb-app --output-logs=new-only - - - name: Build desktop - run: pnpm exec turbo run build --filter=@bb/desktop --output-logs=new-only - - - name: Test desktop and launcher - run: pnpm exec turbo run test --filter=@bb/desktop --filter=bb-app --force --output-logs=new-only - - name: Validate macOS signing secrets env: MACOS_CERTIFICATE_P12: ${{ secrets.MACOS_CERTIFICATE_P12 }} @@ -164,7 +103,7 @@ jobs: exit 1 fi - - name: Package arm64 desktop artifacts + - name: Package arm64 and x64 desktop artifacts env: CSC_LINK: ${{ secrets.MACOS_CERTIFICATE_P12 }} CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} @@ -177,19 +116,6 @@ jobs: - name: Smoke test packaged desktop app run: pnpm exec turbo run smoke:packaged --filter=@bb/desktop --force --output-logs=new-only - - name: Download x64 staging artifacts - uses: actions/download-artifact@v4 - with: - name: bb-desktop-macos-x64-staging - path: apps/desktop/release/x64 - - - name: Combine arm64 and x64 artifacts - run: | - mv apps/desktop/release/x64/*.dmg apps/desktop/release/ - mv apps/desktop/release/x64/*.zip apps/desktop/release/ - mv apps/desktop/release/x64/*.blockmap apps/desktop/release/ - pnpm --dir apps/desktop run desktop:merge-update-metadata -- release/x64/latest-mac.yml - - name: Generate desktop version feed run: pnpm --dir apps/desktop run desktop:version-feed diff --git a/.github/workflows/publish-bb-app.yml b/.github/workflows/publish-bb-app.yml index c83c54e01..abc99b0c4 100644 --- a/.github/workflows/publish-bb-app.yml +++ b/.github/workflows/publish-bb-app.yml @@ -4,8 +4,8 @@ name: Publish bb-app on: schedule: - - cron: '0 3 * * *' - timezone: 'America/Los_Angeles' + - cron: "0 3 * * *" + timezone: "America/Los_Angeles" workflow_dispatch: inputs: npm_tag: @@ -218,120 +218,17 @@ jobs: echo "- commit: \`${GITHUB_SHA}\`" } >> "$GITHUB_STEP_SUMMARY" - nightly-desktop-intel: - name: Build bb Nightly desktop x64 + nightly-desktop: + name: Publish bb Nightly desktop if: ${{ github.event_name == 'schedule' || (inputs.npm_tag == 'nightly' && inputs.dry_run == false) }} needs: publish runs-on: macos-15-intel - timeout-minutes: 60 - env: - BB_DESKTOP_RELEASE_CHANNEL: nightly - CSC_IDENTITY_AUTO_DISCOVERY: 'false' - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up pnpm - uses: pnpm/action-setup@v4 - with: - version: 9.15.0 - run_install: false - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 22.x - cache: pnpm - - - name: Verify Intel runner - run: test "$(uname -m)" = "x86_64" - - - name: Install dependencies - run: pnpm install --frozen-lockfile --prefer-offline - - - name: Prepare nightly version - id: nightly_version - run: | - nightly_version="$(node scripts/prepare-nightly-version.mjs)" - echo "version=${nightly_version}" >> "$GITHUB_OUTPUT" - echo "Prepared bb-app and desktop version ${nightly_version}." - - - name: Typecheck desktop and launcher - run: pnpm exec turbo run typecheck --filter=@bb/desktop --filter=bb-app --output-logs=new-only - - - name: Build desktop - run: pnpm exec turbo run build --filter=@bb/desktop --output-logs=new-only - - - name: Test desktop and launcher - run: pnpm exec turbo run test --filter=@bb/desktop --filter=bb-app --force --output-logs=new-only - - - name: Require macOS signing and notarization secrets - env: - MACOS_CERTIFICATE_P12: ${{ secrets.MACOS_CERTIFICATE_P12 }} - MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - run: | - set -euo pipefail - - required_secret_names=( - "MACOS_CERTIFICATE_P12" - "MACOS_CERTIFICATE_PASSWORD" - "APPLE_ID" - "APPLE_APP_PASSWORD" - "APPLE_TEAM_ID" - ) - missing_secret_names=() - - for secret_name in "${required_secret_names[@]}"; do - if [[ -z "${!secret_name:-}" ]]; then - missing_secret_names+=("$secret_name") - fi - done - - if [[ "${#missing_secret_names[@]}" -gt 0 ]]; then - echo "::error::Nightly desktop auto-updates require signed and notarized artifacts. Missing: ${missing_secret_names[*]}." - exit 1 - fi - - - name: Package signed x64 nightly desktop artifacts - env: - CSC_LINK: ${{ secrets.MACOS_CERTIFICATE_P12 }} - CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} - CSC_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }} - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - run: pnpm exec turbo run desktop:build --filter=@bb/desktop --force --output-logs=new-only - - - name: Smoke test packaged nightly desktop app - run: pnpm exec turbo run smoke:packaged --filter=@bb/desktop --force --output-logs=new-only - - - name: Upload x64 nightly staging artifacts - uses: actions/upload-artifact@v4 - with: - name: bb-nightly-desktop-macos-x64-staging-${{ steps.nightly_version.outputs.version }} - retention-days: 14 - path: | - apps/desktop/release/*.dmg - apps/desktop/release/*.zip - apps/desktop/release/*.blockmap - apps/desktop/release/nightly-mac.yml - if-no-files-found: error - - nightly-desktop: - name: Publish bb Nightly desktop arm64 + x64 - if: ${{ github.event_name == 'schedule' || (inputs.npm_tag == 'nightly' && inputs.dry_run == false) }} - needs: [publish, nightly-desktop-intel] - runs-on: macos-15 - timeout-minutes: 60 + timeout-minutes: 45 permissions: contents: write env: BB_DESKTOP_RELEASE_CHANNEL: nightly - CSC_IDENTITY_AUTO_DISCOVERY: 'false' + CSC_IDENTITY_AUTO_DISCOVERY: "false" steps: - name: Checkout repository @@ -349,8 +246,8 @@ jobs: node-version: 22.x cache: pnpm - - name: Verify arm64 runner - run: test "$(uname -m)" = "arm64" + - name: Verify Intel runner + run: test "$(uname -m)" = "x86_64" - name: Install dependencies run: pnpm install --frozen-lockfile --prefer-offline @@ -401,7 +298,7 @@ jobs: exit 1 fi - - name: Package signed arm64 nightly desktop artifacts + - name: Package signed arm64 and x64 nightly desktop artifacts env: CSC_LINK: ${{ secrets.MACOS_CERTIFICATE_P12 }} CSC_KEY_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} @@ -414,19 +311,6 @@ jobs: - name: Smoke test packaged nightly desktop app run: pnpm exec turbo run smoke:packaged --filter=@bb/desktop --force --output-logs=new-only - - name: Download x64 nightly staging artifacts - uses: actions/download-artifact@v4 - with: - name: bb-nightly-desktop-macos-x64-staging-${{ steps.nightly_version.outputs.version }} - path: apps/desktop/release/x64 - - - name: Combine arm64 and x64 nightly artifacts - run: | - mv apps/desktop/release/x64/*.dmg apps/desktop/release/ - mv apps/desktop/release/x64/*.zip apps/desktop/release/ - mv apps/desktop/release/x64/*.blockmap apps/desktop/release/ - pnpm --dir apps/desktop run desktop:merge-update-metadata -- release/x64/nightly-mac.yml - - name: Generate nightly desktop version feed run: pnpm --dir apps/desktop run desktop:version-feed @@ -496,7 +380,6 @@ jobs: echo echo "- version: \`$VERSION\`" echo "- app identity: \`dev.bb.desktop.nightly\`" - echo "- architectures: \`arm64\`, \`x64\`" echo "- release: https://github.com/get-bb/bb/releases/tag/desktop-nightly" echo "- update feed: https://github.com/get-bb/bb/releases/download/desktop-nightly/desktop-version.json" echo "- retention for workflow artifacts: 14 days" diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 6e72ed507..c2275e2c4 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -162,9 +162,8 @@ GitHub Actions secrets: | `APPLE_TEAM_ID` | Developer Team ID from `developer.apple.com/account` membership details. | Once those secrets are present, the next `Build Desktop` workflow run with -`publish=true` and `release_channel=stable` signs and notarizes both the arm64 -and x64 `.app` bundles, then publishes their `.dmg` / `.zip` assets to -`desktop-latest`. If no required +`publish=true` and `release_channel=stable` signs the `.app`, notarizes it, and +publishes the signed `.dmg` / `.zip` assets to `desktop-latest`. If no required signing secrets are configured, the workflow still builds unsigned artifacts, but the release job publishes only `desktop-version.json` and withholds unsigned binaries from `desktop-latest`. If only some required signing secrets are set, diff --git a/apps/desktop/electron-builder.config.json b/apps/desktop/electron-builder.config.json index 12854b61e..02223ecd4 100644 --- a/apps/desktop/electron-builder.config.json +++ b/apps/desktop/electron-builder.config.json @@ -37,7 +37,16 @@ "icon": "assets/icon.icns", "identity": null, "notarize": false, - "target": ["dmg", "zip"] + "target": [ + { + "target": "dmg", + "arch": ["arm64", "x64"] + }, + { + "target": "zip", + "arch": ["arm64", "x64"] + } + ] }, "dmg": { "sign": false diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 00eb771d4..7b7fe36af 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -11,7 +11,6 @@ "build": "node scripts/build.mjs", "clean": "rimraf dist release", "desktop:build": "pnpm run build && node scripts/run-electron-builder.mjs --mac --publish always", - "desktop:merge-update-metadata": "tsx scripts/merge-update-metadata.mts", "desktop:version-feed": "tsx scripts/generate-version-feed.mts", "dev": "pnpm run prepare-runtime && pnpm run build && node --conditions=source --import tsx scripts/run-electron-dev.mjs", "dist": "pnpm run prepare-runtime && pnpm run desktop:build", @@ -29,10 +28,6 @@ "semver": "^7.7.0", "zod": "^4.3.6" }, - "optionalDependencies": { - "@parcel/watcher-darwin-arm64": "2.5.6", - "@parcel/watcher-darwin-x64": "2.5.6" - }, "devDependencies": { "@bb/config": "workspace:*", "@bb/desktop-contract": "workspace:*", diff --git a/apps/desktop/scripts/merge-update-metadata.mts b/apps/desktop/scripts/merge-update-metadata.mts deleted file mode 100644 index 44b921f45..000000000 --- a/apps/desktop/scripts/merge-update-metadata.mts +++ /dev/null @@ -1,83 +0,0 @@ -import { readFile, writeFile } from "node:fs/promises"; -import { resolve } from "node:path"; -import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; -import { z } from "zod"; -import { - createDesktopReleaseConfig, - resolveDesktopReleaseChannel, -} from "./desktop-release-channel.mjs"; - -const updateFileSchema = z.object({ - url: z.string().min(1), - sha512: z.string().min(1), - size: z.number().int().nonnegative(), -}); - -const updateMetadataSchema = z - .object({ - version: z.string().min(1), - files: z.array(updateFileSchema).min(1), - path: z.string().min(1), - sha512: z.string().min(1), - releaseDate: z.string().min(1), - }) - .passthrough(); - -type UpdateMetadata = z.infer; - -export function mergeUpdateMetadata( - primaryValue: unknown, - secondaryValue: unknown, -): UpdateMetadata { - const primary = updateMetadataSchema.parse(primaryValue); - const secondary = updateMetadataSchema.parse(secondaryValue); - - if (primary.version !== secondary.version) { - throw new Error( - `Cannot merge macOS update metadata for different versions: ${primary.version} and ${secondary.version}`, - ); - } - - const filesByUrl = new Map( - [...primary.files, ...secondary.files].map((file) => [file.url, file]), - ); - - return { - ...primary, - files: [...filesByUrl.values()], - }; -} - -async function main(): Promise { - const secondaryPathArgument = process.argv[2]; - if (secondaryPathArgument === undefined) { - throw new Error( - "Usage: pnpm run desktop:merge-update-metadata -- ", - ); - } - - const packageRoot = process.cwd(); - const releaseChannel = resolveDesktopReleaseChannel(process.env); - const releaseConfig = createDesktopReleaseConfig(releaseChannel); - const primaryPath = resolve( - packageRoot, - "release", - releaseConfig.updateMetadataFileName, - ); - const secondaryPath = resolve(packageRoot, secondaryPathArgument); - const [primaryText, secondaryText] = await Promise.all([ - readFile(primaryPath, "utf8"), - readFile(secondaryPath, "utf8"), - ]); - const merged = mergeUpdateMetadata( - parseYaml(primaryText), - parseYaml(secondaryText), - ); - - await writeFile(primaryPath, stringifyYaml(merged), "utf8"); - process.stdout.write(`Merged ${secondaryPath} into ${primaryPath}\n`); -} - -if (resolve(process.argv[1] ?? "") === resolve(import.meta.filename)) { - await main(); -} diff --git a/apps/desktop/scripts/smoke-packaged-app.mjs b/apps/desktop/scripts/smoke-packaged-app.mjs index 98b7cf578..26a3433b3 100644 --- a/apps/desktop/scripts/smoke-packaged-app.mjs +++ b/apps/desktop/scripts/smoke-packaged-app.mjs @@ -125,19 +125,10 @@ async function readDesktopPackageVersion() { async function resolvePackagedAppBinary() { const entries = await readdir(releaseDir, { withFileTypes: true }); - const nativeOutputDirectory = process.arch === "arm64" ? "mac-arm64" : "mac"; const macOutputDirectories = entries .filter((entry) => entry.isDirectory() && entry.name.startsWith("mac")) .map((entry) => entry.name) - .sort((left, right) => { - if (left === nativeOutputDirectory) { - return -1; - } - if (right === nativeOutputDirectory) { - return 1; - } - return left.localeCompare(right); - }); + .sort(); for (const directory of macOutputDirectories) { const appBinary = join(releaseDir, directory, appBinaryRelativePath); diff --git a/apps/desktop/test/electron-builder-config.test.ts b/apps/desktop/test/electron-builder-config.test.ts index 37624f12a..aba19b27b 100644 --- a/apps/desktop/test/electron-builder-config.test.ts +++ b/apps/desktop/test/electron-builder-config.test.ts @@ -245,21 +245,6 @@ describe("electron-builder signing config", () => { ); }); - it("ships native Parcel watcher packages for arm64 and x64", async () => { - const packageJsonText = await readFile( - resolve(desktopPackageRoot, "package.json"), - "utf8", - ); - const packageJson = desktopPackageJsonSchema.parse( - JSON.parse(packageJsonText), - ); - - expect(packageJson.optionalDependencies).toMatchObject({ - "@parcel/watcher-darwin-arm64": "2.5.6", - "@parcel/watcher-darwin-x64": "2.5.6", - }); - }); - it("unpacks the ESM bb-app bridge with an explicit module extension", async () => { const configText = await readFile( resolve(desktopPackageRoot, "electron-builder.config.json"), @@ -300,16 +285,6 @@ describe("electron-builder signing config", () => { }); }); - it("packages macOS installers and updater archives for the requested architecture", async () => { - const configText = await readFile( - resolve(desktopPackageRoot, "electron-builder.config.json"), - "utf8", - ); - const config = electronBuilderConfigSchema.parse(JSON.parse(configText)); - - expect(config.mac.target).toEqual(["dmg", "zip"]); - }); - it("disables in-place native rebuilds so the shared pnpm store is not mutated", async () => { // electron-builder's npmRebuild rebuilds better-sqlite3 through the // workspace symlink into the shared content-addressed store, flipping the diff --git a/apps/desktop/test/merge-update-metadata.test.ts b/apps/desktop/test/merge-update-metadata.test.ts deleted file mode 100644 index cb3a64bb5..000000000 --- a/apps/desktop/test/merge-update-metadata.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { mergeUpdateMetadata } from "../scripts/merge-update-metadata.mjs"; - -function metadata(version: string, arch: "arm64" | "x64") { - const zip = `bb-${version}-${arch}.zip`; - return { - version, - files: [ - { url: zip, sha512: `${arch}-zip-sha`, size: 10 }, - { - url: `bb-${version}-${arch}.dmg`, - sha512: `${arch}-dmg-sha`, - size: 20, - }, - ], - path: zip, - sha512: `${arch}-zip-sha`, - releaseDate: "2026-08-07T00:00:00.000Z", - }; -} - -describe("mergeUpdateMetadata", () => { - it("combines updater files from native arm64 and x64 builds", () => { - const merged = mergeUpdateMetadata( - metadata("1.2.3", "arm64"), - metadata("1.2.3", "x64"), - ); - - expect(merged.files.map((file) => file.url)).toEqual([ - "bb-1.2.3-arm64.zip", - "bb-1.2.3-arm64.dmg", - "bb-1.2.3-x64.zip", - "bb-1.2.3-x64.dmg", - ]); - }); - - it("rejects metadata from different versions", () => { - expect(() => - mergeUpdateMetadata(metadata("1.2.3", "arm64"), metadata("1.2.4", "x64")), - ).toThrow("different versions"); - }); -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18db4cfdc..9030116d5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -545,13 +545,6 @@ importers: zod: specifier: 4.3.6 version: 4.3.6 - optionalDependencies: - '@parcel/watcher-darwin-arm64': - specifier: 2.5.6 - version: 2.5.6 - '@parcel/watcher-darwin-x64': - specifier: 2.5.6 - version: 2.5.6 devDependencies: '@bb/config': specifier: workspace:*