authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-04 00:27:47-08:00
committergravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-04 00:27:47-08:00
log65a808808f85a4e15e341c1a20dc574f20f9570b
tree324b264622e3339e4d8bd8e2fea2fed5b5b4c883
parentcca1aa15882ed48502d66c4b878aa366b1540c98

Fix electron-updater auth for private GitHub repos

Use setFeedURL with private: true when a GitHub token is provided so electron-updater queries the GitHub API instead of the public Atom feed which rejects Bearer auth headers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

1 files changed, 34 insertions(+), 4 deletions(-)

apps/desktop/src/main.ts+34-4
...@@ -253,6 +253,28 @@ function resolveAppRoot(): string {...@@ -253,6 +253,28 @@ function resolveAppRoot(): string {
253 return app.getAppPath();253 return app.getAppPath();
254}254}
255255
256/** Read the baked-in app-update.yml config (if applicable). */
257function readAppUpdateYml(): Record<string, string> | null {
258 try {
259 // electron-updater reads from process.resourcesPath in packaged builds,
260 // or dev-app-update.yml via app.getAppPath() in dev.
261 const ymlPath = app.isPackaged
262 ? Path.join(process.resourcesPath, "app-update.yml")
263 : Path.join(app.getAppPath(), "dev-app-update.yml");
264 const raw = FS.readFileSync(ymlPath, "utf-8");
265 // The YAML is simple key-value pairs — avoid pulling in a YAML parser by
266 // doing a line-based parse (fields: provider, owner, repo, releaseType, …).
267 const entries: Record<string, string> = {};
268 for (const line of raw.split("\n")) {
269 const match = line.match(/^(\w+):\s*(.+)$/);
270 if (match?.[1] && match[2]) entries[match[1]] = match[2].trim();
271 }
272 return entries.provider ? entries : null;
273 } catch {
274 return null;
275 }
276}
277
256function normalizeCommitHash(value: unknown): string | null {278function normalizeCommitHash(value: unknown): string | null {
257 if (typeof value !== "string") {279 if (typeof value !== "string") {
258 return null;280 return null;
...@@ -687,10 +709,18 @@ function configureAutoUpdater(): void {...@@ -687,10 +709,18 @@ function configureAutoUpdater(): void {
687 process.env.GH_TOKEN?.trim() ||709 process.env.GH_TOKEN?.trim() ||
688 "";710 "";
689 if (githubToken) {711 if (githubToken) {
690 autoUpdater.requestHeaders = {712 // When a token is provided, re-configure the feed with `private: true` so
691 ...autoUpdater.requestHeaders,713 // electron-updater uses the GitHub API (api.github.com) instead of the
692 Authorization: `Bearer ${githubToken}`,714 // public Atom feed (github.com/…/releases.atom) which rejects Bearer auth.
693 };715 const appUpdateYml = readAppUpdateYml();
716 if (appUpdateYml?.provider === "github") {
717 autoUpdater.setFeedURL({
718 ...appUpdateYml,
719 provider: "github" as const,
720 private: true,
721 token: githubToken,
722 });
723 }
694 }724 }
695725
696 autoUpdater.autoDownload = false;726 autoUpdater.autoDownload = false;