337 lines
10 KiB
JavaScript
337 lines
10 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
exports.GitHubPublisher = void 0;
|
||
|
|
||
|
function _builderUtil() {
|
||
|
const data = require("builder-util");
|
||
|
|
||
|
_builderUtil = function () {
|
||
|
return data;
|
||
|
};
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
function _builderUtilRuntime() {
|
||
|
const data = require("builder-util-runtime");
|
||
|
|
||
|
_builderUtilRuntime = function () {
|
||
|
return data;
|
||
|
};
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
function _nodeHttpExecutor() {
|
||
|
const data = require("builder-util/out/nodeHttpExecutor");
|
||
|
|
||
|
_nodeHttpExecutor = function () {
|
||
|
return data;
|
||
|
};
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
function _lazyVal() {
|
||
|
const data = require("lazy-val");
|
||
|
|
||
|
_lazyVal = function () {
|
||
|
return data;
|
||
|
};
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
function _mime() {
|
||
|
const data = _interopRequireDefault(require("mime"));
|
||
|
|
||
|
_mime = function () {
|
||
|
return data;
|
||
|
};
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
function _url() {
|
||
|
const data = require("url");
|
||
|
|
||
|
_url = function () {
|
||
|
return data;
|
||
|
};
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
function _publisher() {
|
||
|
const data = require("./publisher");
|
||
|
|
||
|
_publisher = function () {
|
||
|
return data;
|
||
|
};
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
|
||
|
class GitHubPublisher extends _publisher().HttpPublisher {
|
||
|
constructor(context, info, version, options = {}) {
|
||
|
super(context, true);
|
||
|
this.info = info;
|
||
|
this.version = version;
|
||
|
this.options = options;
|
||
|
this._release = new (_lazyVal().Lazy)(() => this.token === "__test__" ? Promise.resolve(null) : this.getOrCreateRelease());
|
||
|
this.providerName = "GitHub";
|
||
|
this.releaseLogFields = null;
|
||
|
let token = info.token;
|
||
|
|
||
|
if ((0, _builderUtil().isEmptyOrSpaces)(token)) {
|
||
|
token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
|
||
|
|
||
|
if ((0, _builderUtil().isEmptyOrSpaces)(token)) {
|
||
|
throw new (_builderUtil().InvalidConfigurationError)(`GitHub Personal Access Token is not set, neither programmatically, nor using env "GH_TOKEN"`);
|
||
|
}
|
||
|
|
||
|
token = token.trim();
|
||
|
|
||
|
if (!(0, _builderUtil().isTokenCharValid)(token)) {
|
||
|
throw new (_builderUtil().InvalidConfigurationError)(`GitHub Personal Access Token (${JSON.stringify(token)}) contains invalid characters, please check env "GH_TOKEN"`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.token = token;
|
||
|
|
||
|
if (version.startsWith("v")) {
|
||
|
throw new (_builderUtil().InvalidConfigurationError)(`Version must not start with "v": ${version}`);
|
||
|
}
|
||
|
|
||
|
this.tag = info.vPrefixedTagName === false ? version : `v${version}`;
|
||
|
|
||
|
if ((0, _builderUtil().isEnvTrue)(process.env.EP_DRAFT)) {
|
||
|
this.releaseType = "draft";
|
||
|
|
||
|
_builderUtil().log.info({
|
||
|
reason: "env EP_DRAFT is set to true"
|
||
|
}, "GitHub provider release type is set to draft");
|
||
|
} else if ((0, _builderUtil().isEnvTrue)(process.env.EP_PRE_RELEASE) || (0, _builderUtil().isEnvTrue)(process.env.EP_PRELEASE)
|
||
|
/* https://github.com/electron-userland/electron-builder/issues/2878 */
|
||
|
) {
|
||
|
this.releaseType = "prerelease";
|
||
|
|
||
|
_builderUtil().log.info({
|
||
|
reason: "env EP_PRE_RELEASE is set to true"
|
||
|
}, "GitHub provider release type is set to prerelease");
|
||
|
} else if (info.releaseType != null) {
|
||
|
this.releaseType = info.releaseType;
|
||
|
} else if (options.prerelease) {
|
||
|
this.releaseType = "prerelease";
|
||
|
} else {
|
||
|
this.releaseType = options.draft === false ? "release" : "draft";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async getOrCreateRelease() {
|
||
|
const logFields = {
|
||
|
tag: this.tag,
|
||
|
version: this.version
|
||
|
}; // we don't use "Get a release by tag name" because "tag name" means existing git tag, but we draft release and don't create git tag
|
||
|
|
||
|
const releases = await this.githubRequest(`/repos/${this.info.owner}/${this.info.repo}/releases`, this.token);
|
||
|
|
||
|
for (const release of releases) {
|
||
|
if (!(release.tag_name === this.tag || release.tag_name === this.version)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (release.draft) {
|
||
|
return release;
|
||
|
} // https://github.com/electron-userland/electron-builder/issues/1197
|
||
|
// https://github.com/electron-userland/electron-builder/issues/2072
|
||
|
|
||
|
|
||
|
if (this.releaseType === "draft") {
|
||
|
this.releaseLogFields = Object.assign({
|
||
|
reason: "existing type not compatible with publishing type"
|
||
|
}, logFields, {
|
||
|
existingType: release.prerelease ? "pre-release" : "release",
|
||
|
publishingType: this.releaseType
|
||
|
});
|
||
|
|
||
|
_builderUtil().log.warn(this.releaseLogFields, "GitHub release not created");
|
||
|
|
||
|
return null;
|
||
|
} // https://github.com/electron-userland/electron-builder/issues/1133
|
||
|
// https://github.com/electron-userland/electron-builder/issues/2074
|
||
|
// if release created < 2 hours — allow to upload
|
||
|
|
||
|
|
||
|
const publishedAt = release.published_at == null ? null : Date.parse(release.published_at);
|
||
|
|
||
|
if (publishedAt != null && Date.now() - publishedAt > 2 * 3600 * 1000) {
|
||
|
// https://github.com/electron-userland/electron-builder/issues/1183#issuecomment-275867187
|
||
|
this.releaseLogFields = Object.assign({
|
||
|
reason: "existing release published more than 2 hours ago"
|
||
|
}, logFields, {
|
||
|
date: new Date(publishedAt).toString()
|
||
|
});
|
||
|
|
||
|
_builderUtil().log.warn(this.releaseLogFields, "GitHub release not created");
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return release;
|
||
|
} // https://github.com/electron-userland/electron-builder/issues/1835
|
||
|
|
||
|
|
||
|
if (this.options.publish === "always" || (0, _publisher().getCiTag)() != null) {
|
||
|
_builderUtil().log.info(Object.assign({
|
||
|
reason: "release doesn't exist"
|
||
|
}, logFields), `creating GitHub release`);
|
||
|
|
||
|
return this.createRelease();
|
||
|
}
|
||
|
|
||
|
this.releaseLogFields = Object.assign({
|
||
|
reason: "release doesn't exist and not created because \"publish\" is not \"always\" and build is not on tag"
|
||
|
}, logFields);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
async overwriteArtifact(fileName, release) {
|
||
|
// delete old artifact and re-upload
|
||
|
_builderUtil().log.warn({
|
||
|
file: fileName,
|
||
|
reason: "already exists on GitHub"
|
||
|
}, "overwrite published file");
|
||
|
|
||
|
const assets = await this.githubRequest(`/repos/${this.info.owner}/${this.info.repo}/releases/${release.id}/assets`, this.token, null);
|
||
|
|
||
|
for (const asset of assets) {
|
||
|
if (asset.name === fileName) {
|
||
|
await this.githubRequest(`/repos/${this.info.owner}/${this.info.repo}/releases/assets/${asset.id}`, this.token, null, "DELETE");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_builderUtil().log.debug({
|
||
|
file: fileName,
|
||
|
reason: "not found on GitHub"
|
||
|
}, "trying to upload again");
|
||
|
}
|
||
|
|
||
|
async doUpload(fileName, arch, dataLength, requestProcessor) {
|
||
|
const release = await this._release.value;
|
||
|
|
||
|
if (release == null) {
|
||
|
_builderUtil().log.warn(Object.assign({
|
||
|
file: fileName
|
||
|
}, this.releaseLogFields), "skipped publishing");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const parsedUrl = (0, _url().parse)(release.upload_url.substring(0, release.upload_url.indexOf("{")) + "?name=" + fileName);
|
||
|
return await this.doUploadFile(0, parsedUrl, fileName, dataLength, requestProcessor, release);
|
||
|
}
|
||
|
|
||
|
doUploadFile(attemptNumber, parsedUrl, fileName, dataLength, requestProcessor, release) {
|
||
|
return _nodeHttpExecutor().httpExecutor.doApiRequest((0, _builderUtilRuntime().configureRequestOptions)({
|
||
|
hostname: parsedUrl.hostname,
|
||
|
path: parsedUrl.path,
|
||
|
method: "POST",
|
||
|
headers: {
|
||
|
accept: "application/vnd.github.v3+json",
|
||
|
"Content-Type": _mime().default.getType(fileName) || "application/octet-stream",
|
||
|
"Content-Length": dataLength
|
||
|
}
|
||
|
}, this.token), this.context.cancellationToken, requestProcessor).catch(e => {
|
||
|
if (e.statusCode === 422 && e.description != null && e.description.errors != null && e.description.errors[0].code === "already_exists") {
|
||
|
return this.overwriteArtifact(fileName, release).then(() => this.doUploadFile(attemptNumber, parsedUrl, fileName, dataLength, requestProcessor, release));
|
||
|
}
|
||
|
|
||
|
if (attemptNumber > 3) {
|
||
|
return Promise.reject(e);
|
||
|
} else {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const newAttemptNumber = attemptNumber + 1;
|
||
|
setTimeout(() => {
|
||
|
this.doUploadFile(newAttemptNumber, parsedUrl, fileName, dataLength, requestProcessor, release).then(resolve).catch(reject);
|
||
|
}, newAttemptNumber * 2000);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
createRelease() {
|
||
|
return this.githubRequest(`/repos/${this.info.owner}/${this.info.repo}/releases`, this.token, {
|
||
|
tag_name: this.tag,
|
||
|
name: this.version,
|
||
|
draft: this.releaseType === "draft",
|
||
|
prerelease: this.releaseType === "prerelease"
|
||
|
});
|
||
|
} // test only
|
||
|
//noinspection JSUnusedGlobalSymbols
|
||
|
|
||
|
|
||
|
async getRelease() {
|
||
|
return this.githubRequest(`/repos/${this.info.owner}/${this.info.repo}/releases/${(await this._release.value).id}`, this.token);
|
||
|
} //noinspection JSUnusedGlobalSymbols
|
||
|
|
||
|
|
||
|
async deleteRelease() {
|
||
|
if (!this._release.hasValue) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const release = await this._release.value;
|
||
|
|
||
|
for (let i = 0; i < 3; i++) {
|
||
|
try {
|
||
|
return await this.githubRequest(`/repos/${this.info.owner}/${this.info.repo}/releases/${release.id}`, this.token, null, "DELETE");
|
||
|
} catch (e) {
|
||
|
if (e instanceof _builderUtilRuntime().HttpError) {
|
||
|
if (e.statusCode === 404) {
|
||
|
_builderUtil().log.warn({
|
||
|
releaseId: release.id,
|
||
|
reason: "doesn't exist"
|
||
|
}, "cannot delete release");
|
||
|
|
||
|
return;
|
||
|
} else if (e.statusCode === 405 || e.statusCode === 502) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
throw e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_builderUtil().log.warn({
|
||
|
releaseId: release.id
|
||
|
}, "cannot delete release");
|
||
|
}
|
||
|
|
||
|
githubRequest(path, token, data = null, method) {
|
||
|
// host can contains port, but node http doesn't support host as url does
|
||
|
const baseUrl = (0, _url().parse)(`https://${this.info.host || "api.github.com"}`);
|
||
|
return (0, _builderUtilRuntime().parseJson)(_nodeHttpExecutor().httpExecutor.request((0, _builderUtilRuntime().configureRequestOptions)({
|
||
|
hostname: baseUrl.hostname,
|
||
|
port: baseUrl.port,
|
||
|
path: this.info.host != null && this.info.host !== "github.com" ? `/api/v3${path.startsWith("/") ? path : `/${path}`}` : path,
|
||
|
headers: {
|
||
|
accept: "application/vnd.github.v3+json"
|
||
|
}
|
||
|
}, token, method), this.context.cancellationToken, data));
|
||
|
}
|
||
|
|
||
|
toString() {
|
||
|
return `Github (owner: ${this.info.owner}, project: ${this.info.repo}, version: ${this.version})`;
|
||
|
}
|
||
|
|
||
|
} exports.GitHubPublisher = GitHubPublisher;
|
||
|
// __ts-babel@6.0.4
|
||
|
//# sourceMappingURL=gitHubPublisher.js.map
|