Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use tag name instead of version when resolving GitHub files #6117

Merged
merged 2 commits into from Aug 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions empty-ties-argue.md
@@ -0,0 +1,5 @@
---
"electron-updater": patch
---

fix: use git remote's tag name instead of v-prefixed version string when resolving GitHub releases
38 changes: 22 additions & 16 deletions packages/electron-updater/src/providers/GitHubProvider.ts
Expand Up @@ -6,8 +6,11 @@ import { ResolvedUpdateFileInfo } from "../main"
import { getChannelFilename, newBaseUrl, newUrlFromBase } from "../util"
import { parseUpdateInfo, Provider, ProviderRuntimeOptions, resolveFiles } from "./Provider"

const hrefRegExp = /\/tag\/v?([^/]+)$/
const hrefRegExp = /\/tag\/([^/]+)$/

interface GithubUpdateInfo extends UpdateInfo {
tag: string
}
export abstract class BaseGitHubProvider<T extends UpdateInfo> extends Provider<T> {
// so, we don't need to parse port (because node http doesn't support host as url does)
protected readonly baseUrl: URL
Expand All @@ -32,12 +35,12 @@ export abstract class BaseGitHubProvider<T extends UpdateInfo> extends Provider<
}
}

export class GitHubProvider extends BaseGitHubProvider<UpdateInfo> {
export class GitHubProvider extends BaseGitHubProvider<GithubUpdateInfo> {
constructor(protected readonly options: GithubOptions, private readonly updater: AppUpdater, runtimeOptions: ProviderRuntimeOptions) {
super(options, "github.com", runtimeOptions)
}

async getLatestVersion(): Promise<UpdateInfo> {
async getLatestVersion(): Promise<GithubUpdateInfo> {
const cancellationToken = new CancellationToken()

const feedXml: string = (await this.httpRequest(
Expand All @@ -51,16 +54,16 @@ export class GitHubProvider extends BaseGitHubProvider<UpdateInfo> {
const feed = parseXml(feedXml)
// noinspection TypeScriptValidateJSTypes
let latestRelease = feed.element("entry", false, `No published versions on GitHub`)
let version: string | null
let tag: string | null
try {
if (this.updater.allowPrerelease) {
// noinspection TypeScriptValidateJSTypes
version = hrefRegExp.exec(latestRelease.element("link").attribute("href"))![1]
tag = hrefRegExp.exec(latestRelease.element("link").attribute("href"))![1]
} else {
version = await this.getLatestVersionString(cancellationToken)
tag = await this.getLatestTagName(cancellationToken)
for (const element of feed.getElements("entry")) {
// noinspection TypeScriptValidateJSTypes
if (hrefRegExp.exec(element.element("link").attribute("href"))![1] === version) {
if (hrefRegExp.exec(element.element("link").attribute("href"))![1] === tag) {
latestRelease = element
break
}
Expand All @@ -70,12 +73,12 @@ export class GitHubProvider extends BaseGitHubProvider<UpdateInfo> {
throw newError(`Cannot parse releases feed: ${e.stack || e.message},\nXML:\n${feedXml}`, "ERR_UPDATER_INVALID_RELEASE_FEED")
}

if (version == null) {
if (tag == null) {
throw newError(`No published versions on GitHub`, "ERR_UPDATER_NO_PUBLISHED_VERSIONS")
}

const channelFile = getChannelFilename(this.getDefaultChannelName())
const channelFileUrl = newUrlFromBase(this.getBaseDownloadPath(version, channelFile), this.baseUrl)
const channelFileUrl = newUrlFromBase(this.getBaseDownloadPath(tag, channelFile), this.baseUrl)
const requestOptions = this.createRequestOptions(channelFileUrl)
let rawData: string
try {
Expand All @@ -95,10 +98,13 @@ export class GitHubProvider extends BaseGitHubProvider<UpdateInfo> {
if (result.releaseNotes == null) {
result.releaseNotes = computeReleaseNotes(this.updater.currentVersion, this.updater.fullChangelog, feed, latestRelease)
}
return result
return {
tag: tag,
...result,
}
}

private async getLatestVersionString(cancellationToken: CancellationToken): Promise<string | null> {
private async getLatestTagName(cancellationToken: CancellationToken): Promise<string | null> {
const options = this.options
// do not use API for GitHub to avoid limit, only for custom host or GitHub Enterprise
const url =
Expand All @@ -112,7 +118,7 @@ export class GitHubProvider extends BaseGitHubProvider<UpdateInfo> {
}

const releaseInfo: GithubReleaseInfo = JSON.parse(rawData)
return releaseInfo.tag_name.startsWith("v") ? releaseInfo.tag_name.substring(1) : releaseInfo.tag_name
return releaseInfo.tag_name
} catch (e) {
throw newError(`Unable to find latest version on GitHub (${url}), please ensure a production release exists: ${e.stack || e.message}`, "ERR_UPDATER_LATEST_VERSION_NOT_FOUND")
}
Expand All @@ -122,13 +128,13 @@ export class GitHubProvider extends BaseGitHubProvider<UpdateInfo> {
return `/${this.options.owner}/${this.options.repo}/releases`
}

resolveFiles(updateInfo: UpdateInfo): Array<ResolvedUpdateFileInfo> {
resolveFiles(updateInfo: GithubUpdateInfo): Array<ResolvedUpdateFileInfo> {
// still replace space to - due to backward compatibility
return resolveFiles(updateInfo, this.baseUrl, p => this.getBaseDownloadPath(updateInfo.version, p.replace(/ /g, "-")))
return resolveFiles(updateInfo, this.baseUrl, p => this.getBaseDownloadPath(updateInfo.tag, p.replace(/ /g, "-")))
}

private getBaseDownloadPath(version: string, fileName: string): string {
return `${this.basePath}/download/${this.options.vPrefixedTagName === false ? "" : "v"}${version}/${fileName}`
private getBaseDownloadPath(tag: string, fileName: string): string {
return `${this.basePath}/download/${tag}/${fileName}`
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/electron-updater/src/providers/Provider.ts
Expand Up @@ -55,7 +55,7 @@ export abstract class Provider<T extends UpdateInfo> {

abstract getLatestVersion(): Promise<T>

abstract resolveFiles(updateInfo: UpdateInfo): Array<ResolvedUpdateFileInfo>
abstract resolveFiles(updateInfo: T): Array<ResolvedUpdateFileInfo>

/**
* Method to perform API request only to resolve update info, but not to download update.
Expand Down
7 changes: 7 additions & 0 deletions test/snapshots/updater/nsisUpdaterTest.js.snap
Expand Up @@ -107,6 +107,7 @@ Object {
],
"releaseName": "v1.5.2-beta.3",
"releaseNotes": "",
"tag": "v1.5.2-beta.3",
"version": "1.5.2-beta.3",
}
`;
Expand Down Expand Up @@ -160,6 +161,7 @@ Object {
],
"releaseName": "1.1.0",
"releaseNotes": "",
"tag": "v1.1.0",
"version": "1.1.0",
}
`;
Expand All @@ -174,6 +176,7 @@ Object {
],
"releaseName": "1.1.0",
"releaseNotes": "",
"tag": "v1.1.0",
"version": "1.1.0",
}
`;
Expand Down Expand Up @@ -205,6 +208,7 @@ Object {
"version": "1.5.0",
},
],
"tag": "v1.5.2-beta.3",
"version": "1.5.2-beta.3",
}
`;
Expand All @@ -228,6 +232,7 @@ Object {
"version": "1.5.0",
},
],
"tag": "v1.5.2-beta.3",
"version": "1.5.2-beta.3",
}
`;
Expand Down Expand Up @@ -259,6 +264,7 @@ Object {
"version": "1.5.0",
},
],
"tag": "v1.5.2-beta.3",
"version": "1.5.2-beta.3",
}
`;
Expand Down Expand Up @@ -418,6 +424,7 @@ Object {
],
"releaseName": "1.1.0",
"releaseNotes": "",
"tag": "v1.1.0",
"version": "1.1.0",
}
`;
Expand Down