diff --git a/.github/workflows/frontend-nightly.yml b/.github/workflows/frontend-nightly.yml index b58e1524c..ec3c400ab 100644 --- a/.github/workflows/frontend-nightly.yml +++ b/.github/workflows/frontend-nightly.yml @@ -93,15 +93,15 @@ jobs: # macOS signing + notarization env is exported by the "macOS signing # setup" step above via $GITHUB_ENV; the cert is in the keychain. - # Dedicated Intel (darwin-x64) nightly build leg, decoupled from the release - # matrix so the scarce macos-13 runner does not block publish-feed. Mirrors - # the release job's macOS steps exactly. publish-feed stays needs: release - # only (three fast legs); the x64 feed entry appears if this job finishes - # first, but the feed is not gated on it. + # Dedicated Intel (darwin-x64) nightly build leg. Kept separate from the + # release matrix (only a macOS host can build the x64 installer); publish-feed + # now waits on it so the x64 entry is present in nightly-mac.yml and Intel + # nightly users get auto-updates. Mirrors the release job's macOS steps exactly. + # Runner is macos-15-intel (macos-13 deprecated/no capacity; see release wf). release-intel: needs: guard if: needs.guard.outputs.should_build == 'true' - runs-on: macos-13 + runs-on: macos-15-intel permissions: contents: write defaults: @@ -161,7 +161,7 @@ jobs: # assets. The nightly version is only stamped in-memory on the build runners, # so derive the tag from the guard job's computed version, not package.json. publish-feed: - needs: [guard, release] + needs: [guard, release, release-intel] runs-on: ubuntu-latest permissions: contents: write diff --git a/.github/workflows/frontend-release.yml b/.github/workflows/frontend-release.yml index 281532985..06af8c680 100644 --- a/.github/workflows/frontend-release.yml +++ b/.github/workflows/frontend-release.yml @@ -92,7 +92,7 @@ jobs: # release the publish step just created, with --clobber (so re-runs replace). # # Arch-coverage note: each runner only builds its host arch. macos-latest - # (Apple silicon) produces darwin-arm64; macos-13 (Intel x64) produces + # (Apple silicon) produces darwin-arm64; macos-15-intel (Intel x64) produces # darwin-x64. Both macOS runners run this step (see the `if` below), so the # arm64 and x64 stable aliases are now both built (spec §8). # The Linux stable name is undecided (spec §11.3), so we build the deb/rpm @@ -155,13 +155,14 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Dedicated Intel (darwin-x64) build leg, decoupled from the release matrix so - # the scarce macos-13 runner does not block publish-feed. publish-feed needs - # only the three fast legs (release); this job uploads the x64 installer and - # stable alias independently. feed.mjs includes the x64 entry if this job - # finishes before publish-feed, but the feed is not gated on it. + # Dedicated Intel (darwin-x64) build leg. Kept a separate job (not in the + # release matrix) because only a macOS host can build the x64 installer, but + # publish-feed now waits on it (needs: [release, release-intel]) so the x64 + # entry is always present in latest-mac.yml and Intel users get auto-updates. + # Runner is macos-15-intel: macos-13 is deprecated/has no capacity (multi-hour + # queues), so this leg uses the supported Intel x64 image. release-intel: - runs-on: macos-13 + runs-on: macos-15-intel environment: release # same approval gate as the matrix release job permissions: contents: write @@ -216,7 +217,7 @@ jobs: # assets and upload them to the same release. Runs once, on Linux: it only # hashes already-built artifacts, so it needs no per-OS runner. publish-feed: - needs: release + needs: [release, release-intel] runs-on: ubuntu-latest permissions: contents: write diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 000000000..8f18fb362 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1 @@ +app-update.yml diff --git a/frontend/forge.config.ts b/frontend/forge.config.ts index 7c3cd5e42..fa7fd35aa 100644 --- a/frontend/forge.config.ts +++ b/frontend/forge.config.ts @@ -2,6 +2,7 @@ import type { ForgeConfig } from "@electron-forge/shared-types"; import { VitePlugin } from "@electron-forge/plugin-vite"; import MakerNSIS from "./makers/maker-nsis"; import MakerAppImage from "./makers/maker-appimage"; +import { writeFileSync } from "node:fs"; // Default GitHub release target (production). aoagents was the temporary rewrite // home; releases land on AgentWrapper (spec §1.1). @@ -30,7 +31,7 @@ const config: ForgeConfig = { // (.icns on macOS, .ico on Windows); Linux menu icons come from the // deb/rpm makers below, and the runtime window icon from src/main.ts. icon: "assets/icon", - extraResource: ["daemon", "assets/icon.png"], + extraResource: ["daemon", "assets/icon.png", "app-update.yml"], // Notarization. Two paths: // - CI: an App Store Connect API key. APPLE_API_KEY is a PATH to the .p8 // (the workflow decodes APPLE_API_KEY_BASE64 to a temp file), plus the @@ -53,6 +54,27 @@ const config: ForgeConfig = { } : undefined, }, + hooks: { + // electron-forge does not generate app-update.yml (electron-builder does); + // electron-updater reads it from the app's Resources dir at runtime to know + // which GitHub repo to pull from, else it throws ENOENT during download. + // Generate it in prePackage (BEFORE osxSign) and ship it via extraResource + // above, so it is copied into the bundle and SIGNED as part of the seal. + // Writing it after signing (a postPackage hook) adds an unsealed resource + // and macOS reports the app as "damaged". owner/repo are baked from + // AO_RELEASE_REPO at build time. + prePackage: async () => { + const { owner, name } = parseReleaseRepo(process.env.AO_RELEASE_REPO); + const yml = [ + "provider: github", + `owner: ${owner}`, + `repo: ${name}`, + "updaterCacheDirName: agent-orchestrator-updater", + "", + ].join("\n"); + writeFileSync("app-update.yml", yml); + }, + }, rebuildConfig: {}, makers: [ // Windows installer: NSIS via electron-builder (see makers/maker-nsis.ts). diff --git a/frontend/src/main/auto-updater.ts b/frontend/src/main/auto-updater.ts index ea6057998..bf8fceb82 100644 --- a/frontend/src/main/auto-updater.ts +++ b/frontend/src/main/auto-updater.ts @@ -10,22 +10,12 @@ import { type UpdateStatus, } from "./update-settings"; -// Default release repo, mirroring backend cli.releaseRepo. Override via env for -// fork test builds (AO_RELEASE_REPO=owner/repo). -const DEFAULT_RELEASE_REPO = "AgentWrapper/agent-orchestrator"; - -function repo(): { owner: string; name: string } { - const [owner, name] = (process.env.AO_RELEASE_REPO || DEFAULT_RELEASE_REPO).split("/"); - if (owner && name) return { owner, name }; - const [defOwner, defName] = DEFAULT_RELEASE_REPO.split("/"); - return { owner: defOwner, name: defName }; -} - -// configureFeed points electron-updater at the GitHub Releases feed for the -// chosen channel. Shared by the launch auto-check and the manual Settings flow. +// configureFeed sets the update channel on electron-updater. The repo/owner +// are loaded automatically from app-update.yml (written by forge.config.ts's +// postPackage hook into the app's Resources dir at build time). No runtime env +// or setFeedURL call is needed; electron-updater reads the bundled yml on first +// checkForUpdates. function configureFeed(channel: UpdateChannel): void { - const { owner, name } = repo(); - autoUpdater.setFeedURL({ provider: "github", owner, repo: name }); autoUpdater.channel = channel; // "latest" | "nightly" autoUpdater.allowDowngrade = true; // permits a nightly -> stable channel switch }