mirror of
https://github.com/Molunerfinn/PicGo.git
synced 2026-09-20 03:16:37 +00:00
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { RELEASE_URL, RELEASE_URL_BACKUP } from '#/utils/static'
|
||||
import { getLatestVersion } from '~/main/utils/getLatestVersion'
|
||||
|
||||
const { axiosGetMock } = vi.hoisted(() => {
|
||||
return {
|
||||
axiosGetMock: vi.fn()
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('axios', () => {
|
||||
return {
|
||||
default: {
|
||||
get: axiosGetMock
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
describe('main/utils/getLatestVersion', () => {
|
||||
beforeEach(() => {
|
||||
axiosGetMock.mockReset()
|
||||
})
|
||||
|
||||
it('returns stable release when beta channel is older', async () => {
|
||||
axiosGetMock.mockResolvedValueOnce({
|
||||
data: [
|
||||
{ tag_name: 'v2.5.2', prerelease: false, draft: false },
|
||||
{ tag_name: 'v2.4.2-beta.0', prerelease: true, draft: false }
|
||||
]
|
||||
})
|
||||
|
||||
const version = await getLatestVersion(true)
|
||||
|
||||
expect(version).toBe('2.5.2')
|
||||
expect(axiosGetMock).toHaveBeenCalledWith(RELEASE_URL, {
|
||||
headers: {
|
||||
Referer: 'https://github.com'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('returns prerelease when beta channel has a newer version', async () => {
|
||||
axiosGetMock.mockResolvedValueOnce({
|
||||
data: [
|
||||
{ tag_name: 'v2.5.2', prerelease: false, draft: false },
|
||||
{ tag_name: 'v2.6.0-beta.1', prerelease: true, draft: false }
|
||||
]
|
||||
})
|
||||
|
||||
const version = await getLatestVersion(true)
|
||||
|
||||
expect(version).toBe('2.6.0-beta.1')
|
||||
})
|
||||
|
||||
it('ignores prerelease when beta updates are disabled', async () => {
|
||||
axiosGetMock.mockResolvedValueOnce({
|
||||
data: [
|
||||
{ tag_name: 'v2.6.0-beta.1', prerelease: true, draft: false },
|
||||
{ tag_name: 'v2.5.2', prerelease: false, draft: false }
|
||||
]
|
||||
})
|
||||
|
||||
const version = await getLatestVersion(false)
|
||||
|
||||
expect(version).toBe('2.5.2')
|
||||
})
|
||||
|
||||
it('fallback compares stable and beta backup metadata when beta updates are enabled', async () => {
|
||||
axiosGetMock.mockRejectedValueOnce(new Error('network down'))
|
||||
axiosGetMock.mockResolvedValueOnce({
|
||||
data: 'version: 2.5.2'
|
||||
})
|
||||
axiosGetMock.mockResolvedValueOnce({
|
||||
data: 'version: 2.4.2-beta.0'
|
||||
})
|
||||
|
||||
const version = await getLatestVersion(true)
|
||||
|
||||
expect(version).toBe('2.5.2')
|
||||
expect(axiosGetMock).toHaveBeenNthCalledWith(2, `${RELEASE_URL_BACKUP}/latest.yml`, {
|
||||
headers: {
|
||||
Referer: 'https://github.com'
|
||||
}
|
||||
})
|
||||
expect(axiosGetMock).toHaveBeenNthCalledWith(3, `${RELEASE_URL_BACKUP}/latest.beta.yml`, {
|
||||
headers: {
|
||||
Referer: 'https://github.com'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('fallback uses only stable backup metadata when beta updates are disabled', async () => {
|
||||
axiosGetMock.mockRejectedValueOnce(new Error('network down'))
|
||||
axiosGetMock.mockResolvedValueOnce({
|
||||
data: 'version: 2.5.2'
|
||||
})
|
||||
|
||||
const version = await getLatestVersion(false)
|
||||
|
||||
expect(version).toBe('2.5.2')
|
||||
expect(axiosGetMock).toHaveBeenCalledTimes(2)
|
||||
expect(axiosGetMock).toHaveBeenNthCalledWith(2, `${RELEASE_URL_BACKUP}/latest.yml`, {
|
||||
headers: {
|
||||
Referer: 'https://github.com'
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,34 +1,109 @@
|
||||
// for referer policy, we can't use it in renderer
|
||||
import axios from 'axios'
|
||||
import { RELEASE_URL, RELEASE_URL_BACKUP } from '../../universal/utils/static'
|
||||
import semver from 'semver'
|
||||
import yaml from 'js-yaml'
|
||||
|
||||
interface IGithubRelease {
|
||||
tag_name?: string
|
||||
name?: string
|
||||
prerelease?: boolean
|
||||
draft?: boolean
|
||||
}
|
||||
|
||||
interface IReleaseYAML {
|
||||
version?: unknown
|
||||
}
|
||||
|
||||
const REQUEST_HEADERS = {
|
||||
Referer: 'https://github.com'
|
||||
}
|
||||
|
||||
function normalizeVersion (version: unknown): string {
|
||||
if (typeof version !== 'string') {
|
||||
return ''
|
||||
}
|
||||
const normalized = version.trim().replace(/^v/i, '')
|
||||
return semver.valid(normalized) ?? ''
|
||||
}
|
||||
|
||||
function pickLatestVersion (versions: string[]): string {
|
||||
return versions.reduce((latest, current) => {
|
||||
if (latest === '' || semver.gt(current, latest)) {
|
||||
return current
|
||||
}
|
||||
return latest
|
||||
}, '')
|
||||
}
|
||||
|
||||
async function fetchLatestVersionFromGitHub (isCheckBetaUpdate: boolean): Promise<string> {
|
||||
const response = await axios.get(RELEASE_URL, {
|
||||
headers: REQUEST_HEADERS
|
||||
})
|
||||
const releaseList: IGithubRelease[] = Array.isArray(response.data) ? response.data : []
|
||||
|
||||
const versions = releaseList.flatMap((release) => {
|
||||
if (release.draft) {
|
||||
return []
|
||||
}
|
||||
if (!isCheckBetaUpdate && release.prerelease) {
|
||||
return []
|
||||
}
|
||||
|
||||
const version = normalizeVersion(release.tag_name ?? release.name)
|
||||
return version ? [version] : []
|
||||
})
|
||||
|
||||
return pickLatestVersion(versions)
|
||||
}
|
||||
|
||||
async function fetchVersionFromBackupYAML (fileName: 'latest.yml' | 'latest.beta.yml'): Promise<string> {
|
||||
const response = await axios.get(`${RELEASE_URL_BACKUP}/${fileName}`, {
|
||||
headers: REQUEST_HEADERS
|
||||
})
|
||||
const releaseInfo = yaml.load(response.data)
|
||||
|
||||
if (typeof releaseInfo !== 'object' || releaseInfo === null) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return normalizeVersion((releaseInfo as IReleaseYAML).version)
|
||||
}
|
||||
|
||||
async function fetchLatestVersionFromBackup (isCheckBetaUpdate: boolean): Promise<string> {
|
||||
if (!isCheckBetaUpdate) {
|
||||
return fetchVersionFromBackupYAML('latest.yml')
|
||||
}
|
||||
|
||||
const settled = await Promise.allSettled([
|
||||
fetchVersionFromBackupYAML('latest.yml'),
|
||||
fetchVersionFromBackupYAML('latest.beta.yml')
|
||||
])
|
||||
|
||||
const versions: string[] = []
|
||||
settled.forEach((item) => {
|
||||
if (item.status === 'fulfilled' && item.value) {
|
||||
versions.push(item.value)
|
||||
}
|
||||
})
|
||||
|
||||
return pickLatestVersion(versions)
|
||||
}
|
||||
|
||||
export const getLatestVersion = async (isCheckBetaUpdate: boolean = false) => {
|
||||
let res: string = ''
|
||||
try {
|
||||
res = await axios.get(RELEASE_URL, {
|
||||
headers: {
|
||||
Referer: 'https://github.com'
|
||||
}
|
||||
}).then(r => {
|
||||
const list = r.data as IStringKeyMap[]
|
||||
if (isCheckBetaUpdate) {
|
||||
const betaList = list.filter(item => item.name.includes('beta'))
|
||||
return betaList[0].name
|
||||
}
|
||||
const normalList = list.filter(item => !item.name.includes('beta'))
|
||||
return normalList[0].name
|
||||
}).catch(async () => {
|
||||
const result = await axios.get(isCheckBetaUpdate ? `${RELEASE_URL_BACKUP}/latest.beta.yml` : `${RELEASE_URL_BACKUP}/latest.yml`, {
|
||||
headers: {
|
||||
Referer: 'https://github.com'
|
||||
}
|
||||
})
|
||||
const r = yaml.load(result.data) as IStringKeyMap
|
||||
return r.version
|
||||
})
|
||||
const version = await fetchLatestVersionFromGitHub(isCheckBetaUpdate)
|
||||
if (version) {
|
||||
return version
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
return res
|
||||
|
||||
try {
|
||||
return await fetchLatestVersionFromBackup(isCheckBetaUpdate)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user