feat: check and download updates at startup

This commit is contained in:
reaper
2026-08-14 13:50:34 -05:00
parent 55715d9fa4
commit 7b59a1cd90
6 changed files with 158 additions and 5 deletions
+4
View File
@@ -31,3 +31,7 @@ linux:
appImage: appImage:
artifactName: ${name}-${version}.${ext} artifactName: ${name}-${version}.${ext}
npmRebuild: false npmRebuild: false
publish:
provider: github
owner: KrisAsvestas
repo: auroradockside
+83 -5
View File
@@ -13,6 +13,7 @@
"@electron-toolkit/utils": "^4.0.0", "@electron-toolkit/utils": "^4.0.0",
"@tanstack/react-query": "^5.101.4", "@tanstack/react-query": "^5.101.4",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"electron-updater": "^6.8.9",
"lucide-react": "^1.28.0", "lucide-react": "^1.28.0",
"tailwind-merge": "^3.6.0", "tailwind-merge": "^3.6.0",
"zustand": "^5.0.14" "zustand": "^5.0.14"
@@ -3883,7 +3884,6 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0" "license": "Python-2.0"
}, },
"node_modules/aria-query": { "node_modules/aria-query": {
@@ -4307,7 +4307,6 @@
"version": "9.7.0", "version": "9.7.0",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz", "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz",
"integrity": "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==", "integrity": "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"debug": "^4.3.4", "debug": "^4.3.4",
@@ -5293,6 +5292,69 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/electron-updater": {
"version": "6.8.9",
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.8.9.tgz",
"integrity": "sha512-ZhVxM9iGONUpZGI1FxdMRgJjUFXi7AYGVa5PwKlO1tV1/4zDxQmfKpXOHVztKrd6L9rLcFjERvi1Mf2vxyTkig==",
"license": "MIT",
"dependencies": {
"builder-util-runtime": "9.7.0",
"fs-extra": "^10.1.0",
"js-yaml": "^4.1.0",
"lazy-val": "^1.0.5",
"lodash.escaperegexp": "^4.1.2",
"lodash.isequal": "^4.5.0",
"semver": "~7.7.3",
"tiny-typed-emitter": "^2.1.0"
}
},
"node_modules/electron-updater/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/electron-updater/node_modules/jsonfile": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
"integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/electron-updater/node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/electron-updater/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/electron-vite": { "node_modules/electron-vite": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/electron-vite/-/electron-vite-5.0.0.tgz", "resolved": "https://registry.npmjs.org/electron-vite/-/electron-vite-5.0.0.tgz",
@@ -7484,7 +7546,6 @@
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@@ -7645,7 +7706,6 @@
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
"integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/levn": { "node_modules/levn": {
@@ -7958,6 +8018,19 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash.escaperegexp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
"integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
"license": "MIT"
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
"license": "MIT"
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -9461,7 +9534,6 @@
"version": "1.6.1", "version": "1.6.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
"integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
"dev": true,
"license": "BlueOak-1.0.0", "license": "BlueOak-1.0.0",
"engines": { "engines": {
"node": ">=11.0.0" "node": ">=11.0.0"
@@ -10164,6 +10236,12 @@
"semver": "bin/semver" "semver": "bin/semver"
} }
}, },
"node_modules/tiny-typed-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz",
"integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==",
"license": "MIT"
},
"node_modules/tinybench": { "node_modules/tinybench": {
"version": "2.9.0", "version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+1
View File
@@ -33,6 +33,7 @@
"@electron-toolkit/utils": "^4.0.0", "@electron-toolkit/utils": "^4.0.0",
"@tanstack/react-query": "^5.101.4", "@tanstack/react-query": "^5.101.4",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"electron-updater": "^6.8.9",
"lucide-react": "^1.28.0", "lucide-react": "^1.28.0",
"tailwind-merge": "^3.6.0", "tailwind-merge": "^3.6.0",
"zustand": "^5.0.14" "zustand": "^5.0.14"
+2
View File
@@ -11,6 +11,7 @@ import { registerCreateIpc } from './ipc/create'
import { registerWindowIpc } from './ipc/window' import { registerWindowIpc } from './ipc/window'
import { registerSecretsIpc } from './ipc/secrets' import { registerSecretsIpc } from './ipc/secrets'
import { killAllRunningCommands, powerOffAllProjects } from './commandRunner' import { killAllRunningCommands, powerOffAllProjects } from './commandRunner'
import { startAutomaticUpdates } from './updater'
function createWindow(): void { function createWindow(): void {
// Create the browser window. // Create the browser window.
@@ -69,6 +70,7 @@ app.whenReady().then(() => {
registerSecretsIpc() registerSecretsIpc()
createWindow() createWindow()
startAutomaticUpdates()
app.on('activate', function () { app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the // On macOS it's common to re-create a window in the app when the
+10
View File
@@ -0,0 +1,10 @@
import { describe, expect, it } from 'vitest'
import { compareVersions } from './updater'
describe('update version comparison', () => {
it('detects newer module versions', () => {
expect(compareVersions('1.2.0', '1.1.0')).toBeGreaterThan(0)
expect(compareVersions('1.2.0', '1.2.0')).toBe(0)
expect(compareVersions('1.1.9', '1.2.0')).toBeLessThan(0)
})
})
+58
View File
@@ -0,0 +1,58 @@
import { app } from 'electron'
import electronUpdater, { type AppUpdater } from 'electron-updater'
import {
getAvailableModulePackages,
getModuleRegistry,
installModulePackage
} from './moduleRegistry'
function updater(): AppUpdater {
// electron-updater is CommonJS; destructuring its default import keeps the
// production bundle compatible with Electron's ESM interop.
return electronUpdater.autoUpdater
}
export function compareVersions(left: string, right: string): number {
const a = left.split(/[.-]/).map((part) => Number(part) || 0)
const b = right.split(/[.-]/).map((part) => Number(part) || 0)
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
if ((a[index] ?? 0) !== (b[index] ?? 0)) return (a[index] ?? 0) - (b[index] ?? 0)
}
return 0
}
export async function updateInstalledModulesFromCatalog(): Promise<string[]> {
const installed = new Map((await getModuleRegistry()).map((module) => [module.id, module]))
const available = await getAvailableModulePackages()
const updated: string[] = []
for (const candidate of available) {
const current = installed.get(candidate.manifest.id)
if (!current || compareVersions(candidate.manifest.version, current.version) <= 0) continue
await installModulePackage(candidate.sourcePath)
updated.push(`${candidate.manifest.name} ${candidate.manifest.version}`)
}
return updated
}
export function startAutomaticUpdates(): void {
void updateInstalledModulesFromCatalog()
.then((updated) => {
if (updated.length) console.info(`Updated Aurora modules: ${updated.join(', ')}`)
})
.catch((error) => console.warn('Aurora module update check failed:', error))
// electron-updater needs a packaged application and release metadata. The
// extracted development build intentionally skips the remote app check.
if (!app.isPackaged || process.env.AURORA_DISABLE_AUTO_UPDATE === '1') return
const autoUpdater = updater()
autoUpdater.autoDownload = true
autoUpdater.autoInstallOnAppQuit = true
autoUpdater.allowPrerelease = true
autoUpdater.logger = console
void autoUpdater.checkForUpdatesAndNotify().catch((error) =>
console.warn('Aurora application update check failed:', error)
)
}