"use strict";
|
Object.defineProperty(exports, "__esModule", { value: true });
|
exports.computeReleaseNotes = exports.GitHubProvider = exports.BaseGitHubProvider = void 0;
|
const builder_util_runtime_1 = require("builder-util-runtime");
|
const semver = require("semver");
|
const url_1 = require("url");
|
const util_1 = require("../util");
|
const Provider_1 = require("./Provider");
|
const hrefRegExp = /\/tag\/([^/]+)$/;
|
class BaseGitHubProvider extends Provider_1.Provider {
|
constructor(options, defaultHost, runtimeOptions) {
|
super({
|
...runtimeOptions,
|
/* because GitHib uses S3 */
|
isUseMultipleRangeRequest: false,
|
});
|
this.options = options;
|
this.baseUrl = util_1.newBaseUrl(builder_util_runtime_1.githubUrl(options, defaultHost));
|
const apiHost = defaultHost === "github.com" ? "api.github.com" : defaultHost;
|
this.baseApiUrl = util_1.newBaseUrl(builder_util_runtime_1.githubUrl(options, apiHost));
|
}
|
computeGithubBasePath(result) {
|
// https://github.com/electron-userland/electron-builder/issues/1903#issuecomment-320881211
|
const host = this.options.host;
|
return host && !["github.com", "api.github.com"].includes(host) ? `/api/v3${result}` : result;
|
}
|
}
|
exports.BaseGitHubProvider = BaseGitHubProvider;
|
class GitHubProvider extends BaseGitHubProvider {
|
constructor(options, updater, runtimeOptions) {
|
super(options, "github.com", runtimeOptions);
|
this.options = options;
|
this.updater = updater;
|
}
|
async getLatestVersion() {
|
var _a, _b, _c, _d;
|
const cancellationToken = new builder_util_runtime_1.CancellationToken();
|
const feedXml = (await this.httpRequest(util_1.newUrlFromBase(`${this.basePath}.atom`, this.baseUrl), {
|
accept: "application/xml, application/atom+xml, text/xml, */*",
|
}, cancellationToken));
|
const feed = builder_util_runtime_1.parseXml(feedXml);
|
// noinspection TypeScriptValidateJSTypes
|
let latestRelease = feed.element("entry", false, `No published versions on GitHub`);
|
let tag = null;
|
try {
|
if (this.updater.allowPrerelease) {
|
const currentChannel = ((_a = this.updater) === null || _a === void 0 ? void 0 : _a.channel) || ((_b = semver.prerelease(this.updater.currentVersion)) === null || _b === void 0 ? void 0 : _b[0]) || null;
|
if (currentChannel === null) {
|
// noinspection TypeScriptValidateJSTypes
|
tag = hrefRegExp.exec(latestRelease.element("link").attribute("href"))[1];
|
}
|
else {
|
for (const element of feed.getElements("entry")) {
|
// noinspection TypeScriptValidateJSTypes
|
const hrefElement = hrefRegExp.exec(element.element("link").attribute("href"));
|
// If this is null then something is wrong and skip this release
|
if (hrefElement === null)
|
continue;
|
// This Release's Tag
|
const hrefTag = hrefElement[1];
|
//Get Channel from this release's tag
|
const hrefChannel = ((_c = semver.prerelease(hrefTag)) === null || _c === void 0 ? void 0 : _c[0]) || null;
|
const shouldFetchVersion = !currentChannel || ["alpha", "beta"].includes(currentChannel);
|
const isCustomChannel = !["alpha", "beta"].includes(String(hrefChannel));
|
// Allow moving from alpha to beta but not down
|
const channelMismatch = currentChannel === "beta" && hrefChannel === "alpha";
|
if (shouldFetchVersion && !isCustomChannel && !channelMismatch) {
|
tag = hrefTag;
|
break;
|
}
|
const isNextPreRelease = hrefChannel && hrefChannel === currentChannel;
|
if (isNextPreRelease) {
|
tag = hrefTag;
|
break;
|
}
|
}
|
}
|
}
|
else {
|
tag = await this.getLatestTagName(cancellationToken);
|
for (const element of feed.getElements("entry")) {
|
// noinspection TypeScriptValidateJSTypes
|
if (hrefRegExp.exec(element.element("link").attribute("href"))[1] === tag) {
|
latestRelease = element;
|
break;
|
}
|
}
|
}
|
}
|
catch (e) {
|
throw builder_util_runtime_1.newError(`Cannot parse releases feed: ${e.stack || e.message},\nXML:\n${feedXml}`, "ERR_UPDATER_INVALID_RELEASE_FEED");
|
}
|
if (tag == null) {
|
throw builder_util_runtime_1.newError(`No published versions on GitHub`, "ERR_UPDATER_NO_PUBLISHED_VERSIONS");
|
}
|
let rawData;
|
let channelFile = "";
|
let channelFileUrl = "";
|
const fetchData = async (channelName) => {
|
channelFile = util_1.getChannelFilename(channelName);
|
channelFileUrl = util_1.newUrlFromBase(this.getBaseDownloadPath(String(tag), channelFile), this.baseUrl);
|
const requestOptions = this.createRequestOptions(channelFileUrl);
|
try {
|
return (await this.executor.request(requestOptions, cancellationToken));
|
}
|
catch (e) {
|
if (e instanceof builder_util_runtime_1.HttpError && e.statusCode === 404) {
|
throw builder_util_runtime_1.newError(`Cannot find ${channelFile} in the latest release artifacts (${channelFileUrl}): ${e.stack || e.message}`, "ERR_UPDATER_CHANNEL_FILE_NOT_FOUND");
|
}
|
throw e;
|
}
|
};
|
try {
|
const channel = this.updater.allowPrerelease ? this.getCustomChannelName(String(((_d = semver.prerelease(tag)) === null || _d === void 0 ? void 0 : _d[0]) || "latest")) : this.getDefaultChannelName();
|
rawData = await fetchData(channel);
|
}
|
catch (e) {
|
if (this.updater.allowPrerelease) {
|
// Allow fallback to `latest.yml`
|
rawData = await fetchData(this.getDefaultChannelName());
|
}
|
else {
|
throw e;
|
}
|
}
|
const result = Provider_1.parseUpdateInfo(rawData, channelFile, channelFileUrl);
|
if (result.releaseName == null) {
|
result.releaseName = latestRelease.elementValueOrEmpty("title");
|
}
|
if (result.releaseNotes == null) {
|
result.releaseNotes = computeReleaseNotes(this.updater.currentVersion, this.updater.fullChangelog, feed, latestRelease);
|
}
|
return {
|
tag: tag,
|
...result,
|
};
|
}
|
async getLatestTagName(cancellationToken) {
|
const options = this.options;
|
// do not use API for GitHub to avoid limit, only for custom host or GitHub Enterprise
|
const url = options.host == null || options.host === "github.com"
|
? util_1.newUrlFromBase(`${this.basePath}/latest`, this.baseUrl)
|
: new url_1.URL(`${this.computeGithubBasePath(`/repos/${options.owner}/${options.repo}/releases`)}/latest`, this.baseApiUrl);
|
try {
|
const rawData = await this.httpRequest(url, { Accept: "application/json" }, cancellationToken);
|
if (rawData == null) {
|
return null;
|
}
|
const releaseInfo = JSON.parse(rawData);
|
return releaseInfo.tag_name;
|
}
|
catch (e) {
|
throw builder_util_runtime_1.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");
|
}
|
}
|
get basePath() {
|
return `/${this.options.owner}/${this.options.repo}/releases`;
|
}
|
resolveFiles(updateInfo) {
|
// still replace space to - due to backward compatibility
|
return Provider_1.resolveFiles(updateInfo, this.baseUrl, p => this.getBaseDownloadPath(updateInfo.tag, p.replace(/ /g, "-")));
|
}
|
getBaseDownloadPath(tag, fileName) {
|
return `${this.basePath}/download/${tag}/${fileName}`;
|
}
|
}
|
exports.GitHubProvider = GitHubProvider;
|
function getNoteValue(parent) {
|
const result = parent.elementValueOrEmpty("content");
|
// GitHub reports empty notes as <content>No content.</content>
|
return result === "No content." ? "" : result;
|
}
|
function computeReleaseNotes(currentVersion, isFullChangelog, feed, latestRelease) {
|
if (!isFullChangelog) {
|
return getNoteValue(latestRelease);
|
}
|
const releaseNotes = [];
|
for (const release of feed.getElements("entry")) {
|
// noinspection TypeScriptValidateJSTypes
|
const versionRelease = /\/tag\/v?([^/]+)$/.exec(release.element("link").attribute("href"))[1];
|
if (semver.lt(currentVersion, versionRelease)) {
|
releaseNotes.push({
|
version: versionRelease,
|
note: getNoteValue(release),
|
});
|
}
|
}
|
return releaseNotes.sort((a, b) => semver.rcompare(a.version, b.version));
|
}
|
exports.computeReleaseNotes = computeReleaseNotes;
|
//# sourceMappingURL=GitHubProvider.js.map
|