Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 13 additions & 34 deletions ui/desktop/src/components/settings/app/UpdateSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,29 +123,6 @@ export default function UpdateSection() {
}
};

const downloadAndInstallUpdate = async () => {
setUpdateStatus('downloading');
setProgress(0);

try {
const result = await window.electron.downloadUpdate();

if (!result.success) {
throw new Error(result.error || 'Failed to download update');
}

// The download progress and completion will be handled by updater events
} catch (error) {
console.error('Error downloading update:', error);
setUpdateInfo((prev) => ({
...prev,
error: error instanceof Error ? error.message : 'Failed to download update',
}));
setUpdateStatus('error');
setTimeout(() => setUpdateStatus('idle'), 5000);
}
};

Comment on lines -126 to -148
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed as downloads are automatic now

const installUpdate = () => {
window.electron.installUpdate();
};
Expand Down Expand Up @@ -216,13 +193,6 @@ export default function UpdateSection() {
Check for Updates
</Button>

{updateInfo.isUpdateAvailable && updateStatus === 'idle' && (
<Button onClick={downloadAndInstallUpdate} variant="secondary" size="sm">
<Download className="w-3 h-3 mr-1" />
Download Update
</Button>
)}

{updateStatus === 'ready' && (
<Button onClick={installUpdate} variant="default" size="sm">
Install & Restart
Expand All @@ -247,12 +217,21 @@ export default function UpdateSection() {
)}

{/* Update information */}
{updateInfo.isUpdateAvailable && (
{updateInfo.isUpdateAvailable && updateStatus === 'idle' && (
<div className="text-xs text-text-muted mt-4 space-y-1">
<p>Update will be downloaded automatically in the background.</p>
<p className="text-xs text-green-600">
The update will be installed automatically when you quit the app.
</p>
</div>
)}

Comment on lines +220 to +228
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected behavior for signed build

{updateStatus === 'ready' && (
<div className="text-xs text-text-muted mt-4 space-y-1">
<p>Update will be downloaded to your Downloads folder.</p>
<p className="text-xs text-amber-600">
Note: After downloading, you'll need to close the app and manually install the update.
<p className="text-xs text-green-600">
Update is ready! It will be installed when you quit Goose.
</p>
<p className="text-xs text-text-muted">Or click "Install & Restart" to update now.</p>
</div>
)}
</div>
Expand Down
62 changes: 60 additions & 2 deletions ui/desktop/src/utils/autoUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
dialog,
Menu,
MenuItemConstructorOptions,
Notification,
} from 'electron';
import * as path from 'path';
import * as fs from 'fs/promises';
Expand Down Expand Up @@ -116,6 +117,10 @@ export function registerUpdateIpcHandlers() {
lastUpdateState = { updateAvailable: true, latestVersion: result.latestVersion };
updateTrayIcon(true);
sendStatusToWindow('update-available', { version: result.latestVersion });

// Auto-download for GitHub fallback (matching autoDownload behavior)
log.info('Auto-downloading update via GitHub fallback...');
await githubAutoDownload(result.downloadUrl!, result.latestVersion!, 'manual check');
} else {
updateAvailable = false;
lastUpdateState = { updateAvailable: false };
Expand Down Expand Up @@ -274,7 +279,7 @@ export function setupAutoUpdater(tray?: Tray) {
}

// Configure auto-updater settings
autoUpdater.autoDownload = false; // We'll trigger downloads manually
autoUpdater.autoDownload = true; // Automatically download updates when available
autoUpdater.autoInstallOnAppQuit = true;

// Enable updates in development mode for testing
Expand Down Expand Up @@ -331,7 +336,7 @@ export function setupAutoUpdater(tray?: Tray) {

githubUpdater
.checkForUpdates()
.then((result) => {
.then(async (result) => {
if (result.error) {
sendStatusToWindow('error', result.error);
} else if (result.updateAvailable) {
Expand All @@ -346,6 +351,10 @@ export function setupAutoUpdater(tray?: Tray) {
lastUpdateState = { updateAvailable: true, latestVersion: result.latestVersion };
updateTrayIcon(true);
sendStatusToWindow('update-available', { version: result.latestVersion });

// Auto-download for GitHub fallback (matching autoDownload behavior)
log.info('Auto-downloading update via GitHub fallback on startup...');
await githubAutoDownload(result.downloadUrl!, result.latestVersion!, 'on startup');
} else {
updateAvailable = false;
lastUpdateState = { updateAvailable: false };
Expand Down Expand Up @@ -422,6 +431,10 @@ export function setupAutoUpdater(tray?: Tray) {
updateAvailable = true;
updateTrayIcon(true);
sendStatusToWindow('update-available', { version: result.latestVersion });

// Auto-download for GitHub fallback (matching autoDownload behavior)
log.info('Auto-downloading update via GitHub fallback after error...');
await githubAutoDownload(result.downloadUrl!, result.latestVersion!, 'after error');
} else {
updateAvailable = false;
updateTrayIcon(false);
Expand Down Expand Up @@ -452,6 +465,18 @@ export function setupAutoUpdater(tray?: Tray) {
autoUpdater.on('update-downloaded', (info: UpdateInfo) => {
log.info('Update downloaded:', info);
sendStatusToWindow('update-downloaded', info);

// Show native notification
const notification = new Notification({
title: 'Update Ready',
body: `Version ${info.version} will be installed when you quit Goose. Click to install now.`,
});
notification.show();

// Optional: Add click handler to install immediately
notification.on('click', () => {
autoUpdater.quitAndInstall(false, true);
});
});
}

Expand All @@ -467,6 +492,39 @@ function sendStatusToWindow(event: string, data?: unknown) {
});
}

// centralize GitHub fallback auto-download logic.
async function githubAutoDownload(
downloadUrl: string,
latestVersion: string,
contextLabel = ''
): Promise<void> {
try {
const downloadResult = await githubUpdater.downloadUpdate(
downloadUrl,
latestVersion,
(percent) => {
sendStatusToWindow('download-progress', { percent });
}
);

if (downloadResult.success && downloadResult.downloadPath) {
githubUpdateInfo.downloadPath = downloadResult.downloadPath;
githubUpdateInfo.extractedPath = downloadResult.extractedPath;
sendStatusToWindow('update-downloaded', { version: latestVersion });
} else {
log.error(
`GitHub auto-download failed${contextLabel ? ` (${contextLabel})` : ''}:`,
downloadResult.error
);
}
} catch (downloadError) {
log.error(
`Error during GitHub auto-download${contextLabel ? ` (${contextLabel})` : ''}:`,
downloadError
);
}
}

function updateTrayIcon(hasUpdate: boolean) {
if (!trayRef) return;

Expand Down
8 changes: 7 additions & 1 deletion ui/desktop/src/utils/githubUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export class GitHubUpdater {
log.info(`GitHubUpdater: Looking for asset named: ${assetName}`);
log.info(`GitHubUpdater: Available assets: ${release.assets.map((a) => a.name).join(', ')}`);

const asset = release.assets.find((a) => a.name === assetName);
const asset = release.assets.find((a) => a.name.toLowerCase() === assetName); // keeping comparisms to lower case becasue Goose vs goose
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought about either changing the hardcoded values in lines 94-103 or keeping this normalized comparison. Let me know if i should change the hardcoded values instead.

if (asset) {
downloadUrl = asset.browser_download_url;
log.info(`GitHubUpdater: Found matching asset: ${asset.name} (${asset.size} bytes)`);
Expand All @@ -115,6 +115,12 @@ export class GitHubUpdater {
log.warn(`GitHubUpdater: No matching asset found for ${assetName}`);
}

if (!downloadUrl) {
Copy link
Contributor Author

@AdemolaAri AdemolaAri Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edge case - update is available but no asset match.
TLDR; App detects there is a new version to be downloaded, code block on line 94 assetName = 'goose.zip'; but the build for that platform has Goose.zip. Due to uppercase - lowercase 'G' no downloadUrl set and downloading a new version keeps failing cause no url to hit.

throw new Error(
`Update Available but no download URL found for platform: ${platform}, arch: ${arch}`
);
}

return {
updateAvailable: true,
latestVersion,
Expand Down
Loading