From 094593b0830689d6b0b3cd4ae8de51d7a06572ae Mon Sep 17 00:00:00 2001 From: Wardell Bagby <7834478+wardellbagby@users.noreply.github.com> Date: Sat, 31 Jul 2021 04:36:19 -0700 Subject: [PATCH] fix: use tag name instead of version when resolving GitHub files Modify the GitHubProvider to provide the tag name of the latest release as apart of its UpdateInfo (which is now encapsulated in the GithubUpdateInfo interface) so that it uses that same tag name later when it resolves the files to download. fix electron-userland/electron-builder/issues/6076 --- .../src/providers/GitHubProvider.ts | 38 +++++++++++-------- .../src/providers/Provider.ts | 2 +- .../snapshots/updater/nsisUpdaterTest.js.snap | 7 ++++ 3 files changed, 30 insertions(+), 17 deletions(-) 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", } `;