Disable updater until we can debug more in release (#2908)

This commit is contained in:
Zane
2025-06-13 12:20:10 -07:00
committed by GitHub
parent fe16789776
commit e7d38c6023
6 changed files with 388 additions and 158 deletions
@@ -1,7 +1,6 @@
import { useState, useEffect, useRef } from 'react';
import { Switch } from '../../ui/switch';
import UpdateSection from './UpdateSection';
import { UPDATES_ENABLED } from '../../../updates';
interface AppSettingsSectionProps {
scrollToSection?: string;
@@ -12,6 +11,7 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti
const [dockIconEnabled, setDockIconEnabled] = useState(true);
const [isMacOS, setIsMacOS] = useState(false);
const [isDockSwitchDisabled, setIsDockSwitchDisabled] = useState(false);
const [updatesEnabled, setUpdatesEnabled] = useState(false);
const updateSectionRef = useRef<HTMLDivElement>(null);
// Check if running on macOS
@@ -19,6 +19,25 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti
setIsMacOS(window.electron.platform === 'darwin');
}, []);
// Load updater state
useEffect(() => {
window.electron.getUpdaterEnabled().then((enabled) => {
setUpdatesEnabled(enabled);
});
// Listen for updater state changes
const handleUpdaterStateChange = (enabled: boolean) => {
setUpdatesEnabled(enabled);
};
window.electron.onUpdaterStateChanged(handleUpdaterStateChange);
// Cleanup listener on unmount
return () => {
window.electron.removeUpdaterStateListener(handleUpdaterStateChange);
};
}, []);
// Handle scrolling to update section
useEffect(() => {
if (scrollToSection === 'update' && updateSectionRef.current) {
@@ -125,7 +144,7 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti
</div>
{/* Update Section */}
{UPDATES_ENABLED && (
{updatesEnabled && (
<div ref={updateSectionRef} className="mt-8 pt-8 border-t border-gray-200">
<UpdateSection />
</div>
+61 -2
View File
@@ -37,12 +37,31 @@ import * as yaml from 'yaml';
import windowStateKeeper from 'electron-window-state';
import {
setupAutoUpdater,
registerUpdateIpcHandlers,
setTrayRef,
updateTrayMenu,
getUpdateAvailable,
} from './utils/autoUpdater';
import { UPDATES_ENABLED } from './updates';
// Updater toggle functions (moved here to keep updates.ts minimal for release replacement)
let updatesEnabled = UPDATES_ENABLED;
function toggleUpdates(): boolean {
updatesEnabled = !updatesEnabled;
return updatesEnabled;
}
function getUpdatesEnabled(): boolean {
// Only return the toggle state, ignore ENABLE_DEV_UPDATES for UI visibility
return updatesEnabled;
}
function shouldSetupUpdater(): boolean {
// Setup updater if either the toggle is enabled OR dev updates are enabled
return updatesEnabled || process.env.ENABLE_DEV_UPDATES === 'true';
}
// Define temp directory for pasted images
const gooseTempDir = path.join(app.getPath('temp'), 'goose-pasted-images');
@@ -1157,8 +1176,11 @@ const registerGlobalHotkey = (accelerator: string) => {
};
app.whenReady().then(async () => {
// Setup auto-updater
UPDATES_ENABLED && setupAutoUpdater();
// Register update IPC handlers once
registerUpdateIpcHandlers();
// Setup auto-updater if enabled
shouldSetupUpdater() && setupAutoUpdater();
// Add CSP headers to all sessions
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
@@ -1200,6 +1222,38 @@ app.whenReady().then(async () => {
// Register the default global hotkey
registerGlobalHotkey('CommandOrControl+Alt+Shift+G');
// Register hidden key combination to toggle updater (Cmd+Shift+U+P+D+A+T+E)
globalShortcut.register('CommandOrControl+Shift+U', () => {
// This is a multi-key sequence, we'll use a simpler approach
// Register a hidden key combination: Cmd/Ctrl + Alt + Shift + U for "Update toggle"
const newState = toggleUpdates();
log.info(
`Updater toggled via keyboard shortcut. New state: ${newState ? 'ENABLED' : 'DISABLED'}`
);
// Show a notification to the user
new Notification({
title: 'Goose Updater',
body: `Updates ${newState ? 'enabled' : 'disabled'}`,
}).show();
// If we're enabling updates and haven't set up the auto-updater yet, set it up now
if (newState) {
try {
setupAutoUpdater(tray || undefined);
log.info('Auto-updater setup completed after keyboard toggle');
} catch (error) {
log.error('Error setting up auto-updater after keyboard toggle:', error);
}
}
// Notify all windows about the updater state change
const windows = BrowserWindow.getAllWindows();
windows.forEach((win) => {
win.webContents.send('updater-state-changed', newState);
});
});
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
details.requestHeaders['Origin'] = 'http://localhost:5173';
callback({ cancel: false, requestHeaders: details.requestHeaders });
@@ -1610,6 +1664,11 @@ app.whenReady().then(async () => {
ipcMain.on('get-app-version', (event) => {
event.returnValue = app.getVersion();
});
// Handler for getting updater state
ipcMain.handle('get-updater-enabled', () => {
return getUpdatesEnabled();
});
});
/**
+26
View File
@@ -89,6 +89,10 @@ type ElectronAPI = {
restartApp: () => void;
onUpdaterEvent: (callback: (event: UpdaterEvent) => void) => void;
getUpdateState: () => Promise<{ updateAvailable: boolean; latestVersion?: string } | null>;
// Updater state functions
getUpdaterEnabled: () => Promise<boolean>;
onUpdaterStateChanged: (callback: (enabled: boolean) => void) => void;
removeUpdaterStateListener: (callback: (enabled: boolean) => void) => void;
};
type AppConfigAPI = {
@@ -96,6 +100,12 @@ type AppConfigAPI = {
getAll: () => Record<string, unknown>;
};
// Store callback wrappers for proper cleanup
const updaterStateCallbacks = new Map<
(enabled: boolean) => void,
(event: Electron.IpcRendererEvent, enabled: boolean) => void
>();
const electronAPI: ElectronAPI = {
platform: process.platform,
reactReady: () => ipcRenderer.send('react-ready'),
@@ -183,6 +193,22 @@ const electronAPI: ElectronAPI = {
getUpdateState: (): Promise<{ updateAvailable: boolean; latestVersion?: string } | null> => {
return ipcRenderer.invoke('get-update-state');
},
// Updater state functions
getUpdaterEnabled: (): Promise<boolean> => {
return ipcRenderer.invoke('get-updater-enabled');
},
onUpdaterStateChanged: (callback: (enabled: boolean) => void): void => {
const wrapper = (_event: Electron.IpcRendererEvent, enabled: boolean) => callback(enabled);
updaterStateCallbacks.set(callback, wrapper);
ipcRenderer.on('updater-state-changed', wrapper);
},
removeUpdaterStateListener: (callback: (enabled: boolean) => void): void => {
const wrapper = updaterStateCallbacks.get(callback);
if (wrapper) {
ipcRenderer.off('updater-state-changed', wrapper);
updaterStateCallbacks.delete(callback);
}
},
};
const appConfigAPI: AppConfigAPI = {
+1 -1
View File
@@ -1 +1 @@
export const UPDATES_ENABLED = true;
export const UPDATES_ENABLED = false;
+248 -153
View File
@@ -30,189 +30,68 @@ let githubUpdateInfo: {
// Store update state
let lastUpdateState: { updateAvailable: boolean; latestVersion?: string } | null = null;
// Configure auto-updater
export function setupAutoUpdater(tray?: Tray) {
if (tray) {
trayRef = tray;
// Track if IPC handlers have been registered
let ipcUpdateHandlersRegistered = false;
// Register IPC handlers (only once)
export function registerUpdateIpcHandlers() {
if (ipcUpdateHandlersRegistered) {
return;
}
// Set the feed URL for GitHub releases
autoUpdater.setFeedURL({
provider: 'github',
owner: 'block',
repo: 'goose',
releaseType: 'release',
});
// Configure auto-updater settings
autoUpdater.autoDownload = false; // We'll trigger downloads manually
autoUpdater.autoInstallOnAppQuit = true;
// Enable updates in development mode for testing
if (process.env.ENABLE_DEV_UPDATES === 'true') {
autoUpdater.forceDevUpdateConfig = true;
}
// Set logger
autoUpdater.logger = log;
// Check for updates on startup
setTimeout(() => {
log.info('Checking for updates on startup...');
autoUpdater.checkForUpdates().catch((err) => {
log.error('Error checking for updates on startup:', err);
// If electron-updater fails, try GitHub API as fallback
if (
err.message.includes('HttpError: 404') ||
err.message.includes('ERR_CONNECTION_REFUSED') ||
err.message.includes('ENOTFOUND')
) {
log.info('Using GitHub API fallback for startup update check...');
isUsingGitHubFallback = true;
githubUpdater
.checkForUpdates()
.then((result) => {
if (result.error) {
sendStatusToWindow('error', result.error);
} else if (result.updateAvailable) {
// Store GitHub update info
githubUpdateInfo = {
latestVersion: result.latestVersion,
downloadUrl: result.downloadUrl,
releaseUrl: result.releaseUrl,
};
updateAvailable = true;
lastUpdateState = { updateAvailable: true, latestVersion: result.latestVersion };
updateTrayIcon(true);
sendStatusToWindow('update-available', { version: result.latestVersion });
} else {
updateAvailable = false;
lastUpdateState = { updateAvailable: false };
updateTrayIcon(false);
sendStatusToWindow('update-not-available', {
version: autoUpdater.currentVersion.version,
});
}
})
.catch((fallbackError) => {
log.error('GitHub fallback also failed on startup:', fallbackError);
});
}
});
}, 5000); // Wait 5 seconds after app starts
// Handle update events
autoUpdater.on('checking-for-update', () => {
log.info('Checking for update...');
sendStatusToWindow('checking-for-update');
});
autoUpdater.on('update-available', (info: UpdateInfo) => {
log.info('Update available:', info);
updateAvailable = true;
lastUpdateState = { updateAvailable: true, latestVersion: info.version };
updateTrayIcon(true);
sendStatusToWindow('update-available', info);
});
autoUpdater.on('update-not-available', (info: UpdateInfo) => {
log.info('Update not available:', info);
updateAvailable = false;
lastUpdateState = { updateAvailable: false };
updateTrayIcon(false);
sendStatusToWindow('update-not-available', info);
});
autoUpdater.on('error', async (err) => {
log.error('Error in auto-updater:', err);
// Check if this is a 404 error (missing update files) or connection error
if (
err.message.includes('HttpError: 404') ||
err.message.includes('ERR_CONNECTION_REFUSED') ||
err.message.includes('ENOTFOUND')
) {
log.info('Falling back to GitHub API for update check...');
isUsingGitHubFallback = true;
try {
const result = await githubUpdater.checkForUpdates();
if (result.error) {
sendStatusToWindow('error', result.error);
} else if (result.updateAvailable) {
// Store GitHub update info
githubUpdateInfo = {
latestVersion: result.latestVersion,
downloadUrl: result.downloadUrl,
releaseUrl: result.releaseUrl,
};
updateAvailable = true;
updateTrayIcon(true);
sendStatusToWindow('update-available', { version: result.latestVersion });
} else {
updateAvailable = false;
updateTrayIcon(false);
sendStatusToWindow('update-not-available', {
version: autoUpdater.currentVersion.version,
});
}
} catch (fallbackError) {
log.error('GitHub fallback also failed:', fallbackError);
sendStatusToWindow(
'error',
'Unable to check for updates. Please check your internet connection.'
);
}
} else {
sendStatusToWindow('error', err.message);
}
});
autoUpdater.on('download-progress', (progressObj) => {
let log_message = 'Download speed: ' + progressObj.bytesPerSecond;
log_message = log_message + ' - Downloaded ' + progressObj.percent + '%';
log_message = log_message + ' (' + progressObj.transferred + '/' + progressObj.total + ')';
log.info(log_message);
sendStatusToWindow('download-progress', progressObj);
});
autoUpdater.on('update-downloaded', (info: UpdateInfo) => {
log.info('Update downloaded:', info);
sendStatusToWindow('update-downloaded', info);
});
log.info('Registering update IPC handlers...');
ipcUpdateHandlersRegistered = true;
// IPC handlers for renderer process
ipcMain.handle('check-for-updates', async () => {
try {
log.info('Manual check for updates requested');
// Reset fallback flag
isUsingGitHubFallback = false;
githubUpdateInfo = {};
// Ensure auto-updater is properly initialized
if (!autoUpdater.currentVersion) {
log.error('Auto-updater currentVersion is null/undefined');
throw new Error('Auto-updater not initialized. Please restart the application.');
}
log.info(
`About to check for updates with currentVersion: ${JSON.stringify(autoUpdater.currentVersion)}`
);
log.info(`Feed URL: ${autoUpdater.getFeedURL()}`);
const result = await autoUpdater.checkForUpdates();
log.info('Auto-updater checkForUpdates result:', result);
return {
updateInfo: result?.updateInfo,
error: null,
};
} catch (error) {
log.error('Error checking for updates:', error);
log.error('Manual check error details:', {
message: error instanceof Error ? error.message : 'Unknown error',
stack: error instanceof Error ? error.stack : 'No stack',
name: error instanceof Error ? error.name : 'Unknown',
code:
error instanceof Error && 'code' in error
? (error as Error & { code: unknown }).code
: undefined,
toString: error?.toString(),
});
// If electron-updater fails, try GitHub API fallback
if (
error instanceof Error &&
(error.message.includes('HttpError: 404') ||
error.message.includes('ERR_CONNECTION_REFUSED') ||
error.message.includes('ENOTFOUND'))
error.message.includes('ENOTFOUND') ||
error.message.includes('No published versions'))
) {
log.info('Using GitHub API fallback in check-for-updates...');
log.info('Manual fallback triggered by error:', error.message);
isUsingGitHubFallback = true;
try {
@@ -365,6 +244,222 @@ export function setupAutoUpdater(tray?: Tray) {
});
}
// Configure auto-updater
export function setupAutoUpdater(tray?: Tray) {
if (tray) {
trayRef = tray;
}
log.info('Setting up auto-updater...');
log.info(`Current app version: ${app.getVersion()}`);
log.info(`Platform: ${process.platform}, Arch: ${process.arch}`);
log.info(`NODE_ENV: ${process.env.NODE_ENV}`);
log.info(`ENABLE_DEV_UPDATES: ${process.env.ENABLE_DEV_UPDATES}`);
log.info(`App is packaged: ${app.isPackaged}`);
log.info(`App path: ${app.getAppPath()}`);
log.info(`Resources path: ${process.resourcesPath}`);
// Set the feed URL for GitHub releases
const feedConfig = {
provider: 'github' as const,
owner: 'block',
repo: 'goose',
releaseType: 'release' as const,
};
log.info('Setting feed URL with config:', feedConfig);
autoUpdater.setFeedURL(feedConfig);
// Log the feed URL after setting it
try {
const feedUrl = autoUpdater.getFeedURL();
log.info(`Feed URL set to: ${feedUrl}`);
} catch (e) {
log.error('Error getting feed URL:', e);
}
// Configure auto-updater settings
autoUpdater.autoDownload = false; // We'll trigger downloads manually
autoUpdater.autoInstallOnAppQuit = true;
// Enable updates in development mode for testing
if (process.env.ENABLE_DEV_UPDATES === 'true') {
log.info('Enabling dev updates config');
autoUpdater.forceDevUpdateConfig = true;
}
// Additional debugging for release builds
if (app.isPackaged) {
log.info('App is packaged - this is a release build');
// Try to get more info about the updater configuration
try {
log.info(`Auto-updater channel: ${autoUpdater.channel}`);
log.info(`Auto-updater allowPrerelease: ${autoUpdater.allowPrerelease}`);
log.info(`Auto-updater allowDowngrade: ${autoUpdater.allowDowngrade}`);
} catch (e) {
log.error('Error getting auto-updater properties:', e);
}
} else {
log.info('App is not packaged - this is a development build');
}
// Set logger
autoUpdater.logger = log;
log.info('Auto-updater setup completed');
// Check for updates on startup
setTimeout(() => {
log.info('Checking for updates on startup...');
log.info(`autoUpdater.currentVersion: ${JSON.stringify(autoUpdater.currentVersion)}`);
log.info(`autoUpdater.getFeedURL(): ${autoUpdater.getFeedURL()}`);
autoUpdater.checkForUpdates().catch((err) => {
log.error('Error checking for updates on startup:', err);
log.error('Error details:', {
message: err.message,
stack: err.stack,
name: err.name,
code: 'code' in err ? err.code : undefined,
});
// If electron-updater fails, try GitHub API as fallback
if (
err.message.includes('HttpError: 404') ||
err.message.includes('ERR_CONNECTION_REFUSED') ||
err.message.includes('ENOTFOUND') ||
err.message.includes('No published versions')
) {
log.info('Using GitHub API fallback for startup update check...');
log.info('Fallback triggered by error containing:', err.message);
isUsingGitHubFallback = true;
githubUpdater
.checkForUpdates()
.then((result) => {
if (result.error) {
sendStatusToWindow('error', result.error);
} else if (result.updateAvailable) {
// Store GitHub update info
githubUpdateInfo = {
latestVersion: result.latestVersion,
downloadUrl: result.downloadUrl,
releaseUrl: result.releaseUrl,
};
updateAvailable = true;
lastUpdateState = { updateAvailable: true, latestVersion: result.latestVersion };
updateTrayIcon(true);
sendStatusToWindow('update-available', { version: result.latestVersion });
} else {
updateAvailable = false;
lastUpdateState = { updateAvailable: false };
updateTrayIcon(false);
sendStatusToWindow('update-not-available', {
version: autoUpdater.currentVersion.version,
});
}
})
.catch((fallbackError) => {
log.error('GitHub fallback also failed on startup:', fallbackError);
});
}
});
}, 5000); // Wait 5 seconds after app starts
// Handle update events
autoUpdater.on('checking-for-update', () => {
log.info('Auto-updater: Checking for update...');
log.info(`Auto-updater: Feed URL during check: ${autoUpdater.getFeedURL()}`);
sendStatusToWindow('checking-for-update');
});
autoUpdater.on('update-available', (info: UpdateInfo) => {
log.info('Update available:', info);
updateAvailable = true;
lastUpdateState = { updateAvailable: true, latestVersion: info.version };
updateTrayIcon(true);
sendStatusToWindow('update-available', info);
});
autoUpdater.on('update-not-available', (info: UpdateInfo) => {
log.info('Update not available:', info);
updateAvailable = false;
lastUpdateState = { updateAvailable: false };
updateTrayIcon(false);
sendStatusToWindow('update-not-available', info);
});
autoUpdater.on('error', async (err) => {
log.error('Error in auto-updater:', err);
log.error('Auto-updater error details:', {
message: err.message,
stack: err.stack,
name: err.name,
code: 'code' in err ? err.code : undefined,
toString: err.toString(),
});
// Check if this is a 404 error (missing update files) or connection error
if (
err.message.includes('HttpError: 404') ||
err.message.includes('ERR_CONNECTION_REFUSED') ||
err.message.includes('ENOTFOUND') ||
err.message.includes('No published versions')
) {
log.info('Falling back to GitHub API for update check...');
log.info('Fallback triggered by error:', err.message);
isUsingGitHubFallback = true;
try {
const result = await githubUpdater.checkForUpdates();
if (result.error) {
sendStatusToWindow('error', result.error);
} else if (result.updateAvailable) {
// Store GitHub update info
githubUpdateInfo = {
latestVersion: result.latestVersion,
downloadUrl: result.downloadUrl,
releaseUrl: result.releaseUrl,
};
updateAvailable = true;
updateTrayIcon(true);
sendStatusToWindow('update-available', { version: result.latestVersion });
} else {
updateAvailable = false;
updateTrayIcon(false);
sendStatusToWindow('update-not-available', {
version: autoUpdater.currentVersion.version,
});
}
} catch (fallbackError) {
log.error('GitHub fallback also failed:', fallbackError);
sendStatusToWindow(
'error',
'Unable to check for updates. Please check your internet connection.'
);
}
} else {
sendStatusToWindow('error', err.message);
}
});
autoUpdater.on('download-progress', (progressObj) => {
let log_message = 'Download speed: ' + progressObj.bytesPerSecond;
log_message = log_message + ' - Downloaded ' + progressObj.percent + '%';
log_message = log_message + ' (' + progressObj.transferred + '/' + progressObj.total + ')';
log.info(log_message);
sendStatusToWindow('download-progress', progressObj);
});
autoUpdater.on('update-downloaded', (info: UpdateInfo) => {
log.info('Update downloaded:', info);
sendStatusToWindow('update-downloaded', info);
});
}
interface UpdaterEvent {
event: string;
data?: unknown;
+31
View File
@@ -34,6 +34,8 @@ export class GitHubUpdater {
async checkForUpdates(): Promise<UpdateCheckResult> {
try {
log.info('GitHubUpdater: Checking for updates via GitHub API...');
log.info(`GitHubUpdater: API URL: ${this.apiUrl}`);
log.info(`GitHubUpdater: Current app version: ${app.getVersion()}`);
const response = await fetch(this.apiUrl, {
headers: {
@@ -42,11 +44,21 @@ export class GitHubUpdater {
},
});
log.info(
`GitHubUpdater: GitHub API response status: ${response.status} ${response.statusText}`
);
if (!response.ok) {
const errorText = await response.text();
log.error(`GitHubUpdater: GitHub API error response: ${errorText}`);
throw new Error(`GitHub API returned ${response.status}: ${response.statusText}`);
}
const release: GitHubRelease = await response.json();
log.info(`GitHubUpdater: Found release: ${release.tag_name} (${release.name})`);
log.info(`GitHubUpdater: Release published at: ${release.published_at}`);
log.info(`GitHubUpdater: Release assets count: ${release.assets.length}`);
const latestVersion = release.tag_name.replace(/^v/, ''); // Remove 'v' prefix if present
const currentVersion = app.getVersion();
@@ -56,6 +68,7 @@ export class GitHubUpdater {
// Compare versions
const updateAvailable = compareVersions(latestVersion, currentVersion) > 0;
log.info(`GitHubUpdater: Update available: ${updateAvailable}`);
if (!updateAvailable) {
return {
@@ -70,6 +83,8 @@ export class GitHubUpdater {
let downloadUrl: string | undefined;
let assetName: string;
log.info(`GitHubUpdater: Looking for asset for platform: ${platform}, arch: ${arch}`);
if (platform === 'darwin') {
// macOS
if (arch === 'arm64') {
@@ -85,9 +100,16 @@ export class GitHubUpdater {
assetName = `Goose-linux-${arch}.zip`;
}
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);
if (asset) {
downloadUrl = asset.browser_download_url;
log.info(`GitHubUpdater: Found matching asset: ${asset.name} (${asset.size} bytes)`);
log.info(`GitHubUpdater: Download URL: ${downloadUrl}`);
} else {
log.warn(`GitHubUpdater: No matching asset found for ${assetName}`);
}
return {
@@ -98,6 +120,15 @@ export class GitHubUpdater {
};
} catch (error) {
log.error('GitHubUpdater: Error checking for updates:', error);
log.error('GitHubUpdater: Error details:', {
message: error instanceof Error ? error.message : 'Unknown error',
stack: error instanceof Error ? error.stack : 'No stack',
name: error instanceof Error ? error.name : 'Unknown',
code:
error instanceof Error && 'code' in error
? (error as Error & { code: unknown }).code
: undefined,
});
return {
updateAvailable: false,
error: error instanceof Error ? error.message : 'Unknown error',