fix(update): bundle app-update.yml so electron-updater can install (forge omits it) (#2244)
* fix(update): generate app-update.yml at build time; drop runtime setFeedURL electron-forge does not emit app-update.yml; electron-updater requires it at process.resourcesPath to resolve the GitHub release feed. Without it every packaged app threw ENOENT on the first download attempt, making updates detected but never installed. Two-part fix: - forge.config.ts: add postPackage hook that writes app-update.yml into each platform's Resources dir (baked from AO_RELEASE_REPO so fork builds point at the fork, prod at AgentWrapper/agent-orchestrator). - auto-updater.ts: remove setFeedURL + repo() + DEFAULT_RELEASE_REPO; configureFeed now only sets channel + allowDowngrade. electron-updater auto-loads the bundled yml on the first checkForUpdates call. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: format with prettier [skip ci] * fix(update): generate app-update.yml before signing, not after The postPackage hook wrote app-update.yml into Contents/Resources AFTER osxSign sealed the bundle, so the added file was unsealed and macOS reported the app as "damaged" (codesign: "a sealed resource is missing or invalid"). Generate it in a prePackage hook and ship it via packagerConfig.extraResource, so it is copied into the bundle and signed as part of the seal. The generated app-update.yml is gitignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: format with prettier [skip ci] * ci(release): move Intel leg to macos-15-intel and gate the feed on it macos-13 is deprecated and has no runner capacity (multi-hour queues), so the detached release-intel leg never completed. Switch it to the supported macos-15-intel image in both the release and nightly workflows. Now that the Intel leg gets a runner and builds + signs the x64 installer reliably (verified on fork v0.10.10), re-couple it into publish-feed (needs: [release, release-intel]) so latest-mac.yml / nightly-mac.yml always carry the x64 entry and Intel macOS users receive auto-updates. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
ff23e66055
commit
965e388792
|
|
@ -93,15 +93,15 @@ jobs:
|
||||||
# macOS signing + notarization env is exported by the "macOS signing
|
# macOS signing + notarization env is exported by the "macOS signing
|
||||||
# setup" step above via $GITHUB_ENV; the cert is in the keychain.
|
# setup" step above via $GITHUB_ENV; the cert is in the keychain.
|
||||||
|
|
||||||
# Dedicated Intel (darwin-x64) nightly build leg, decoupled from the release
|
# Dedicated Intel (darwin-x64) nightly build leg. Kept separate from the
|
||||||
# matrix so the scarce macos-13 runner does not block publish-feed. Mirrors
|
# release matrix (only a macOS host can build the x64 installer); publish-feed
|
||||||
# the release job's macOS steps exactly. publish-feed stays needs: release
|
# now waits on it so the x64 entry is present in nightly-mac.yml and Intel
|
||||||
# only (three fast legs); the x64 feed entry appears if this job finishes
|
# nightly users get auto-updates. Mirrors the release job's macOS steps exactly.
|
||||||
# first, but the feed is not gated on it.
|
# Runner is macos-15-intel (macos-13 deprecated/no capacity; see release wf).
|
||||||
release-intel:
|
release-intel:
|
||||||
needs: guard
|
needs: guard
|
||||||
if: needs.guard.outputs.should_build == 'true'
|
if: needs.guard.outputs.should_build == 'true'
|
||||||
runs-on: macos-13
|
runs-on: macos-15-intel
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
defaults:
|
defaults:
|
||||||
|
|
@ -161,7 +161,7 @@ jobs:
|
||||||
# assets. The nightly version is only stamped in-memory on the build runners,
|
# 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.
|
# so derive the tag from the guard job's computed version, not package.json.
|
||||||
publish-feed:
|
publish-feed:
|
||||||
needs: [guard, release]
|
needs: [guard, release, release-intel]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ jobs:
|
||||||
# release the publish step just created, with --clobber (so re-runs replace).
|
# release the publish step just created, with --clobber (so re-runs replace).
|
||||||
#
|
#
|
||||||
# Arch-coverage note: each runner only builds its host arch. macos-latest
|
# 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
|
# 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).
|
# 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
|
# The Linux stable name is undecided (spec §11.3), so we build the deb/rpm
|
||||||
|
|
@ -155,13 +155,14 @@ jobs:
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
# Dedicated Intel (darwin-x64) build leg, decoupled from the release matrix so
|
# Dedicated Intel (darwin-x64) build leg. Kept a separate job (not in the
|
||||||
# the scarce macos-13 runner does not block publish-feed. publish-feed needs
|
# release matrix) because only a macOS host can build the x64 installer, but
|
||||||
# only the three fast legs (release); this job uploads the x64 installer and
|
# publish-feed now waits on it (needs: [release, release-intel]) so the x64
|
||||||
# stable alias independently. feed.mjs includes the x64 entry if this job
|
# entry is always present in latest-mac.yml and Intel users get auto-updates.
|
||||||
# finishes before publish-feed, but the feed is not gated on it.
|
# 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:
|
release-intel:
|
||||||
runs-on: macos-13
|
runs-on: macos-15-intel
|
||||||
environment: release # same approval gate as the matrix release job
|
environment: release # same approval gate as the matrix release job
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
@ -216,7 +217,7 @@ jobs:
|
||||||
# assets and upload them to the same release. Runs once, on Linux: it only
|
# 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.
|
# hashes already-built artifacts, so it needs no per-OS runner.
|
||||||
publish-feed:
|
publish-feed:
|
||||||
needs: release
|
needs: [release, release-intel]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
app-update.yml
|
||||||
|
|
@ -2,6 +2,7 @@ import type { ForgeConfig } from "@electron-forge/shared-types";
|
||||||
import { VitePlugin } from "@electron-forge/plugin-vite";
|
import { VitePlugin } from "@electron-forge/plugin-vite";
|
||||||
import MakerNSIS from "./makers/maker-nsis";
|
import MakerNSIS from "./makers/maker-nsis";
|
||||||
import MakerAppImage from "./makers/maker-appimage";
|
import MakerAppImage from "./makers/maker-appimage";
|
||||||
|
import { writeFileSync } from "node:fs";
|
||||||
|
|
||||||
// Default GitHub release target (production). aoagents was the temporary rewrite
|
// Default GitHub release target (production). aoagents was the temporary rewrite
|
||||||
// home; releases land on AgentWrapper (spec §1.1).
|
// 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
|
// (.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.
|
// deb/rpm makers below, and the runtime window icon from src/main.ts.
|
||||||
icon: "assets/icon",
|
icon: "assets/icon",
|
||||||
extraResource: ["daemon", "assets/icon.png"],
|
extraResource: ["daemon", "assets/icon.png", "app-update.yml"],
|
||||||
// Notarization. Two paths:
|
// Notarization. Two paths:
|
||||||
// - CI: an App Store Connect API key. APPLE_API_KEY is a PATH to the .p8
|
// - 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
|
// (the workflow decodes APPLE_API_KEY_BASE64 to a temp file), plus the
|
||||||
|
|
@ -53,6 +54,27 @@ const config: ForgeConfig = {
|
||||||
}
|
}
|
||||||
: undefined,
|
: 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: {},
|
rebuildConfig: {},
|
||||||
makers: [
|
makers: [
|
||||||
// Windows installer: NSIS via electron-builder (see makers/maker-nsis.ts).
|
// Windows installer: NSIS via electron-builder (see makers/maker-nsis.ts).
|
||||||
|
|
|
||||||
|
|
@ -10,22 +10,12 @@ import {
|
||||||
type UpdateStatus,
|
type UpdateStatus,
|
||||||
} from "./update-settings";
|
} from "./update-settings";
|
||||||
|
|
||||||
// Default release repo, mirroring backend cli.releaseRepo. Override via env for
|
// configureFeed sets the update channel on electron-updater. The repo/owner
|
||||||
// fork test builds (AO_RELEASE_REPO=owner/repo).
|
// are loaded automatically from app-update.yml (written by forge.config.ts's
|
||||||
const DEFAULT_RELEASE_REPO = "AgentWrapper/agent-orchestrator";
|
// 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
|
||||||
function repo(): { owner: string; name: string } {
|
// checkForUpdates.
|
||||||
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.
|
|
||||||
function configureFeed(channel: UpdateChannel): void {
|
function configureFeed(channel: UpdateChannel): void {
|
||||||
const { owner, name } = repo();
|
|
||||||
autoUpdater.setFeedURL({ provider: "github", owner, repo: name });
|
|
||||||
autoUpdater.channel = channel; // "latest" | "nightly"
|
autoUpdater.channel = channel; // "latest" | "nightly"
|
||||||
autoUpdater.allowDowngrade = true; // permits a nightly -> stable channel switch
|
autoUpdater.allowDowngrade = true; // permits a nightly -> stable channel switch
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue