diff --git a/ELECTRON_UPGRADE_FIXES.md b/ELECTRON_UPGRADE_FIXES.md new file mode 100644 index 0000000..8831828 --- /dev/null +++ b/ELECTRON_UPGRADE_FIXES.md @@ -0,0 +1,94 @@ +# Electron Upgrade Feature - Bug Fixes + +## Problem Identified +The upgrade feature was downloading and installing new Electron versions successfully, but the app always showed the old version (1.0.0) after restart because: + +1. **Version Source Issue**: The app was reading `app.getVersion()` which gets the version from `package.json` at startup time +2. **Package.json Not Re-read**: Even after npm installed a new Electron version, the app didn't re-read the updated `package.json` +3. **Runtime Display**: The About tab showed the bundled Electron version (37.x) which is baked into the binary at build time + +## Solutions Implemented + +### 1. **New Helper Function: `getInstalledElectronVersion()`** +- Reads `package.json` directly every time it's called (not cached) +- Extracts the actual installed Electron version from `devDependencies` +- Handles both stable (`electron`) and nightly (`electron-nightly`) packages +- Strips version specifiers (^, ~, etc.) to get the clean version number +- Falls back to `app.getVersion()` if reading fails + +```javascript +function getInstalledElectronVersion() { + try { + const packageJsonPath = path.join(__dirname, 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + + const electronDep = packageJson.devDependencies?.electron; + const electronNightlyDep = packageJson.devDependencies?.['electron-nightly']; + + if (electronDep) { + return electronDep.replace(/^\D+/, ''); + } + if (electronNightlyDep) { + return electronNightlyDep.replace(/^\D+/, ''); + } + return app.getVersion(); + } catch (err) { + return app.getVersion(); + } +} +``` + +### 2. **Updated `get-electron-versions` Handler** +- Now uses `getInstalledElectronVersion()` instead of `app.getVersion()` +- Returns the actual installed version that was modified by npm +- Performs fresh version checks each time (no caching) + +### 3. **Improved `upgrade-electron` Handler** +- Increased `maxBuffer` to handle large npm output +- Added cleanup logic to remove the old Electron variant when switching types + - Removes `electron` when upgrading to `nightly` + - Removes `electron-nightly` when upgrading to `stable` +- Better error logging to debug npm failures +- Returns clearer messages about installation status + +### 4. **Enhanced UI/UX in settings.js** +- Added more descriptive status text ("Downloading and installing..." instead of just "Upgrading...") +- Disables all controls during upgrade to prevent multiple clicks +- Reduced restart delay from 2000ms to 1500ms for faster feedback +- Better error handling with proper cleanup of disabled states + +## How It Works Now + +1. **User clicks "Check for Updates"** + - Queries npm registry for latest version + - Uses `getInstalledElectronVersion()` to read current version from `package.json` + - Compares versions and shows if update is available + +2. **User clicks "Upgrade Electron"** + - Confirms action + - Runs `npm install --save-dev electron@latest` (or `electron-nightly@latest`) + - npm downloads and installs new version + - Handler removes the other Electron variant from `package.json` if needed + - Shows success message + +3. **App Restarts** + - Uses `app.relaunch()` and `app.quit()` + - When app relaunches, it: + - Loads new Electron binary from `node_modules` + - Runs new Electron version + - Settings page shows correct new version on next check + +## Testing Recommendations + +1. Test upgrading from stable to nightly version +2. Test upgrading from nightly back to stable +3. Verify version display updates after restart +4. Check that old variant is removed from `package.json` +5. Verify app runs stably with new Electron version + +## Notes for Future Development + +- The About tab displays `process.versions.electron` which is the bundled Chromium version, not the Electron framework version +- The Electron version we display in the upgrade section comes from `package.json` which is the actual framework version +- When building with electron-builder, the bundled version becomes fixed until next rebuild +- For development/testing, the upgrade feature reads live from `package.json` diff --git a/main.js b/main.js index cd0f3e2..50beb6b 100644 --- a/main.js +++ b/main.js @@ -666,6 +666,150 @@ ipcMain.handle('open-devtools', (event) => { return contents.isDevToolsOpened(); }); +// Helper function to read package.json version +function getInstalledElectronVersion() { + try { + const packageJsonPath = path.join(__dirname, 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + + // Get the version from devDependencies + const electronDep = packageJson.devDependencies?.electron; + const electronNightlyDep = packageJson.devDependencies?.['electron-nightly']; + + if (electronDep) { + return electronDep.replace(/^\D+/, ''); // Remove ^ or ~ or other version specifiers + } + if (electronNightlyDep) { + return electronNightlyDep.replace(/^\D+/, ''); + } + + return app.getVersion(); + } catch (err) { + console.error('Error reading installed electron version:', err); + return app.getVersion(); + } +} + +// Electron version management handlers +ipcMain.handle('get-electron-versions', async (event, buildType = 'stable') => { + const https = require('https'); + + return new Promise((resolve) => { + let url; + + if (buildType === 'nightly') { + // Get latest nightly version from npm + url = 'https://registry.npmjs.org/electron-nightly/latest'; + } else { + // Get latest stable version from npm + url = 'https://registry.npmjs.org/electron/latest'; + } + + const request = https.get(url, (res) => { + let data = ''; + res.on('data', chunk => data += chunk); + res.on('end', () => { + try { + const packageInfo = JSON.parse(data); + // Get the actual installed version from package.json, not app.getVersion() + const installedVersion = getInstalledElectronVersion(); + resolve({ + available: packageInfo.version, + current: installedVersion, + buildType: buildType + }); + } catch (err) { + console.error('Failed to parse version info:', err); + resolve({ + available: null, + current: getInstalledElectronVersion(), + error: 'Failed to fetch version info' + }); + } + }); + }); + + request.on('error', (err) => { + console.error('Failed to fetch versions:', err); + resolve({ + available: null, + current: getInstalledElectronVersion(), + error: err.message + }); + }); + + request.setTimeout(5000, () => { + request.destroy(); + resolve({ + available: null, + current: getInstalledElectronVersion(), + error: 'Version check timed out' + }); + }); + }); +}); + +ipcMain.handle('upgrade-electron', async (event, buildType = 'stable') => { + const { execFile } = require('child_process'); + const packageName = buildType === 'nightly' ? 'electron-nightly' : 'electron'; + + return new Promise((resolve) => { + // First, remove the other electron package if switching types + const otherPackage = buildType === 'nightly' ? 'electron' : 'electron-nightly'; + + // Run npm install to upgrade the package + const args = ['install', '--save-dev', packageName + '@latest']; + + execFile('npm', args, + { cwd: __dirname, shell: true, maxBuffer: 10 * 1024 * 1024 }, + (error, stdout, stderr) => { + if (error) { + console.error('Upgrade failed:', error); + console.error('stderr:', stderr); + resolve({ + success: false, + error: error.message, + message: 'Failed to upgrade Electron' + }); + } else { + console.log('Upgrade output:', stdout); + console.log('Upgrade stderr:', stderr); + + // Update package.json to remove the other electron variant if needed + try { + const packageJsonPath = path.join(__dirname, 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + + // Remove electron if we're upgrading to nightly + if (buildType === 'nightly' && packageJson.devDependencies?.electron) { + delete packageJson.devDependencies.electron; + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n'); + } + // Remove electron-nightly if we're upgrading to stable + else if (buildType === 'stable' && packageJson.devDependencies?.['electron-nightly']) { + delete packageJson.devDependencies['electron-nightly']; + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n'); + } + } catch (err) { + console.warn('Could not clean up alternate electron package:', err); + } + + resolve({ + success: true, + message: 'Electron upgrade completed. Restarting application...' + }); + } + } + ); + }); +}); + +ipcMain.handle('restart-app', async (event) => { + // Quit and relaunch the app + app.relaunch(); + app.quit(); +}); + // Open local file dialog -> returns file:// URL (or null if cancelled) ipcMain.handle('show-open-file-dialog', async () => { try { diff --git a/renderer/settings.html b/renderer/settings.html index 4b2249f..8b59f73 100644 --- a/renderer/settings.html +++ b/renderer/settings.html @@ -245,6 +245,31 @@ +
Upgrading Electron will require the application to restart.
+