diff --git a/empty-ties-argue.md b/empty-ties-argue.md new file mode 100644 index 0000000000..7a1e87132f --- /dev/null +++ b/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 diff --git a/packages/electron-updater/src/providers/GitHubProvider.ts b/packages/electron-updater/src/providers/GitHubProvider.ts index 9c524865ce..eaa5756130 100644 --- a/packages/electron-updater/src/providers/GitHubProvider.ts +++ b/packages/electron-updater/src/providers/GitHubProvider.ts @@ -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 extends Provider { // so, we don't need to parse port (because node http doesn't support host as url does) protected readonly baseUrl: URL @@ -32,12 +35,12 @@ export abstract class BaseGitHubProvider extends Provider< } } -export class GitHubProvider extends BaseGitHubProvider { +export class GitHubProvider extends BaseGitHubProvider { constructor(protected readonly options: GithubOptions, private readonly updater: AppUpdater, runtimeOptions: ProviderRuntimeOptions) { super(options, "github.com", runtimeOptions) } - async getLatestVersion(): Promise { + async getLatestVersion(): Promise { const cancellationToken = new CancellationToken() const feedXml: string = (await this.httpRequest( @@ -51,16 +54,16 @@ export class GitHubProvider extends BaseGitHubProvider { 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 } @@ -70,12 +73,12 @@ export class GitHubProvider extends BaseGitHubProvider { 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 { @@ -95,10 +98,13 @@ export class GitHubProvider extends BaseGitHubProvider { 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 { + private async getLatestTagName(cancellationToken: CancellationToken): Promise { const options = this.options // do not use API for GitHub to avoid limit, only for custom host or GitHub Enterprise const url = @@ -112,7 +118,7 @@ export class GitHubProvider extends BaseGitHubProvider { } 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") } @@ -122,13 +128,13 @@ export class GitHubProvider extends BaseGitHubProvider { return `/${this.options.owner}/${this.options.repo}/releases` } - resolveFiles(updateInfo: UpdateInfo): Array { + resolveFiles(updateInfo: GithubUpdateInfo): Array { // 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}` } } diff --git a/packages/electron-updater/src/providers/Provider.ts b/packages/electron-updater/src/providers/Provider.ts index 5fdbb3baf5..f8f9ee0975 100644 --- a/packages/electron-updater/src/providers/Provider.ts +++ b/packages/electron-updater/src/providers/Provider.ts @@ -55,7 +55,7 @@ export abstract class Provider { abstract getLatestVersion(): Promise - abstract resolveFiles(updateInfo: UpdateInfo): Array + abstract resolveFiles(updateInfo: T): Array /** * Method to perform API request only to resolve update info, but not to download update. diff --git a/test/snapshots/updater/nsisUpdaterTest.js.snap b/test/snapshots/updater/nsisUpdaterTest.js.snap index 24fa64ae55..97a7b10dc5 100644 --- a/test/snapshots/updater/nsisUpdaterTest.js.snap +++ b/test/snapshots/updater/nsisUpdaterTest.js.snap @@ -107,6 +107,7 @@ Object { ], "releaseName": "v1.5.2-beta.3", "releaseNotes": "", + "tag": "v1.5.2-beta.3", "version": "1.5.2-beta.3", } `; @@ -160,6 +161,7 @@ Object { ], "releaseName": "1.1.0", "releaseNotes": "", + "tag": "v1.1.0", "version": "1.1.0", } `; @@ -174,6 +176,7 @@ Object { ], "releaseName": "1.1.0", "releaseNotes": "", + "tag": "v1.1.0", "version": "1.1.0", } `; @@ -205,6 +208,7 @@ Object { "version": "1.5.0", }, ], + "tag": "v1.5.2-beta.3", "version": "1.5.2-beta.3", } `; @@ -228,6 +232,7 @@ Object { "version": "1.5.0", }, ], + "tag": "v1.5.2-beta.3", "version": "1.5.2-beta.3", } `; @@ -259,6 +264,7 @@ Object { "version": "1.5.0", }, ], + "tag": "v1.5.2-beta.3", "version": "1.5.2-beta.3", } `; @@ -418,6 +424,7 @@ Object { ], "releaseName": "1.1.0", "releaseNotes": "", + "tag": "v1.1.0", "version": "1.1.0", } `;