Skip to content

Commit c980ea7

Browse files
authored
Merge pull request #6 from KilleenCode/support-rc5-updater
support tauri >=v1.0.0-rc.5 urls, routing with fallback to legacy
2 parents 4c71835 + 9696037 commit c980ea7

File tree

11 files changed

+314
-147
lines changed

11 files changed

+314
-147
lines changed

src/constants.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,41 @@
11
export enum AVAILABLE_PLATFORMS {
22
MacOS = 'darwin',
3-
Win32 = 'win32',
4-
Win64 = 'win64',
3+
Windows = 'windows',
54
Linux = 'linux',
65
}
6+
7+
export enum AVAILABLE_ARCHITECTURES {
8+
x64 = 'x86_64',
9+
x86 = 'i686',
10+
arm64 = 'aarch64',
11+
armv7 = 'armv7',
12+
}
13+
14+
//TODO: check if this is correct
15+
export const ARCH_FILTERS = {
16+
[AVAILABLE_ARCHITECTURES.x64]: ['x64', 'amd64'],
17+
[AVAILABLE_ARCHITECTURES.x86]: ['x86'],
18+
[AVAILABLE_ARCHITECTURES.arm64]: ['arm64'],
19+
[AVAILABLE_ARCHITECTURES.armv7]: ['armv7'],
20+
} as { [key in AVAILABLE_ARCHITECTURES]: string[] }
21+
22+
export type Filter = {
23+
extension: string
24+
matches: string[]
25+
}
26+
export const PLATFORM_FILTERS = {
27+
[AVAILABLE_PLATFORMS.MacOS]: {
28+
extension: 'gz',
29+
matches: ['.app', 'osx'],
30+
},
31+
[AVAILABLE_PLATFORMS.Windows]: {
32+
extension: 'zip',
33+
matches: ['x64', 'x32'],
34+
},
35+
[AVAILABLE_PLATFORMS.Linux]: {
36+
extension: 'gz',
37+
matches: ['AppImage'],
38+
},
39+
} as {
40+
[key in AVAILABLE_PLATFORMS]: Filter
41+
}

src/getPlatform.ts

Lines changed: 23 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,29 @@
1-
import { AVAILABLE_PLATFORMS } from './constants'
2-
3-
export function sanitizeVersion(version: string): string | undefined {
4-
// if it start with app-name-v1.0.0, get the version.
5-
// If there is a string name, assume a `v` char.
6-
const semanticV = version.split('v').pop()
7-
8-
return semanticV
9-
}
10-
11-
export function checkPlatform(
12-
platform: string,
1+
import {
2+
ARCH_FILTERS,
3+
AVAILABLE_ARCHITECTURES,
4+
AVAILABLE_PLATFORMS,
5+
PLATFORM_FILTERS,
6+
} from './constants'
7+
import { fileExt } from './utils/fileExt'
8+
9+
export const testAsset = (
10+
target: AVAILABLE_PLATFORMS,
11+
arch: AVAILABLE_ARCHITECTURES,
1312
fileName: string,
14-
): string | undefined {
15-
const extension = extname(fileName)
16-
// OSX we should have our .app tar.gz
17-
if (
18-
(fileName.includes('.app') ||
19-
fileName.includes('darwin') ||
20-
fileName.includes('osx')) &&
21-
extension === 'gz' &&
22-
platform === AVAILABLE_PLATFORMS.MacOS
23-
) {
24-
return 'darwin'
25-
}
26-
27-
// Windows 64 bits
28-
if (
29-
(fileName.includes('x64') || fileName.includes('win64')) &&
30-
extension === 'zip' &&
31-
platform === AVAILABLE_PLATFORMS.Win64
32-
) {
33-
return 'win64'
13+
): boolean => {
14+
const { matches, extension } = PLATFORM_FILTERS[target]
15+
const arch_matches = ARCH_FILTERS[arch]
16+
const rightArch =
17+
arch_matches && ARCH_FILTERS[arch].some((arch) => fileName.includes(arch))
18+
19+
// .app gz files don't have arch in the name
20+
if (!rightArch && target !== AVAILABLE_PLATFORMS.MacOS) {
21+
return false
3422
}
3523

36-
// Windows 32 bits
37-
if (
38-
(fileName.includes('x32') || fileName.includes('win32')) &&
39-
extension === 'zip' &&
40-
platform === AVAILABLE_PLATFORMS.Win32
41-
) {
42-
return 'win32'
24+
if (fileExt(fileName) !== extension) {
25+
return false
4326
}
4427

45-
// Linux app image
46-
if (
47-
fileName.includes('AppImage') &&
48-
extension === 'gz' &&
49-
platform === AVAILABLE_PLATFORMS.Linux
50-
) {
51-
return 'linux'
52-
}
53-
}
54-
55-
function extname(filename: string) {
56-
return filename.split('.').pop() || ''
57-
}
58-
59-
export type Asset = { name: string; browser_download_url: string }
60-
61-
export async function findAssetSignature(
62-
fileName: string,
63-
assets: Asset[],
64-
): Promise<string | undefined> {
65-
// check in our assets if we have a file: `fileName.sig`
66-
// by example fileName can be: App-1.0.0.zip
67-
68-
const foundSignature = assets.find(
69-
(asset) => asset.name.toLowerCase() === `${fileName.toLowerCase()}.sig`,
70-
)
71-
72-
if (!foundSignature) {
73-
return undefined
74-
}
75-
76-
const response = await fetch(foundSignature.browser_download_url)
77-
if (response.status !== 200) {
78-
return undefined
79-
}
80-
const signature = await response.text()
81-
return signature
82-
}
83-
84-
export function validatePlatform(platform: string): string | undefined {
85-
switch (platform) {
86-
case AVAILABLE_PLATFORMS.MacOS:
87-
case AVAILABLE_PLATFORMS.Win32:
88-
case AVAILABLE_PLATFORMS.Win64:
89-
case AVAILABLE_PLATFORMS.Linux:
90-
return platform
91-
}
28+
return matches.some((match) => fileName.includes(match))
9229
}

src/handler.ts

Lines changed: 63 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,86 @@
1-
import {
2-
Asset,
3-
checkPlatform,
4-
findAssetSignature,
5-
sanitizeVersion,
6-
validatePlatform,
7-
} from './getPlatform'
1+
import { testAsset } from './getPlatform'
82
import semverValid from 'semver/functions/valid'
93
import semverGt from 'semver/functions/gt'
10-
11-
type TauriUpdateResponse = {
12-
url: string
13-
version: string
14-
notes?: string
15-
pub_date?: string
16-
signature?: string
17-
}
4+
import { AVAILABLE_ARCHITECTURES, AVAILABLE_PLATFORMS } from './constants'
5+
import { handleLegacyRequest } from './legacy/handler'
6+
import { findAssetSignature, getLatestRelease } from './services/github'
7+
import { TauriUpdateResponse } from './types'
8+
import { sanitizeVersion } from './utils/versioning'
189

1910
declare global {
2011
const GITHUB_ACCOUNT: string
2112
const GITHUB_REPO: string
2213
}
2314

24-
export async function handleRequest(request: Request): Promise<Response> {
25-
const path = new URL(request.url).pathname
26-
const [platform, version] = path.slice(1).split('/')
15+
const responses = {
16+
NotFound: () => new Response('Not found', { status: 404 }),
17+
NoContent: () => new Response(null, { status: 204 }),
18+
SendUpdate: (data: TauriUpdateResponse) =>
19+
new Response(JSON.stringify(data), {
20+
headers: { 'Content-Type': 'application/json; charset=utf-8' },
21+
}),
22+
}
2723

28-
const reqUrl = new URL(
29-
`https://api.github.com/repos/${GITHUB_ACCOUNT}/${GITHUB_REPO}/releases/latest`,
30-
)
31-
const headers = new Headers({
32-
Accept: 'application/vnd.github.preview',
33-
'User-Agent': request.headers.get('User-Agent') as string,
34-
})
35-
const releaseResponse = await fetch(reqUrl.toString(), {
36-
method: 'GET',
37-
headers,
38-
})
24+
type RequestPathParts = [
25+
string,
26+
AVAILABLE_PLATFORMS,
27+
AVAILABLE_ARCHITECTURES,
28+
string,
29+
]
30+
const handleV1Request = async (request: Request) => {
31+
const path = new URL(request.url).pathname
32+
const [, target, arch, appVersion] = path
33+
.slice(1)
34+
.split('/') as RequestPathParts
3935

40-
const release = (await releaseResponse.clone().json()) as {
41-
tag_name: string
42-
assets: Asset[]
43-
body: string
44-
published_at: string
45-
}
46-
if (!platform || !validatePlatform(platform) || !version) {
47-
return releaseResponse
36+
if (!target || !arch || !appVersion || !semverValid(appVersion)) {
37+
return responses.NotFound()
4838
}
49-
const remoteVersion = sanitizeVersion(release.tag_name.toLowerCase())
39+
const release = await getLatestRelease(request)
5040

41+
const remoteVersion = sanitizeVersion(release.tag_name.toLowerCase())
5142
if (!remoteVersion || !semverValid(remoteVersion)) {
52-
return new Response('Not found', { status: 404 })
43+
return responses.NotFound()
5344
}
5445

55-
const shouldUpdate = semverGt(remoteVersion, version)
46+
const shouldUpdate = semverGt(remoteVersion, appVersion)
5647
if (!shouldUpdate) {
57-
return new Response(null, { status: 204 })
48+
return responses.NoContent()
5849
}
5950

60-
for (const asset of release.assets) {
61-
const { name, browser_download_url } = asset
62-
const findPlatform = checkPlatform(platform, name)
63-
if (!findPlatform) {
64-
continue
65-
}
51+
const match = release.assets.find(({ name }) => {
52+
const test = testAsset(target, arch, name)
6653

67-
// try to find signature for this asset
68-
const signature = await findAssetSignature(name, release.assets)
69-
const data: TauriUpdateResponse = {
70-
url: browser_download_url,
71-
version: remoteVersion,
72-
notes: release.body,
73-
pub_date: release.published_at,
74-
signature,
54+
return test
55+
})
56+
57+
if (typeof match === 'undefined') {
58+
return responses.NotFound()
59+
}
60+
61+
const signature = await findAssetSignature(match.name, release.assets)
62+
const data: TauriUpdateResponse = {
63+
url: match.browser_download_url,
64+
version: remoteVersion,
65+
notes: release.body,
66+
pub_date: release.published_at,
67+
signature,
68+
}
69+
70+
return responses.SendUpdate(data)
71+
}
72+
73+
export async function handleRequest(request: Request): Promise<Response> {
74+
const path = new URL(request.url).pathname
75+
const version = path.slice(1).split('/')[0]
76+
77+
if (version.includes('v')) {
78+
switch (version) {
79+
case 'v1':
80+
default:
81+
return handleV1Request(request)
7582
}
76-
return new Response(JSON.stringify(data), {
77-
headers: { 'Content-Type': 'application/json; charset=utf-8' },
78-
})
7983
}
8084

81-
return new Response(JSON.stringify({ remoteVersion, version, shouldUpdate }))
85+
return handleLegacyRequest(request)
8286
}

src/legacy/checkPlatform.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { LEGACY_AVAILABLE_PLATFORMS } from './constants'
2+
import { fileExt } from '../utils/fileExt'
3+
4+
export function checkPlatform(
5+
platform: string,
6+
fileName: string,
7+
): string | undefined {
8+
const extension = fileExt(fileName)
9+
// OSX we should have our .app tar.gz
10+
if (
11+
(fileName.includes('.app') ||
12+
fileName.includes('darwin') ||
13+
fileName.includes('osx')) &&
14+
extension === 'gz' &&
15+
platform === LEGACY_AVAILABLE_PLATFORMS.MacOS
16+
) {
17+
return 'darwin'
18+
}
19+
20+
// Windows 64 bits
21+
if (
22+
(fileName.includes('x64') || fileName.includes('win64')) &&
23+
extension === 'zip' &&
24+
platform === LEGACY_AVAILABLE_PLATFORMS.Win64
25+
) {
26+
return 'win64'
27+
}
28+
29+
// Windows 32 bits
30+
if (
31+
(fileName.includes('x32') || fileName.includes('win32')) &&
32+
extension === 'zip' &&
33+
platform === LEGACY_AVAILABLE_PLATFORMS.Win32
34+
) {
35+
return 'win32'
36+
}
37+
38+
// Linux app image
39+
if (
40+
fileName.includes('AppImage') &&
41+
extension === 'gz' &&
42+
platform === LEGACY_AVAILABLE_PLATFORMS.Linux
43+
) {
44+
return 'linux'
45+
}
46+
}

src/legacy/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export enum LEGACY_AVAILABLE_PLATFORMS {
2+
MacOS = 'darwin',
3+
Win32 = 'win32',
4+
Win64 = 'win64',
5+
Linux = 'linux',
6+
}

0 commit comments

Comments
 (0)