diff --git a/src/main/main.ts b/src/main/main.ts index 3c8fea8..cfd89e5 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -5247,14 +5247,16 @@ interface GitHubRelease { body: string; html_url: string; published_at: string; + prerelease: boolean; assets: Array<{ name: string; browser_download_url: string }>; } // Check for updates from GitHub releases ipcMain.handle('updates:checkForUpdate', async () => { try { + // Fetch release list so we can skip non-semver tags (e.g. the "assets" release) const response = await fetch( - `https://api.github.com/repos/${GITHUB_REPO_OWNER}/${GITHUB_REPO_NAME}/releases/latest`, + `https://api.github.com/repos/${GITHUB_REPO_OWNER}/${GITHUB_REPO_NAME}/releases?per_page=10`, { headers: { Accept: 'application/vnd.github.v3+json', @@ -5270,7 +5272,15 @@ ipcMain.handle('updates:checkForUpdate', async () => { throw new Error(`GitHub API error: ${response.status}`); } - const release = (await response.json()) as GitHubRelease; + const releases = (await response.json()) as GitHubRelease[]; + // Find the first non-prerelease release with a semver tag + const semverRegex = /^v?\d+\.\d+\.\d+$/; + const release = releases.find((r) => !r.prerelease && semverRegex.test(r.tag_name)); + + if (!release) { + return { hasUpdate: false, error: 'No semver releases found' }; + } + const latestVersion = release.tag_name.replace(/^v/, ''); // Get current version from package.json @@ -5297,15 +5307,19 @@ ipcMain.handle('updates:checkForUpdate', async () => { // Compare versions (simple comparison, assumes semver) const hasUpdate = compareVersions(latestVersion, currentVersion) > 0; + // Pick a platform-appropriate download asset + const isWindows = process.platform === 'win32'; + const installerAsset = release.assets.find((a) => + isWindows ? a.name.endsWith('.exe') : a.name.endsWith('.dmg') + ); + return { hasUpdate: hasUpdate && latestVersion !== (store.get('dismissedUpdateVersion', '') as string), currentVersion, latestVersion, releaseNotes: release.body || '', releaseUrl: release.html_url, - downloadUrl: - release.assets.find((a) => a.name.endsWith('.dmg'))?.browser_download_url || - release.html_url, + downloadUrl: installerAsset?.browser_download_url || release.html_url, }; } catch (error) { console.error('Failed to check for updates:', error); diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 63c2e48..bb9cd6c 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -713,6 +713,26 @@ const App: React.FC = () => { return () => clearTimeout(timer); }, []); + // Debug helper: run window.showUpdateModal() in DevTools console to test the modal + useEffect(() => { + (window as any).showUpdateModal = (info?: { + currentVersion?: string; + latestVersion?: string; + downloadUrl?: string; + }) => { + const defaults = { + currentVersion: buildInfo.baseVersion, + latestVersion: '99.0.0', + downloadUrl: 'https://github.com/CooperAgent/cooper/releases', + }; + setUpdateInfo({ ...defaults, ...info }); + setShowUpdateModal(true); + }; + return () => { + delete (window as any).showUpdateModal; + }; + }, []); + // Check if user has seen welcome wizard on startup useEffect(() => { const checkWelcomeWizard = async () => { diff --git a/src/renderer/components/UpdateAvailableModal/UpdateAvailableModal.tsx b/src/renderer/components/UpdateAvailableModal/UpdateAvailableModal.tsx index b94e200..0b8fa5c 100644 --- a/src/renderer/components/UpdateAvailableModal/UpdateAvailableModal.tsx +++ b/src/renderer/components/UpdateAvailableModal/UpdateAvailableModal.tsx @@ -23,7 +23,9 @@ export const UpdateAvailableModal: React.FC = ({ }; const handleOpenReleases = () => { - window.electronAPI.updates.openDownloadUrl('https://github.com/CooperAgent/cooper'); + window.electronAPI.updates.openDownloadUrl( + 'https://github.com/CooperAgent/cooper/releases/latest' + ); }; return ( @@ -69,23 +71,8 @@ export const UpdateAvailableModal: React.FC = ({

- Pull the latest version and rebuild to upgrade. + Visit the releases page to download the latest version.

- -
-
-
macOS
-
-                {`cd cooper\ngit pull\nnpm install\nnpm run dist\nopen release/Cooper-*-arm64.dmg`}
-              
-
-
-
Windows (PowerShell)
-
-                {`cd cooper\ngit pull\npwsh -NoProfile -File .\\scripts\\setup-windows.ps1\nnpm run dist:win`}
-              
-
-
@@ -95,7 +82,7 @@ export const UpdateAvailableModal: React.FC = ({ Later
diff --git a/tests/components/UpdateModals.test.tsx b/tests/components/UpdateModals.test.tsx index e6d0354..0f95abc 100644 --- a/tests/components/UpdateModals.test.tsx +++ b/tests/components/UpdateModals.test.tsx @@ -64,17 +64,19 @@ describe('UpdateAvailableModal', () => { expect(defaultProps.onClose).toHaveBeenCalled(); }); - it('opens repository page when Open Repository button is clicked', async () => { + it('opens releases page when Open Releases button is clicked', async () => { render(); await waitFor(() => { - expect(screen.getByText('Open Repository')).toBeInTheDocument(); + expect(screen.getByText('Open Releases')).toBeInTheDocument(); }); - fireEvent.click(screen.getByText('Open Repository')); + fireEvent.click(screen.getByText('Open Releases')); await waitFor(() => { - expect(mockOpenDownloadUrl).toHaveBeenCalled(); + expect(mockOpenDownloadUrl).toHaveBeenCalledWith( + 'https://github.com/CooperAgent/cooper/releases/latest' + ); }); }); });