Skip to content

Commit 58135af

Browse files
committed
feat: implement dynamic auto-updater release fetching with version override support
1 parent c2a09dd commit 58135af

2 files changed

Lines changed: 112 additions & 10 deletions

File tree

src/main.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { usePluginAPI } from '@main/api'
1616
import { setupConfig } from '@main/config'
1717
import { resolve } from 'node:path'
1818
import Squirrel from 'electron-squirrel-startup'
19+
import { fetchLatestRelease } from './main/releases'
1920

2021
if (is.dev) {
2122
app.setPath('userData', app.getPath('userData') + '-dev')
@@ -79,9 +80,9 @@ function createWindow(): void {
7980
const position =
8081
externalDisplay && is.dev
8182
? {
82-
x: externalDisplay.bounds.x + 50,
83-
y: externalDisplay.bounds.y + 50
84-
}
83+
x: externalDisplay.bounds.x + 50,
84+
y: externalDisplay.bounds.y + 50
85+
}
8586
: {}
8687

8788
// Create the browser window.
@@ -170,13 +171,31 @@ app.whenReady().then(async () => {
170171
await handleProtocolUrl(url)
171172
})
172173

173-
if (!is.dev) {
174-
autoUpdater.setFeedURL({
175-
url: 'https://github.com/CynToolkit/pipelab/releases/latest/download',
176-
headers: {
177-
'Cache-Control': 'no-cache'
174+
if (!is.dev || process.env.APP_UPDATE_URL || process.env.PIPELAB_OVERRIDE_RELEASE) {
175+
try {
176+
const latestRelease = await fetchLatestRelease('@pipelab/app')
177+
if (latestRelease) {
178+
logger().info('Found latest release:', latestRelease.tag_name)
179+
logger().info('Release API URL:', latestRelease.url)
180+
} else {
181+
logger().info('No specific release found, falling back to generic /latest/download')
178182
}
179-
})
183+
184+
const feedUrl = latestRelease
185+
? `https://github.com/CynToolkit/pipelab/releases/download/${latestRelease.tag_name}`
186+
: 'https://github.com/CynToolkit/pipelab/releases/latest/download'
187+
188+
logger().info('Setting auto-updater feed URL:', feedUrl)
189+
190+
autoUpdater.setFeedURL({
191+
url: feedUrl,
192+
headers: {
193+
'Cache-Control': 'no-cache'
194+
}
195+
})
196+
} catch (error) {
197+
logger().error('Failed to setup auto-updater:', error)
198+
}
180199

181200
autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => {
182201
api?.execute('update:set-status', {
@@ -378,7 +397,8 @@ app.whenReady().then(async () => {
378397
mainWindow.show()
379398
mainWindow.maximize()
380399

381-
if (app.isPackaged) {
400+
if (app.isPackaged || process.env.APP_UPDATE_URL || process.env.PIPELAB_OVERRIDE_RELEASE) {
401+
autoUpdater.checkForUpdates()
382402
setTimeout(() => {
383403
autoUpdater.checkForUpdates()
384404
console.log('checkForUpdates')

src/main/releases.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { useLogger } from '@@/logger'
2+
import semver from 'semver'
3+
4+
export type Release = {
5+
id: number
6+
tag_name: string
7+
name: string
8+
body: string
9+
published_at: string
10+
html_url: string
11+
url: string
12+
}
13+
14+
const REPO = 'CynToolkit/pipelab'
15+
16+
export const fetchAllReleases = async (): Promise<Release[]> => {
17+
const { logger } = useLogger()
18+
try {
19+
const url = `https://api.github.com/repos/${REPO}/releases`
20+
logger().info('Fetching releases from GitHub API:', url)
21+
const response = await fetch(url, {
22+
headers: {
23+
'User-Agent': 'Pipelab'
24+
}
25+
})
26+
if (!response.ok) {
27+
throw new Error(`Failed to fetch releases: ${response.statusText}`)
28+
}
29+
return (await response.json()) as Release[]
30+
} catch (error) {
31+
logger().error('Error fetching all releases:', error)
32+
throw error
33+
}
34+
}
35+
36+
/**
37+
* Fetches the latest release for a specific package.
38+
* Supports PIPELAB_OVERRIDE_RELEASE environment variable.
39+
*/
40+
export const fetchLatestRelease = async (packageName = '@pipelab/app'): Promise<Release | null> => {
41+
const { logger } = useLogger()
42+
43+
try {
44+
const releases = await fetchAllReleases()
45+
46+
// Handle environment variable override
47+
const override = process.env.PIPELAB_OVERRIDE_RELEASE
48+
if (override) {
49+
logger().info('Using release override:', override)
50+
const targetTag = override.includes('@') ? override : `${packageName}@${override}`
51+
const release = releases.find((r) => r.tag_name === targetTag)
52+
if (release) {
53+
return release
54+
}
55+
logger().warn(`Release override ${override} not found for package ${packageName}`)
56+
}
57+
58+
// Filter for releases that follow the {packageName}@X.Y.Z tag pattern
59+
const packageReleases = releases.filter((r) => r.tag_name.startsWith(`${packageName}@`))
60+
61+
if (packageReleases.length === 0) {
62+
return null
63+
}
64+
65+
// Sort by semver
66+
const getVersion = (tagName: string) => tagName.split('@').pop() || '0.0.0'
67+
68+
packageReleases.sort((a, b) => {
69+
const vA = getVersion(a.tag_name)
70+
const vB = getVersion(b.tag_name)
71+
if (semver.valid(vA) && semver.valid(vB)) {
72+
return semver.rcompare(vA, vB)
73+
}
74+
return new Date(b.published_at).getTime() - new Date(a.published_at).getTime()
75+
})
76+
77+
return packageReleases[0]
78+
} catch (error) {
79+
logger().error('Failed to fetch latest release:', error)
80+
return null
81+
}
82+
}

0 commit comments

Comments
 (0)