Initial commit

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
dinlo
2026-05-31 18:44:04 +08:00
commit 436a9631fc
8616 changed files with 1389957 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
import { Arch } from "builder-util";
import { BitbucketOptions } from "builder-util-runtime/out/publishOptions";
import { ClientRequest } from "http";
import { PublishContext } from "./";
import { HttpPublisher } from "./httpPublisher";
export declare class BitbucketPublisher extends HttpPublisher {
readonly providerName = "bitbucket";
readonly hostname = "api.bitbucket.org";
private readonly info;
private readonly auth;
private readonly basePath;
constructor(context: PublishContext, info: BitbucketOptions);
protected doUpload(fileName: string, _arch: Arch, _dataLength: number, _requestProcessor: (request: ClientRequest, reject: (error: Error) => void) => void, file: string): Promise<any>;
deleteRelease(filename: string): Promise<void>;
toString(): string;
static convertAppPassword(username: string, token: string): string;
}
+59
View File
@@ -0,0 +1,59 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BitbucketPublisher = void 0;
const builder_util_1 = require("builder-util");
const builder_util_runtime_1 = require("builder-util-runtime");
const FormData = require("form-data");
const fs_extra_1 = require("fs-extra");
const httpPublisher_1 = require("./httpPublisher");
class BitbucketPublisher extends httpPublisher_1.HttpPublisher {
constructor(context, info) {
super(context);
this.providerName = "bitbucket";
this.hostname = "api.bitbucket.org";
const token = info.token || process.env.BITBUCKET_TOKEN || null;
const username = info.username || process.env.BITBUCKET_USERNAME || null;
if ((0, builder_util_1.isEmptyOrSpaces)(token)) {
throw new builder_util_1.InvalidConfigurationError(`Bitbucket token is not set using env "BITBUCKET_TOKEN" (see https://www.electron.build/publish#BitbucketOptions)`);
}
if ((0, builder_util_1.isEmptyOrSpaces)(username)) {
builder_util_1.log.warn('No Bitbucket username provided via "BITBUCKET_USERNAME". Defaulting to use repo owner.');
}
this.info = info;
this.auth = BitbucketPublisher.convertAppPassword(username !== null && username !== void 0 ? username : this.info.owner, token);
this.basePath = `/2.0/repositories/${this.info.owner}/${this.info.slug}/downloads`;
}
doUpload(fileName, _arch, _dataLength, _requestProcessor, file) {
return builder_util_runtime_1.HttpExecutor.retryOnServerError(async () => {
const fileContent = await (0, fs_extra_1.readFile)(file);
const form = new FormData();
form.append("files", fileContent, fileName);
const upload = {
hostname: this.hostname,
path: this.basePath,
headers: form.getHeaders(),
timeout: this.info.timeout || undefined,
};
await builder_util_1.httpExecutor.doApiRequest((0, builder_util_runtime_1.configureRequestOptions)(upload, this.auth, "POST"), this.context.cancellationToken, it => form.pipe(it));
return fileName;
});
}
async deleteRelease(filename) {
const req = {
hostname: this.hostname,
path: `${this.basePath}/${filename}`,
timeout: this.info.timeout || undefined,
};
await builder_util_1.httpExecutor.request((0, builder_util_runtime_1.configureRequestOptions)(req, this.auth, "DELETE"), this.context.cancellationToken);
}
toString() {
const { owner, slug, channel } = this.info;
return `Bitbucket (owner: ${owner}, slug: ${slug}, channel: ${channel})`;
}
static convertAppPassword(username, token) {
const base64encodedData = Buffer.from(`${username}:${token.trim()}`).toString("base64");
return `Basic ${base64encodedData}`;
}
}
exports.BitbucketPublisher = BitbucketPublisher;
//# sourceMappingURL=bitbucketPublisher.js.map
File diff suppressed because one or more lines are too long
+36
View File
@@ -0,0 +1,36 @@
import { Arch } from "builder-util";
import { GithubOptions } from "builder-util-runtime";
import { ClientRequest } from "http";
import { Lazy } from "lazy-val";
import { HttpPublisher } from "./httpPublisher";
import { PublishContext, PublishOptions } from "./index";
export interface Release {
id: number;
tag_name: string;
draft: boolean;
prerelease: boolean;
published_at: string;
upload_url: string;
}
export declare class GitHubPublisher extends HttpPublisher {
private readonly info;
private readonly version;
private readonly options;
private readonly tag;
readonly _release: Lazy<any>;
private readonly token;
readonly providerName = "github";
private readonly releaseType;
private releaseLogFields;
constructor(context: PublishContext, info: GithubOptions, version: string, options?: PublishOptions);
private getOrCreateRelease;
private overwriteArtifact;
protected doUpload(fileName: string, arch: Arch, dataLength: number, requestProcessor: (request: ClientRequest, reject: (error: Error) => void) => void): Promise<any>;
private doUploadFile;
private doesErrorMeanAlreadyExists;
private createRelease;
getRelease(): Promise<any>;
deleteRelease(): Promise<any>;
private githubRequest;
toString(): string;
}
+226
View File
@@ -0,0 +1,226 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GitHubPublisher = void 0;
const builder_util_1 = require("builder-util");
const builder_util_runtime_1 = require("builder-util-runtime");
const lazy_val_1 = require("lazy-val");
const mime = require("mime");
const url_1 = require("url");
const httpPublisher_1 = require("./httpPublisher");
const publisher_1 = require("./publisher");
class GitHubPublisher extends httpPublisher_1.HttpPublisher {
constructor(context, info, version, options = {}) {
super(context, true);
this.info = info;
this.version = version;
this.options = options;
this._release = new lazy_val_1.Lazy(() => (this.token === "__test__" ? Promise.resolve(null) : this.getOrCreateRelease()));
this.providerName = "github";
this.releaseLogFields = null;
let token = info.token;
if ((0, builder_util_1.isEmptyOrSpaces)(token) || process.env.GITHUB_RELEASE_TOKEN) {
token = process.env.GITHUB_RELEASE_TOKEN ? process.env.GITHUB_RELEASE_TOKEN : process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
if ((0, builder_util_1.isEmptyOrSpaces)(token)) {
throw new builder_util_1.InvalidConfigurationError(`GitHub Personal Access Token is not set, neither programmatically, nor using env "GH_TOKEN"`);
}
token = token.trim();
if (!(0, builder_util_1.isTokenCharValid)(token)) {
throw new builder_util_1.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 builder_util_1.InvalidConfigurationError(`Version must not start with "v": ${version}`);
}
this.tag = (0, builder_util_runtime_1.githubTagPrefix)(info) + version;
if ((0, builder_util_1.isEnvTrue)(process.env.EP_DRAFT)) {
this.releaseType = "draft";
builder_util_1.log.info({ reason: "env EP_DRAFT is set to true" }, "GitHub provider release type is set to draft");
}
else if ((0, builder_util_1.isEnvTrue)(process.env.EP_PRE_RELEASE) || (0, builder_util_1.isEnvTrue)(process.env.EP_PRELEASE) /* https://github.com/electron-userland/electron-builder/issues/2878 */) {
this.releaseType = "prerelease";
builder_util_1.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 {
// noinspection PointlessBooleanExpressionJS
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 = {
reason: "existing type not compatible with publishing type",
...logFields,
existingType: release.prerelease ? "pre-release" : "release",
publishingType: this.releaseType,
};
builder_util_1.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 (!(0, builder_util_1.isEnvTrue)(process.env.EP_GH_IGNORE_TIME) && publishedAt != null && Date.now() - publishedAt > 2 * 3600 * 1000) {
// https://github.com/electron-userland/electron-builder/issues/1183#issuecomment-275867187
this.releaseLogFields = {
reason: "existing release published more than 2 hours ago",
...logFields,
date: new Date(publishedAt).toString(),
};
builder_util_1.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_1.getCiTag)() != null) {
builder_util_1.log.info({
reason: "release doesn't exist",
...logFields,
}, `creating GitHub release`);
return this.createRelease();
}
this.releaseLogFields = {
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
builder_util_1.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;
}
}
builder_util_1.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) {
builder_util_1.log.warn({ file: fileName, ...this.releaseLogFields }, "skipped publishing");
return;
}
const parsedUrl = (0, url_1.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 builder_util_1.httpExecutor
.doApiRequest((0, builder_util_runtime_1.configureRequestOptions)({
protocol: parsedUrl.protocol,
hostname: parsedUrl.hostname,
path: parsedUrl.path,
method: "POST",
headers: {
accept: "application/vnd.github.v3+json",
"Content-Type": mime.getType(fileName) || "application/octet-stream",
"Content-Length": dataLength,
},
timeout: this.info.timeout || undefined,
}, this.token), this.context.cancellationToken, requestProcessor)
.catch((e) => {
if (attemptNumber > 3) {
return Promise.reject(e);
}
else if (this.doesErrorMeanAlreadyExists(e)) {
return this.overwriteArtifact(fileName, release).then(() => this.doUploadFile(attemptNumber + 1, parsedUrl, fileName, dataLength, requestProcessor, release));
}
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);
});
}
});
}
doesErrorMeanAlreadyExists(e) {
if (!e.description) {
return false;
}
const desc = e.description;
const descIncludesAlreadyExists = (desc.includes("errors") && desc.includes("already_exists")) || (desc.errors && desc.errors.length >= 1 && desc.errors[0].code === "already_exists");
return e.statusCode === 422 && descIncludesAlreadyExists;
}
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 builder_util_runtime_1.HttpError) {
if (e.statusCode === 404) {
builder_util_1.log.warn({ releaseId: release.id, reason: "doesn't exist" }, "cannot delete release");
return;
}
else if (e.statusCode === 405 || e.statusCode === 502) {
continue;
}
}
throw e;
}
}
builder_util_1.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_1.parse)(`https://${this.info.host || "api.github.com"}`);
return (0, builder_util_runtime_1.parseJson)(builder_util_1.httpExecutor.request((0, builder_util_runtime_1.configureRequestOptions)({
protocol: baseUrl.protocol,
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" },
timeout: this.info.timeout || undefined,
}, token, method), this.context.cancellationToken, data));
}
toString() {
return `Github (owner: ${this.info.owner}, project: ${this.info.repo}, version: ${this.version})`;
}
}
exports.GitHubPublisher = GitHubPublisher;
//# sourceMappingURL=gitHubPublisher.js.map
File diff suppressed because one or more lines are too long
+36
View File
@@ -0,0 +1,36 @@
import { Arch } from "builder-util";
import { GitlabOptions } from "builder-util-runtime";
import { ClientRequest } from "http";
import { Lazy } from "lazy-val";
import { HttpPublisher } from "./httpPublisher";
import { PublishContext } from "./index";
type RequestProcessor = (request: ClientRequest, reject: (error: Error) => void) => void;
export declare class GitlabPublisher extends HttpPublisher {
private readonly info;
private readonly version;
private readonly tag;
readonly _release: Lazy<any>;
private readonly token;
private readonly host;
private readonly baseApiPath;
private readonly projectId;
readonly providerName = "gitlab";
private releaseLogFields;
constructor(context: PublishContext, info: GitlabOptions, version: string);
private getOrCreateRelease;
private getExistingRelease;
private getDefaultBranch;
private createRelease;
protected doUpload(fileName: string, arch: Arch, dataLength: number, requestProcessor: RequestProcessor, filePath: string): Promise<any>;
private uploadFileAndReturnAssetPath;
private addReleaseAssetLink;
private uploadToProjectUpload;
private uploadToGenericPackages;
private buildProjectUrl;
private resolveProjectId;
private gitlabRequest;
private setAuthHeaderForToken;
private categorizeGitlabError;
toString(): string;
}
export {};
+290
View File
@@ -0,0 +1,290 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GitlabPublisher = void 0;
const builder_util_1 = require("builder-util");
const fs_1 = require("fs");
const promises_1 = require("fs/promises");
const promises_2 = require("fs/promises");
const builder_util_runtime_1 = require("builder-util-runtime");
const lazy_val_1 = require("lazy-val");
const mime = require("mime");
const FormData = require("form-data");
const url_1 = require("url");
const httpPublisher_1 = require("./httpPublisher");
class GitlabPublisher extends httpPublisher_1.HttpPublisher {
constructor(context, info, version) {
super(context, true);
this.info = info;
this.version = version;
this._release = new lazy_val_1.Lazy(() => (this.token === "__test__" ? Promise.resolve(null) : this.getOrCreateRelease()));
this.providerName = "gitlab";
this.releaseLogFields = null;
let token = info.token || null;
if ((0, builder_util_1.isEmptyOrSpaces)(token)) {
token = process.env.GITLAB_TOKEN || null;
if ((0, builder_util_1.isEmptyOrSpaces)(token)) {
throw new builder_util_1.InvalidConfigurationError(`GitLab Personal Access Token is not set, neither programmatically, nor using env "GITLAB_TOKEN"`);
}
token = token.trim();
if (!(0, builder_util_1.isTokenCharValid)(token)) {
throw new builder_util_1.InvalidConfigurationError(`GitLab Personal Access Token (${JSON.stringify(token)}) contains invalid characters, please check env "GITLAB_TOKEN"`);
}
}
this.token = token;
this.host = info.host || "gitlab.com";
this.projectId = this.resolveProjectId();
this.baseApiPath = `https://${this.host}/api/v4`;
if (version.startsWith("v")) {
throw new builder_util_1.InvalidConfigurationError(`Version must not start with "v": ${version}`);
}
// By default, we prefix the version with "v"
this.tag = info.vPrefixedTagName === false ? version : `v${version}`;
}
async getOrCreateRelease() {
const logFields = {
tag: this.tag,
version: this.version,
};
try {
const existingRelease = await this.getExistingRelease();
if (existingRelease) {
return existingRelease;
}
// Create new release if it doesn't exist
return this.createRelease();
}
catch (error) {
const errorInfo = this.categorizeGitlabError(error);
builder_util_1.log.error({
...logFields,
error: error.message,
errorType: errorInfo.type,
statusCode: errorInfo.statusCode,
}, "Failed to get or create GitLab release");
throw error;
}
}
async getExistingRelease() {
const url = this.buildProjectUrl("/releases");
const releases = await this.gitlabRequest(url);
for (const release of releases) {
if (release.tag_name === this.tag) {
return release;
}
}
return null;
}
async getDefaultBranch() {
try {
const url = this.buildProjectUrl();
const project = await this.gitlabRequest(url);
return project.default_branch || "main";
}
catch (error) {
builder_util_1.log.warn({ error: error.message }, "Failed to get default branch, using 'main' as fallback");
return "main";
}
}
async createRelease() {
const releaseName = this.info.vPrefixedTagName === false ? this.version : `v${this.version}`;
const branchName = await this.getDefaultBranch();
const releaseData = {
tag_name: this.tag,
name: releaseName,
description: `Release ${releaseName}`,
ref: branchName,
};
builder_util_1.log.debug({
tag: this.tag,
name: releaseName,
ref: branchName,
projectId: this.projectId,
}, "creating GitLab release");
const url = this.buildProjectUrl("/releases");
return this.gitlabRequest(url, releaseData, "POST");
}
async doUpload(fileName, arch, dataLength, requestProcessor, filePath) {
const release = await this._release.value;
if (release == null) {
builder_util_1.log.warn({ file: fileName, ...this.releaseLogFields }, "skipped publishing");
return;
}
const logFields = {
file: fileName,
arch: builder_util_1.Arch[arch],
size: dataLength,
uploadTarget: this.info.uploadTarget || "project_upload",
};
try {
builder_util_1.log.debug(logFields, "starting GitLab upload");
const assetPath = await this.uploadFileAndReturnAssetPath(fileName, dataLength, requestProcessor, filePath);
// Add the uploaded file as a release asset link
if (assetPath) {
await this.addReleaseAssetLink(fileName, assetPath);
builder_util_1.log.info({ ...logFields, assetPath }, "GitLab upload completed successfully");
}
else {
builder_util_1.log.warn({ ...logFields }, "No asset URL found for file");
}
return assetPath;
}
catch (e) {
const errorInfo = this.categorizeGitlabError(e);
builder_util_1.log.error({
...logFields,
error: e.message,
errorType: errorInfo.type,
statusCode: errorInfo.statusCode,
}, "GitLab upload failed");
throw e;
}
}
async uploadFileAndReturnAssetPath(fileName, dataLength, requestProcessor, filePath) {
// Default to project_upload method
const uploadTarget = this.info.uploadTarget || "project_upload";
let assetPath;
if (uploadTarget === "generic_package") {
await this.uploadToGenericPackages(fileName, dataLength, requestProcessor);
// For generic packages, construct the download URL
const projectId = encodeURIComponent(this.projectId);
assetPath = `${this.baseApiPath}/projects/${projectId}/packages/generic/releases/${this.version}/${fileName}`;
}
else {
// Default to project_upload
const uploadResult = await this.uploadToProjectUpload(fileName, filePath);
// For project uploads, construct full URL from relative path
assetPath = `https://${this.host}${uploadResult.full_path}`;
}
return assetPath;
}
async addReleaseAssetLink(fileName, assetUrl) {
try {
const linkData = {
name: fileName,
url: assetUrl,
link_type: "other",
};
const url = this.buildProjectUrl(`/releases/${this.tag}/assets/links`);
await this.gitlabRequest(url, linkData, "POST");
builder_util_1.log.debug({ fileName, assetUrl }, "Successfully linked asset to GitLab release");
}
catch (e) {
builder_util_1.log.warn({ fileName, assetUrl, error: e.message }, "Failed to link asset to GitLab release");
// Don't throw - the file was uploaded successfully, linking is optional
}
}
async uploadToProjectUpload(fileName, filePath) {
const uploadUrl = `${this.baseApiPath}/projects/${encodeURIComponent(this.projectId)}/uploads`;
const parsedUrl = new url_1.URL(uploadUrl);
// Check file size to determine upload method
const stats = await (0, promises_1.stat)(filePath);
const fileSize = stats.size;
const STREAMING_THRESHOLD = 50 * 1024 * 1024; // 50MB
const form = new FormData();
if (fileSize > STREAMING_THRESHOLD) {
// Use streaming for large files
builder_util_1.log.debug({ fileName, fileSize }, "using streaming upload for large file");
const fileStream = (0, fs_1.createReadStream)(filePath);
form.append("file", fileStream, fileName);
}
else {
// Use buffer for small files
builder_util_1.log.debug({ fileName, fileSize }, "using buffer upload for small file");
const fileContent = await (0, promises_2.readFile)(filePath);
form.append("file", fileContent, fileName);
}
const response = await builder_util_1.httpExecutor.doApiRequest((0, builder_util_runtime_1.configureRequestOptions)({
protocol: parsedUrl.protocol,
hostname: parsedUrl.hostname,
port: parsedUrl.port,
path: parsedUrl.pathname,
headers: { ...form.getHeaders(), ...this.setAuthHeaderForToken(this.token) },
timeout: this.info.timeout || undefined,
}, null, "POST"), this.context.cancellationToken, (it) => form.pipe(it));
// Parse the JSON response string
return JSON.parse(response);
}
async uploadToGenericPackages(fileName, dataLength, requestProcessor) {
const uploadUrl = `${this.baseApiPath}/projects/${encodeURIComponent(this.projectId)}/packages/generic/releases/${this.version}/${fileName}`;
const parsedUrl = new url_1.URL(uploadUrl);
return builder_util_1.httpExecutor.doApiRequest((0, builder_util_runtime_1.configureRequestOptions)({
protocol: parsedUrl.protocol,
hostname: parsedUrl.hostname,
port: parsedUrl.port,
path: parsedUrl.pathname,
headers: { "Content-Length": dataLength, "Content-Type": mime.getType(fileName) || "application/octet-stream", ...this.setAuthHeaderForToken(this.token) },
timeout: this.info.timeout || undefined,
}, null, "PUT"), this.context.cancellationToken, requestProcessor);
}
buildProjectUrl(path = "") {
return new url_1.URL(`${this.baseApiPath}/projects/${encodeURIComponent(this.projectId)}${path}`);
}
resolveProjectId() {
if (this.info.projectId) {
return String(this.info.projectId);
}
throw new builder_util_1.InvalidConfigurationError("GitLab project ID is not specified, please set it in configuration.");
}
gitlabRequest(url, data = null, method = "GET") {
return (0, builder_util_runtime_1.parseJson)(builder_util_1.httpExecutor.request((0, builder_util_runtime_1.configureRequestOptions)({
port: url.port,
path: url.pathname,
protocol: url.protocol,
hostname: url.hostname,
headers: { "Content-Type": "application/json", ...this.setAuthHeaderForToken(this.token) },
timeout: this.info.timeout || undefined,
}, null, method), this.context.cancellationToken, data));
}
setAuthHeaderForToken(token) {
const headers = {};
if (token != null) {
// If the token starts with "Bearer", it is an OAuth application secret
// Note that the original gitlab token would not start with "Bearer"
// it might start with "gloas-", if so user needs to add "Bearer " prefix to the token
if (token.startsWith("Bearer")) {
headers.authorization = token;
}
else {
headers["PRIVATE-TOKEN"] = token;
}
}
return headers;
}
categorizeGitlabError(error) {
if (error instanceof builder_util_runtime_1.HttpError) {
const statusCode = error.statusCode;
switch (statusCode) {
case 401:
return { type: "authentication", statusCode };
case 403:
return { type: "authorization", statusCode };
case 404:
return { type: "not_found", statusCode };
case 409:
return { type: "conflict", statusCode };
case 413:
return { type: "file_too_large", statusCode };
case 422:
return { type: "validation_error", statusCode };
case 429:
return { type: "rate_limit", statusCode };
case 500:
case 502:
case 503:
case 504:
return { type: "server_error", statusCode };
default:
return { type: "http_error", statusCode };
}
}
if (error.code === "ECONNRESET" || error.code === "ENOTFOUND" || error.code === "ETIMEDOUT") {
return { type: "network_error" };
}
return { type: "unknown_error" };
}
toString() {
return `GitLab (project: ${this.projectId}, version: ${this.version})`;
}
}
exports.GitlabPublisher = GitlabPublisher;
//# sourceMappingURL=gitlabPublisher.js.map
File diff suppressed because one or more lines are too long
+11
View File
@@ -0,0 +1,11 @@
import { Arch } from "builder-util";
import { ClientRequest } from "http";
import { PublishContext, UploadTask } from ".";
import { Publisher } from "./publisher";
export declare abstract class HttpPublisher extends Publisher {
protected readonly context: PublishContext;
private readonly useSafeArtifactName;
protected constructor(context: PublishContext, useSafeArtifactName?: boolean);
upload(task: UploadTask): Promise<any>;
protected abstract doUpload(fileName: string, arch: Arch, dataLength: number, requestProcessor: (request: ClientRequest, reject: (error: Error) => void) => void, file: string): Promise<any>;
}
+46
View File
@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpPublisher = void 0;
const builder_util_1 = require("builder-util");
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
const publisher_1 = require("./publisher");
class HttpPublisher extends publisher_1.Publisher {
constructor(context, useSafeArtifactName = false) {
super(context);
this.context = context;
this.useSafeArtifactName = useSafeArtifactName;
}
async upload(task) {
const fileName = (this.useSafeArtifactName ? task.safeArtifactName : null) || (0, path_1.basename)(task.file);
if (task.fileContent != null) {
await this.doUpload(fileName, task.arch || builder_util_1.Arch.x64, task.fileContent.length, (request, reject) => {
if (task.timeout) {
request.setTimeout(task.timeout, () => {
request.destroy();
reject(new Error("Request timed out"));
});
}
return request.end(task.fileContent);
}, task.file);
return;
}
const fileStat = await (0, fs_extra_1.stat)(task.file);
const progressBar = this.createProgressBar(fileName, fileStat.size);
return this.doUpload(fileName, task.arch || builder_util_1.Arch.x64, fileStat.size, (request, reject) => {
if (progressBar != null) {
// reset (because can be called several times (several attempts)
progressBar.update(0);
}
if (task.timeout) {
request.setTimeout(task.timeout, () => {
request.destroy();
reject(new Error("Request timed out"));
});
}
return this.createReadStreamAndProgressBar(task.file, fileStat, progressBar, reject).pipe(request);
}, task.file);
}
}
exports.HttpPublisher = HttpPublisher;
//# sourceMappingURL=httpPublisher.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"httpPublisher.js","sourceRoot":"","sources":["../src/httpPublisher.ts"],"names":[],"mappings":";;;AAAA,+CAAmC;AACnC,uCAA+B;AAE/B,+BAA+B;AAE/B,2CAAuC;AAEvC,MAAsB,aAAc,SAAQ,qBAAS;IACnD,YACqB,OAAuB,EACzB,sBAAsB,KAAK;QAE5C,KAAK,CAAC,OAAO,CAAC,CAAA;QAHK,YAAO,GAAP,OAAO,CAAgB;QACzB,wBAAmB,GAAnB,mBAAmB,CAAQ;IAG9C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAgB;QAC3B,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAA,eAAQ,EAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEjG,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC;YAC7B,MAAM,IAAI,CAAC,QAAQ,CACjB,QAAQ,EACR,IAAI,CAAC,IAAI,IAAI,mBAAI,CAAC,GAAG,EACrB,IAAI,CAAC,WAAW,CAAC,MAAM,EACvB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAClB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBACjB,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;wBACpC,OAAO,CAAC,OAAO,EAAE,CAAA;wBACjB,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAA;oBACxC,CAAC,CAAC,CAAA;gBACJ,CAAC;gBACD,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YACtC,CAAC,EACD,IAAI,CAAC,IAAI,CACV,CAAA;YACD,OAAM;QACR,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAA,eAAI,EAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEtC,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;QACnE,OAAO,IAAI,CAAC,QAAQ,CAClB,QAAQ,EACR,IAAI,CAAC,IAAI,IAAI,mBAAI,CAAC,GAAG,EACrB,QAAQ,CAAC,IAAI,EACb,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAClB,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;gBACxB,gEAAgE;gBAChE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YACvB,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;oBACpC,OAAO,CAAC,OAAO,EAAE,CAAA;oBACjB,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAA;gBACxC,CAAC,CAAC,CAAA;YACJ,CAAC;YACD,OAAO,IAAI,CAAC,8BAA8B,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACpG,CAAC,EACD,IAAI,CAAC,IAAI,CACV,CAAA;IACH,CAAC;CASF;AA7DD,sCA6DC","sourcesContent":["import { Arch } from \"builder-util\"\nimport { stat } from \"fs-extra\"\nimport { ClientRequest } from \"http\"\nimport { basename } from \"path\"\nimport { PublishContext, UploadTask } from \".\"\nimport { Publisher } from \"./publisher\"\n\nexport abstract class HttpPublisher extends Publisher {\n protected constructor(\n protected readonly context: PublishContext,\n private readonly useSafeArtifactName = false\n ) {\n super(context)\n }\n\n async upload(task: UploadTask): Promise<any> {\n const fileName = (this.useSafeArtifactName ? task.safeArtifactName : null) || basename(task.file)\n\n if (task.fileContent != null) {\n await this.doUpload(\n fileName,\n task.arch || Arch.x64,\n task.fileContent.length,\n (request, reject) => {\n if (task.timeout) {\n request.setTimeout(task.timeout, () => {\n request.destroy()\n reject(new Error(\"Request timed out\"))\n })\n }\n return request.end(task.fileContent)\n },\n task.file\n )\n return\n }\n\n const fileStat = await stat(task.file)\n\n const progressBar = this.createProgressBar(fileName, fileStat.size)\n return this.doUpload(\n fileName,\n task.arch || Arch.x64,\n fileStat.size,\n (request, reject) => {\n if (progressBar != null) {\n // reset (because can be called several times (several attempts)\n progressBar.update(0)\n }\n if (task.timeout) {\n request.setTimeout(task.timeout, () => {\n request.destroy()\n reject(new Error(\"Request timed out\"))\n })\n }\n return this.createReadStreamAndProgressBar(task.file, fileStat, progressBar, reject).pipe(request)\n },\n task.file\n )\n }\n\n protected abstract doUpload(\n fileName: string,\n arch: Arch,\n dataLength: number,\n requestProcessor: (request: ClientRequest, reject: (error: Error) => void) => void,\n file: string\n ): Promise<any>\n}\n"]}
+28
View File
@@ -0,0 +1,28 @@
import { Arch } from "builder-util";
import { CancellationToken } from "builder-util-runtime";
import { MultiProgress } from "./multiProgress";
export { BitbucketPublisher } from "./bitbucketPublisher";
export { GitHubPublisher } from "./gitHubPublisher";
export { GitlabPublisher } from "./gitlabPublisher";
export { KeygenPublisher } from "./keygenPublisher";
export { S3Publisher } from "./s3/s3Publisher";
export { SpacesPublisher } from "./s3/spacesPublisher";
export { SnapStorePublisher } from "./snapStorePublisher";
export type PublishPolicy = "onTag" | "onTagOrDraft" | "always" | "never";
export { ProgressCallback } from "./progress";
export interface PublishOptions {
publish?: PublishPolicy | null;
}
export { HttpPublisher } from "./httpPublisher";
export { getCiTag, Publisher } from "./publisher";
export interface PublishContext {
readonly cancellationToken: CancellationToken;
readonly progress: MultiProgress | null;
}
export interface UploadTask {
file: string;
fileContent?: Buffer | null;
arch: Arch | null;
safeArtifactName?: string | null;
timeout?: number | null;
}
+25
View File
@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Publisher = exports.getCiTag = exports.HttpPublisher = exports.ProgressCallback = exports.SnapStorePublisher = exports.SpacesPublisher = exports.S3Publisher = exports.KeygenPublisher = exports.GitlabPublisher = exports.GitHubPublisher = exports.BitbucketPublisher = void 0;
var bitbucketPublisher_1 = require("./bitbucketPublisher");
Object.defineProperty(exports, "BitbucketPublisher", { enumerable: true, get: function () { return bitbucketPublisher_1.BitbucketPublisher; } });
var gitHubPublisher_1 = require("./gitHubPublisher");
Object.defineProperty(exports, "GitHubPublisher", { enumerable: true, get: function () { return gitHubPublisher_1.GitHubPublisher; } });
var gitlabPublisher_1 = require("./gitlabPublisher");
Object.defineProperty(exports, "GitlabPublisher", { enumerable: true, get: function () { return gitlabPublisher_1.GitlabPublisher; } });
var keygenPublisher_1 = require("./keygenPublisher");
Object.defineProperty(exports, "KeygenPublisher", { enumerable: true, get: function () { return keygenPublisher_1.KeygenPublisher; } });
var s3Publisher_1 = require("./s3/s3Publisher");
Object.defineProperty(exports, "S3Publisher", { enumerable: true, get: function () { return s3Publisher_1.S3Publisher; } });
var spacesPublisher_1 = require("./s3/spacesPublisher");
Object.defineProperty(exports, "SpacesPublisher", { enumerable: true, get: function () { return spacesPublisher_1.SpacesPublisher; } });
var snapStorePublisher_1 = require("./snapStorePublisher");
Object.defineProperty(exports, "SnapStorePublisher", { enumerable: true, get: function () { return snapStorePublisher_1.SnapStorePublisher; } });
var progress_1 = require("./progress");
Object.defineProperty(exports, "ProgressCallback", { enumerable: true, get: function () { return progress_1.ProgressCallback; } });
var httpPublisher_1 = require("./httpPublisher");
Object.defineProperty(exports, "HttpPublisher", { enumerable: true, get: function () { return httpPublisher_1.HttpPublisher; } });
var publisher_1 = require("./publisher");
Object.defineProperty(exports, "getCiTag", { enumerable: true, get: function () { return publisher_1.getCiTag; } });
Object.defineProperty(exports, "Publisher", { enumerable: true, get: function () { return publisher_1.Publisher; } });
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAIA,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,qDAAmD;AAA1C,kHAAA,eAAe,OAAA;AACxB,qDAAmD;AAA1C,kHAAA,eAAe,OAAA;AACxB,qDAAmD;AAA1C,kHAAA,eAAe,OAAA;AACxB,gDAA8C;AAArC,0GAAA,WAAW,OAAA;AACpB,wDAAsD;AAA7C,kHAAA,eAAe,OAAA;AACxB,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAI3B,uCAA6C;AAApC,4GAAA,gBAAgB,OAAA;AAMzB,iDAA+C;AAAtC,8GAAA,aAAa,OAAA;AACtB,yCAAiD;AAAxC,qGAAA,QAAQ,OAAA;AAAE,sGAAA,SAAS,OAAA","sourcesContent":["import { Arch } from \"builder-util\"\nimport { CancellationToken } from \"builder-util-runtime\"\nimport { MultiProgress } from \"./multiProgress\"\n\nexport { BitbucketPublisher } from \"./bitbucketPublisher\"\nexport { GitHubPublisher } from \"./gitHubPublisher\"\nexport { GitlabPublisher } from \"./gitlabPublisher\"\nexport { KeygenPublisher } from \"./keygenPublisher\"\nexport { S3Publisher } from \"./s3/s3Publisher\"\nexport { SpacesPublisher } from \"./s3/spacesPublisher\"\nexport { SnapStorePublisher } from \"./snapStorePublisher\"\n\nexport type PublishPolicy = \"onTag\" | \"onTagOrDraft\" | \"always\" | \"never\"\n\nexport { ProgressCallback } from \"./progress\"\n\nexport interface PublishOptions {\n publish?: PublishPolicy | null\n}\n\nexport { HttpPublisher } from \"./httpPublisher\"\nexport { getCiTag, Publisher } from \"./publisher\"\n\nexport interface PublishContext {\n readonly cancellationToken: CancellationToken\n readonly progress: MultiProgress | null\n}\n\nexport interface UploadTask {\n file: string\n fileContent?: Buffer | null\n\n arch: Arch | null\n safeArtifactName?: string | null\n timeout?: number | null\n}\n"]}
+103
View File
@@ -0,0 +1,103 @@
import { Arch } from "builder-util";
import { KeygenOptions } from "builder-util-runtime/out/publishOptions";
import { ClientRequest } from "http";
import { PublishContext } from "./";
import { HttpPublisher } from "./httpPublisher";
export interface KeygenError {
title: string;
detail: string;
code: string;
}
export interface KeygenRelease {
id: string;
type: "releases";
attributes: {
name: string | null;
description: string | null;
channel: "stable" | "rc" | "beta" | "alpha" | "dev";
status: "DRAFT" | "PUBLISHED" | "YANKED";
tag: string;
version: string;
semver: {
major: number;
minor: number;
patch: number;
prerelease: string | null;
build: string | null;
};
metadata: {
[s: string]: any;
};
created: string;
updated: string;
yanked: string | null;
};
relationships: {
account: {
data: {
type: "accounts";
id: string;
};
};
product: {
data: {
type: "products";
id: string;
};
};
};
}
export interface KeygenArtifact {
id: string;
type: "artifacts";
attributes: {
filename: string;
filetype: string | null;
filesize: number | null;
platform: string | null;
arch: string | null;
signature: string | null;
checksum: string | null;
status: "WAITING" | "UPLOADED" | "FAILED" | "YANKED";
metadata: {
[s: string]: any;
};
created: string;
updated: string;
};
relationships: {
account: {
data: {
type: "accounts";
id: string;
};
};
release: {
data: {
type: "releases";
id: string;
};
};
};
links: {
redirect: string;
};
}
export declare class KeygenPublisher extends HttpPublisher {
readonly providerName = "keygen";
readonly defaultHostname = "api.keygen.sh";
private readonly info;
private readonly auth;
private readonly version;
private readonly basePath;
get hostname(): string;
constructor(context: PublishContext, info: KeygenOptions, version: string);
protected doUpload(fileName: string, _arch: Arch, dataLength: number, requestProcessor: (request: ClientRequest, reject: (error: Error) => void) => void, _file: string): Promise<string>;
private uploadArtifact;
private createArtifact;
private getOrCreateRelease;
private getRelease;
private createRelease;
deleteRelease(releaseId: string): Promise<void>;
toString(): string;
}
+167
View File
@@ -0,0 +1,167 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.KeygenPublisher = void 0;
const builder_util_1 = require("builder-util");
const builder_util_runtime_1 = require("builder-util-runtime");
const filename_1 = require("builder-util/out/filename");
const httpPublisher_1 = require("./httpPublisher");
class KeygenPublisher extends httpPublisher_1.HttpPublisher {
get hostname() {
return this.info.host || this.defaultHostname;
}
constructor(context, info, version) {
super(context);
this.providerName = "keygen";
this.defaultHostname = "api.keygen.sh";
const token = process.env.KEYGEN_TOKEN;
if ((0, builder_util_1.isEmptyOrSpaces)(token)) {
throw new builder_util_1.InvalidConfigurationError(`Keygen token is not set using env "KEYGEN_TOKEN" (see https://www.electron.build/publish#KeygenOptions)`);
}
this.info = info;
this.auth = `Bearer ${token.trim()}`;
this.version = version;
this.basePath = `/v1/accounts/${this.info.account}`;
}
doUpload(fileName, _arch, dataLength, requestProcessor, _file) {
return builder_util_runtime_1.HttpExecutor.retryOnServerError(async () => {
const { data, errors } = await this.getOrCreateRelease();
if (errors) {
throw new Error(`Keygen - Creating release returned errors: ${JSON.stringify(errors)}`);
}
await this.uploadArtifact(data.id, fileName, dataLength, requestProcessor);
return data.id;
});
}
async uploadArtifact(releaseId, fileName, dataLength, requestProcessor) {
const { data, errors } = await this.createArtifact(releaseId, fileName, dataLength);
if (errors) {
throw new Error(`Keygen - Creating artifact returned errors: ${JSON.stringify(errors)}`);
}
// Follow the redirect and upload directly to S3-equivalent storage provider
const url = new URL(data.links.redirect);
const upload = {
hostname: url.hostname,
path: url.pathname + url.search,
headers: {
"Content-Length": dataLength,
},
timeout: this.info.timeout || undefined,
};
await builder_util_1.httpExecutor.doApiRequest((0, builder_util_runtime_1.configureRequestOptions)(upload, null, "PUT"), this.context.cancellationToken, requestProcessor);
}
async createArtifact(releaseId, fileName, dataLength) {
const upload = {
hostname: this.hostname,
path: `${this.basePath}/artifacts`,
headers: {
"Content-Type": "application/vnd.api+json",
Accept: "application/vnd.api+json",
"Keygen-Version": "1.1",
Prefer: "no-redirect",
},
timeout: this.info.timeout || undefined,
};
const data = {
type: "artifacts",
attributes: {
filename: fileName,
filetype: (0, filename_1.getCompleteExtname)(fileName),
filesize: dataLength,
platform: this.info.platform,
},
relationships: {
release: {
data: {
type: "releases",
id: releaseId,
},
},
},
};
builder_util_1.log.debug({ data: JSON.stringify(data) }, "Keygen create artifact");
return (0, builder_util_runtime_1.parseJson)(builder_util_1.httpExecutor.request((0, builder_util_runtime_1.configureRequestOptions)(upload, this.auth, "POST"), this.context.cancellationToken, { data }));
}
async getOrCreateRelease() {
try {
// First, we'll attempt to fetch the release.
return await this.getRelease();
}
catch (e) {
if (e.statusCode !== 404) {
throw e;
}
try {
// Next, if the release doesn't exist, we'll attempt to create it.
return await this.createRelease();
}
catch (e) {
if (e.statusCode !== 409 && e.statusCode !== 422) {
throw e;
}
// Lastly, when a conflict occurs (in the case of parallel uploads),
// we'll try to fetch it one last time.
return this.getRelease();
}
}
}
async getRelease() {
const req = {
hostname: this.hostname,
path: `${this.basePath}/releases/${this.version}?product=${this.info.product}`,
headers: {
Accept: "application/vnd.api+json",
"Keygen-Version": "1.1",
},
timeout: this.info.timeout || undefined,
};
return (0, builder_util_runtime_1.parseJson)(builder_util_1.httpExecutor.request((0, builder_util_runtime_1.configureRequestOptions)(req, this.auth, "GET"), this.context.cancellationToken, null));
}
async createRelease() {
const req = {
hostname: this.hostname,
path: `${this.basePath}/releases`,
headers: {
"Content-Type": "application/vnd.api+json",
Accept: "application/vnd.api+json",
"Keygen-Version": "1.1",
},
timeout: this.info.timeout || undefined,
};
const data = {
type: "releases",
attributes: {
version: this.version,
channel: this.info.channel || "stable",
status: "PUBLISHED",
},
relationships: {
product: {
data: {
type: "products",
id: this.info.product,
},
},
},
};
builder_util_1.log.debug({ data: JSON.stringify(data) }, "Keygen create release");
return (0, builder_util_runtime_1.parseJson)(builder_util_1.httpExecutor.request((0, builder_util_runtime_1.configureRequestOptions)(req, this.auth, "POST"), this.context.cancellationToken, { data }));
}
async deleteRelease(releaseId) {
const req = {
hostname: this.hostname,
path: `${this.basePath}/releases/${releaseId}`,
headers: {
Accept: "application/vnd.api+json",
"Keygen-Version": "1.1",
},
timeout: this.info.timeout || undefined,
};
await builder_util_1.httpExecutor.request((0, builder_util_runtime_1.configureRequestOptions)(req, this.auth, "DELETE"), this.context.cancellationToken);
}
toString() {
const { account, product, platform } = this.info;
return `Keygen (account: ${account}, product: ${product}, platform: ${platform}, version: ${this.version})`;
}
}
exports.KeygenPublisher = KeygenPublisher;
//# sourceMappingURL=keygenPublisher.js.map
File diff suppressed because one or more lines are too long
+12
View File
@@ -0,0 +1,12 @@
import { ProgressBar } from "./progress";
export declare class MultiProgress {
private readonly stream;
private cursor;
private totalLines;
private isLogListenerAdded;
private barCount;
createBar(format: string, options: any): ProgressBar;
private allocateLines;
private moveCursor;
terminate(): void;
}
+79
View File
@@ -0,0 +1,79 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiProgress = void 0;
const builder_util_1 = require("builder-util");
const progress_1 = require("./progress");
class MultiProgress {
constructor() {
this.stream = process.stdout;
this.cursor = 0;
this.totalLines = 0;
this.isLogListenerAdded = false;
this.barCount = 0;
}
createBar(format, options) {
options.stream = this.stream;
// eslint-disable-next-line @typescript-eslint/no-this-alias
const manager = this;
class MultiProgressBar extends progress_1.ProgressBar {
constructor(format, options) {
super(format, options);
this.index = -1;
}
render() {
if (this.index === -1) {
this.index = manager.totalLines;
manager.allocateLines(1);
}
else {
manager.moveCursor(this.index);
}
super.render();
if (!manager.isLogListenerAdded) {
manager.isLogListenerAdded = true;
(0, builder_util_1.setPrinter)(message => {
let newLineCount = 0;
let newLineIndex = message.indexOf("\n");
while (newLineIndex > -1) {
newLineCount++;
newLineIndex = message.indexOf("\n", ++newLineIndex);
}
manager.allocateLines(newLineCount + 1);
manager.stream.write(message);
});
}
}
terminate() {
manager.barCount--;
if (manager.barCount === 0 && manager.totalLines > 0) {
manager.allocateLines(1);
manager.totalLines = 0;
manager.cursor = 0;
(0, builder_util_1.setPrinter)(null);
manager.isLogListenerAdded = false;
}
}
}
const bar = new MultiProgressBar(format, options);
this.barCount++;
return bar;
}
allocateLines(count) {
this.stream.moveCursor(0, this.totalLines - 1);
// if cursor pointed to previous line where \n is already printed, another \n is ignored, so, we can simply print it
this.stream.write("\n");
this.totalLines += count;
this.cursor = this.totalLines - 1;
}
moveCursor(index) {
this.stream.moveCursor(0, index - this.cursor);
this.cursor = index;
}
terminate() {
this.moveCursor(this.totalLines);
this.stream.clearLine();
this.stream.cursorTo(0);
}
}
exports.MultiProgress = MultiProgress;
//# sourceMappingURL=multiProgress.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"multiProgress.js","sourceRoot":"","sources":["../src/multiProgress.ts"],"names":[],"mappings":"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","sourcesContent":["import { setPrinter } from \"builder-util\"\nimport { ProgressBar } from \"./progress\"\n\nexport class MultiProgress {\n private readonly stream = process.stdout as any\n private cursor = 0\n\n private totalLines = 0\n\n private isLogListenerAdded = false\n\n private barCount = 0\n\n createBar(format: string, options: any): ProgressBar {\n options.stream = this.stream\n\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const manager = this\n class MultiProgressBar extends ProgressBar {\n private index = -1\n\n constructor(format: string, options: any) {\n super(format, options)\n }\n\n render() {\n if (this.index === -1) {\n this.index = manager.totalLines\n manager.allocateLines(1)\n } else {\n manager.moveCursor(this.index)\n }\n\n super.render()\n\n if (!manager.isLogListenerAdded) {\n manager.isLogListenerAdded = true\n setPrinter(message => {\n let newLineCount = 0\n let newLineIndex = message.indexOf(\"\\n\")\n while (newLineIndex > -1) {\n newLineCount++\n newLineIndex = message.indexOf(\"\\n\", ++newLineIndex)\n }\n\n manager.allocateLines(newLineCount + 1)\n manager.stream.write(message)\n })\n }\n }\n\n terminate() {\n manager.barCount--\n if (manager.barCount === 0 && manager.totalLines > 0) {\n manager.allocateLines(1)\n manager.totalLines = 0\n manager.cursor = 0\n setPrinter(null)\n manager.isLogListenerAdded = false\n }\n }\n }\n\n const bar = new MultiProgressBar(format, options)\n this.barCount++\n return bar\n }\n\n private allocateLines(count: number) {\n this.stream.moveCursor(0, this.totalLines - 1)\n // if cursor pointed to previous line where \\n is already printed, another \\n is ignored, so, we can simply print it\n this.stream.write(\"\\n\")\n this.totalLines += count\n this.cursor = this.totalLines - 1\n }\n\n private moveCursor(index: number) {\n this.stream.moveCursor(0, index - this.cursor)\n this.cursor = index\n }\n\n terminate() {\n this.moveCursor(this.totalLines)\n this.stream.clearLine()\n this.stream.cursorTo(0)\n }\n}\n"]}
+70
View File
@@ -0,0 +1,70 @@
/*!
* node-progress
* Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
export declare abstract class ProgressBar {
private readonly format;
private readonly stream;
private current;
total: number;
private readonly width;
private chars;
private tokens;
private lastDraw;
private start;
private complete;
/**
* Initialize a `ProgressBar` with the given `fmt` string and `options` or`total`.
*
* Options:
* - `curr` current completed index
* - `total` total number of ticks to complete
* - `width` the displayed width of the progress bar defaulting to total
* - `stream` the output stream defaulting to stderr
* - `head` head character defaulting to complete character
* - `complete` completion character defaulting to "="
* - `incomplete` incomplete character defaulting to "-"
* - `renderThrottle` minimum time between updates in milliseconds defaulting to 16
* - `callback` optional function to call when the progress bar completes
* - `clear` will clear the progress bar upon termination
*
* Tokens:
* - `:bar` the progress bar itself
* - `:current` current tick number
* - `:total` total ticks
* - `:elapsed` time elapsed in seconds
* - `:percent` completion percentage
* - `:eta` eta in seconds
* - `:rate` rate of ticks per second
*/
constructor(format: string, options?: any);
/**
* "tick" the progress bar with optional `len` and optional `tokens`.
*/
tick(delta: number): void;
set currentAmount(value: number);
render(): void;
/**
* "update" the progress bar to represent an exact percentage.
* The ratio (between 0 and 1) specified will be multiplied by `total` and
* floored, representing the closest available "tick." For example, if a
* progress bar has a length of 3 and `update(0.5)` is called, the progress
* will be set to 1.
*
* A ratio of 0.5 will attempt to set the progress to halfway.
*/
update(ratio: number): void;
/**
* "interrupt" the progress bar and write a message above it.
*/
interrupt(message: string): void;
abstract terminate(): void;
}
export declare class ProgressCallback {
private readonly progressBar;
private start;
private nextUpdate;
constructor(progressBar: ProgressBar);
update(transferred: number, total: number): void;
}
+162
View File
@@ -0,0 +1,162 @@
"use strict";
/*!
* node-progress
* Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProgressCallback = exports.ProgressBar = void 0;
class ProgressBar {
/**
* Initialize a `ProgressBar` with the given `fmt` string and `options` or`total`.
*
* Options:
* - `curr` current completed index
* - `total` total number of ticks to complete
* - `width` the displayed width of the progress bar defaulting to total
* - `stream` the output stream defaulting to stderr
* - `head` head character defaulting to complete character
* - `complete` completion character defaulting to "="
* - `incomplete` incomplete character defaulting to "-"
* - `renderThrottle` minimum time between updates in milliseconds defaulting to 16
* - `callback` optional function to call when the progress bar completes
* - `clear` will clear the progress bar upon termination
*
* Tokens:
* - `:bar` the progress bar itself
* - `:current` current tick number
* - `:total` total ticks
* - `:elapsed` time elapsed in seconds
* - `:percent` completion percentage
* - `:eta` eta in seconds
* - `:rate` rate of ticks per second
*/
constructor(format, options = {}) {
this.format = format;
this.current = 0;
this.total = 0;
this.tokens = null;
this.lastDraw = "";
this.start = 0;
this.complete = false;
this.stream = options.stream || process.stderr;
this.total = options.total;
this.width = options.width || this.total;
this.chars = {
complete: options.complete || "=",
incomplete: options.incomplete || "-",
head: options.head || options.complete || "=",
};
}
/**
* "tick" the progress bar with optional `len` and optional `tokens`.
*/
tick(delta) {
this.currentAmount = this.current + delta;
}
set currentAmount(value) {
this.current = value;
if (this.complete) {
return;
}
this.render();
if (this.current >= this.total) {
this.complete = true;
this.terminate();
}
}
render() {
// start time for eta
if (this.start === 0) {
this.start = Date.now();
}
const ratio = Math.min(Math.max(this.current / this.total, 0), 1);
const percent = ratio * 100;
const elapsed = Date.now() - this.start;
const eta = percent === 100 ? 0 : elapsed * (this.total / this.current - 1);
const rate = this.current / (elapsed / 1000);
/* populate the bar template with percentages and timestamps */
let str = this.format
.replace(":current", this.current.toString())
.replace(":total", this.total.toString())
.replace(":elapsed", isNaN(elapsed) ? "0.0" : (elapsed / 1000).toFixed(1))
.replace(":eta", isNaN(eta) || !isFinite(eta) ? "0.0" : (eta / 1000).toFixed(1))
.replace(":percent", percent.toFixed(0) + "%")
.replace(":rate", Math.round(rate).toString());
// compute the available space (non-zero) for the bar
let availableSpace = Math.max(0, this.stream.columns - str.replace(":bar", "").length);
if (availableSpace && process.platform === "win32") {
availableSpace -= 1;
}
const width = Math.min(this.width, availableSpace);
const completeLength = Math.round(width * ratio);
let complete = Array(Math.max(0, completeLength + 1)).join(this.chars.complete);
const incomplete = Array(Math.max(0, width - completeLength + 1)).join(this.chars.incomplete);
/* add head to the complete string */
if (completeLength > 0) {
complete = `${complete.slice(0, -1)}${this.chars.head}`;
}
/* fill in the actual progress bar */
str = str.replace(":bar", complete + incomplete);
/* replace the extra tokens */
if (this.tokens != null) {
for (const key of Object.keys(this.tokens)) {
str = str.replace(`:${key}`, this.tokens[key]);
}
}
if (this.lastDraw !== str) {
this.stream.cursorTo(0);
this.stream.write(str);
this.stream.clearLine(1);
this.lastDraw = str;
}
}
/**
* "update" the progress bar to represent an exact percentage.
* The ratio (between 0 and 1) specified will be multiplied by `total` and
* floored, representing the closest available "tick." For example, if a
* progress bar has a length of 3 and `update(0.5)` is called, the progress
* will be set to 1.
*
* A ratio of 0.5 will attempt to set the progress to halfway.
*/
update(ratio) {
const goal = Math.floor(ratio * this.total);
const delta = goal - this.current;
this.tick(delta);
}
/**
* "interrupt" the progress bar and write a message above it.
*/
interrupt(message) {
// clear the current line
const stream = this.stream;
stream.clearLine();
// move the cursor to the start of the line
stream.cursorTo(0);
// write the message text
stream.write(message);
// terminate the line after writing the message
stream.write("\n");
// re-display the progress bar with its lastDraw
stream.write(this.lastDraw);
}
}
exports.ProgressBar = ProgressBar;
class ProgressCallback {
constructor(progressBar) {
this.progressBar = progressBar;
this.start = Date.now();
this.nextUpdate = this.start + 1000;
}
update(transferred, total) {
const now = Date.now();
if (now >= this.nextUpdate || transferred >= total) {
this.nextUpdate = now + 1000;
this.progressBar.total = total;
this.progressBar.currentAmount = transferred;
}
}
}
exports.ProgressCallback = ProgressCallback;
//# sourceMappingURL=progress.js.map
File diff suppressed because one or more lines are too long
+14
View File
@@ -0,0 +1,14 @@
import { PublishProvider } from "builder-util-runtime";
import { Stats } from "fs-extra";
import { PublishContext, UploadTask } from ".";
import { ProgressBar } from "./progress";
export declare abstract class Publisher {
protected readonly context: PublishContext;
protected constructor(context: PublishContext);
abstract get providerName(): PublishProvider;
abstract upload(task: UploadTask): Promise<any>;
protected createProgressBar(fileName: string, size: number): ProgressBar | null;
protected createReadStreamAndProgressBar(file: string, fileStat: Stats, progressBar: ProgressBar | null, reject: (error: Error) => void): NodeJS.ReadableStream;
abstract toString(): string;
}
export declare function getCiTag(): string | null;
+52
View File
@@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Publisher = void 0;
exports.getCiTag = getCiTag;
const builder_util_1 = require("builder-util");
const builder_util_runtime_1 = require("builder-util-runtime");
const chalk = require("chalk");
const fs_extra_1 = require("fs-extra");
const progressBarOptions = {
incomplete: " ",
width: 20,
};
class Publisher {
constructor(context) {
this.context = context;
}
createProgressBar(fileName, size) {
builder_util_1.log.info({ file: fileName, provider: this.providerName }, "uploading");
if (this.context.progress == null || size < 512 * 1024) {
return null;
}
return this.context.progress.createBar(`${" ".repeat(builder_util_1.PADDING + 2)}[:bar] :percent :etas | ${chalk.green(fileName)} to ${this.providerName}`, {
total: size,
...progressBarOptions,
});
}
createReadStreamAndProgressBar(file, fileStat, progressBar, reject) {
const fileInputStream = (0, fs_extra_1.createReadStream)(file);
fileInputStream.on("error", reject);
if (progressBar == null) {
return fileInputStream;
}
else {
const progressStream = new builder_util_runtime_1.ProgressCallbackTransform(fileStat.size, this.context.cancellationToken, it => progressBar.tick(it.delta));
progressStream.on("error", reject);
return fileInputStream.pipe(progressStream);
}
}
}
exports.Publisher = Publisher;
function getCiTag() {
const tag = process.env.TRAVIS_TAG ||
process.env.APPVEYOR_REPO_TAG_NAME ||
process.env.CIRCLE_TAG ||
process.env.BITRISE_GIT_TAG ||
process.env.CI_BUILD_TAG || // deprecated, GitLab uses `CI_COMMIT_TAG` instead
process.env.CI_COMMIT_TAG ||
process.env.BITBUCKET_TAG ||
(process.env.GITHUB_REF_TYPE === "tag" ? process.env.GITHUB_REF_NAME : null);
return tag != null && tag.length > 0 ? tag : null;
}
//# sourceMappingURL=publisher.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"publisher.js","sourceRoot":"","sources":["../src/publisher.ts"],"names":[],"mappings":";;;AA8CA,4BAWC;AAzDD,+CAA2C;AAC3C,+DAAiF;AACjF,+BAA8B;AAC9B,uCAAkD;AAIlD,MAAM,kBAAkB,GAAG;IACzB,UAAU,EAAE,GAAG;IACf,KAAK,EAAE,EAAE;CACV,CAAA;AAED,MAAsB,SAAS;IAC7B,YAAyC,OAAuB;QAAvB,YAAO,GAAP,OAAO,CAAgB;IAAG,CAAC;IAM1D,iBAAiB,CAAC,QAAgB,EAAE,IAAY;QACxD,kBAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,WAAW,CAAC,CAAA;QACtE,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,EAAE,CAAC;YACvD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,sBAAO,GAAG,CAAC,CAAC,2BAA2B,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,YAAY,EAAE,EAAE;YAC3I,KAAK,EAAE,IAAI;YACX,GAAG,kBAAkB;SACtB,CAAC,CAAA;IACJ,CAAC;IAES,8BAA8B,CAAC,IAAY,EAAE,QAAe,EAAE,WAA+B,EAAE,MAA8B;QACrI,MAAM,eAAe,GAAG,IAAA,2BAAgB,EAAC,IAAI,CAAC,CAAA;QAC9C,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAEnC,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YACxB,OAAO,eAAe,CAAA;QACxB,CAAC;aAAM,CAAC;YACN,MAAM,cAAc,GAAG,IAAI,gDAAyB,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;YACrI,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YAClC,OAAO,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;QAC7C,CAAC;IACH,CAAC;CAGF;AAhCD,8BAgCC;AAED,SAAgB,QAAQ;IACtB,MAAM,GAAG,GACP,OAAO,CAAC,GAAG,CAAC,UAAU;QACtB,OAAO,CAAC,GAAG,CAAC,sBAAsB;QAClC,OAAO,CAAC,GAAG,CAAC,UAAU;QACtB,OAAO,CAAC,GAAG,CAAC,eAAe;QAC3B,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,kDAAkD;QAC9E,OAAO,CAAC,GAAG,CAAC,aAAa;QACzB,OAAO,CAAC,GAAG,CAAC,aAAa;QACzB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAC9E,OAAO,GAAG,IAAI,IAAI,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;AACnD,CAAC","sourcesContent":["import { log, PADDING } from \"builder-util\"\nimport { ProgressCallbackTransform, PublishProvider } from \"builder-util-runtime\"\nimport * as chalk from \"chalk\"\nimport { createReadStream, Stats } from \"fs-extra\"\nimport { PublishContext, UploadTask } from \".\"\nimport { ProgressBar } from \"./progress\"\n\nconst progressBarOptions = {\n incomplete: \" \",\n width: 20,\n}\n\nexport abstract class Publisher {\n protected constructor(protected readonly context: PublishContext) {}\n\n abstract get providerName(): PublishProvider\n\n abstract upload(task: UploadTask): Promise<any>\n\n protected createProgressBar(fileName: string, size: number): ProgressBar | null {\n log.info({ file: fileName, provider: this.providerName }, \"uploading\")\n if (this.context.progress == null || size < 512 * 1024) {\n return null\n }\n return this.context.progress.createBar(`${\" \".repeat(PADDING + 2)}[:bar] :percent :etas | ${chalk.green(fileName)} to ${this.providerName}`, {\n total: size,\n ...progressBarOptions,\n })\n }\n\n protected createReadStreamAndProgressBar(file: string, fileStat: Stats, progressBar: ProgressBar | null, reject: (error: Error) => void): NodeJS.ReadableStream {\n const fileInputStream = createReadStream(file)\n fileInputStream.on(\"error\", reject)\n\n if (progressBar == null) {\n return fileInputStream\n } else {\n const progressStream = new ProgressCallbackTransform(fileStat.size, this.context.cancellationToken, it => progressBar.tick(it.delta))\n progressStream.on(\"error\", reject)\n return fileInputStream.pipe(progressStream)\n }\n }\n\n abstract toString(): string\n}\n\nexport function getCiTag() {\n const tag =\n process.env.TRAVIS_TAG ||\n process.env.APPVEYOR_REPO_TAG_NAME ||\n process.env.CIRCLE_TAG ||\n process.env.BITRISE_GIT_TAG ||\n process.env.CI_BUILD_TAG || // deprecated, GitLab uses `CI_COMMIT_TAG` instead\n process.env.CI_COMMIT_TAG ||\n process.env.BITBUCKET_TAG ||\n (process.env.GITHUB_REF_TYPE === \"tag\" ? process.env.GITHUB_REF_NAME : null)\n return tag != null && tag.length > 0 ? tag : null\n}\n"]}
+11
View File
@@ -0,0 +1,11 @@
import { BaseS3Options } from "builder-util-runtime";
import { PublishContext, UploadTask } from "..";
import { Publisher } from "../publisher";
export declare abstract class BaseS3Publisher extends Publisher {
private options;
protected constructor(context: PublishContext, options: BaseS3Options);
protected abstract getBucketName(): string;
protected configureS3Options(args: Array<string>): void;
upload(task: UploadTask): Promise<any>;
toString(): string;
}
+64
View File
@@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseS3Publisher = void 0;
const builder_util_1 = require("builder-util");
const promises_1 = require("fs/promises");
const path = require("path");
const publisher_1 = require("../publisher");
class BaseS3Publisher extends publisher_1.Publisher {
constructor(context, options) {
super(context);
this.options = options;
}
configureS3Options(args) {
// if explicitly set to null, do not add
if (this.options.acl !== null) {
args.push("--acl", this.options.acl || "public-read");
}
}
// http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/s3-example-creating-buckets.html
async upload(task) {
const fileName = path.basename(task.file);
const cancellationToken = this.context.cancellationToken;
const target = (this.options.path == null ? "" : `${this.options.path}/`) + fileName;
const args = ["publish-s3", "--bucket", this.getBucketName(), "--key", target, "--file", task.file];
this.configureS3Options(args);
if (process.env.__TEST_S3_PUBLISHER__ != null) {
const testFile = path.join(process.env.__TEST_S3_PUBLISHER__, target);
await (0, promises_1.mkdir)(path.dirname(testFile), { recursive: true });
await (0, promises_1.symlink)(task.file, testFile);
return;
}
// https://github.com/aws/aws-sdk-go/issues/279
this.createProgressBar(fileName, -1);
// if (progressBar != null) {
// const callback = new ProgressCallback(progressBar)
// uploader.on("progress", () => {
// if (!cancellationToken.cancelled) {
// callback.update(uploader.loaded, uploader.contentLength)
// }
// })
// }
return await cancellationToken.createPromise((resolve, reject, onCancel) => {
(0, builder_util_1.executeAppBuilder)(args, process => {
onCancel(() => {
process.kill("SIGINT");
});
})
.then(() => {
try {
builder_util_1.log.debug({ provider: this.providerName, file: fileName, bucket: this.getBucketName() }, "uploaded");
}
finally {
resolve(undefined);
}
})
.catch(reject);
});
}
toString() {
return `${this.providerName} (bucket: ${this.getBucketName()})`;
}
}
exports.BaseS3Publisher = BaseS3Publisher;
//# sourceMappingURL=baseS3Publisher.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"baseS3Publisher.js","sourceRoot":"","sources":["../../src/s3/baseS3Publisher.ts"],"names":[],"mappings":";;;AAAA,+CAAqD;AAErD,0CAA4C;AAC5C,6BAA4B;AAE5B,4CAAwC;AAExC,MAAsB,eAAgB,SAAQ,qBAAS;IACrD,YACE,OAAuB,EACf,OAAsB;QAE9B,KAAK,CAAC,OAAO,CAAC,CAAA;QAFN,YAAO,GAAP,OAAO,CAAe;IAGhC,CAAC;IAIS,kBAAkB,CAAC,IAAmB;QAC9C,wCAAwC;QACxC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,aAAa,CAAC,CAAA;QACvD,CAAC;IACH,CAAC;IAED,oGAAoG;IACpG,KAAK,CAAC,MAAM,CAAC,IAAgB;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACzC,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAA;QAExD,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,QAAQ,CAAA;QAEpF,MAAM,IAAI,GAAG,CAAC,YAAY,EAAE,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QACnG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAA;QAE7B,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,IAAI,EAAE,CAAC;YAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;YACrE,MAAM,IAAA,gBAAK,EAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YACxD,MAAM,IAAA,kBAAO,EAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;YAClC,OAAM;QACR,CAAC;QAED,+CAA+C;QAC/C,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAA;QACpC,6BAA6B;QAC7B,uDAAuD;QACvD,oCAAoC;QACpC,0CAA0C;QAC1C,iEAAiE;QACjE,QAAQ;QACR,OAAO;QACP,IAAI;QAEJ,OAAO,MAAM,iBAAiB,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;YACzE,IAAA,gCAAiB,EAAC,IAAI,EAAE,OAAO,CAAC,EAAE;gBAChC,QAAQ,CAAC,GAAG,EAAE;oBACZ,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;gBACxB,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC;iBACC,IAAI,CAAC,GAAG,EAAE;gBACT,IAAI,CAAC;oBACH,kBAAG,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,EAAE,UAAU,CAAC,CAAA;gBACtG,CAAC;wBAAS,CAAC;oBACT,OAAO,CAAC,SAAS,CAAC,CAAA;gBACpB,CAAC;YACH,CAAC,CAAC;iBACD,KAAK,CAAC,MAAM,CAAC,CAAA;QAClB,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,QAAQ;QACN,OAAO,GAAG,IAAI,CAAC,YAAY,aAAa,IAAI,CAAC,aAAa,EAAE,GAAG,CAAA;IACjE,CAAC;CACF;AAjED,0CAiEC","sourcesContent":["import { executeAppBuilder, log } from \"builder-util\"\nimport { BaseS3Options } from \"builder-util-runtime\"\nimport { mkdir, symlink } from \"fs/promises\"\nimport * as path from \"path\"\nimport { PublishContext, UploadTask } from \"..\"\nimport { Publisher } from \"../publisher\"\n\nexport abstract class BaseS3Publisher extends Publisher {\n protected constructor(\n context: PublishContext,\n private options: BaseS3Options\n ) {\n super(context)\n }\n\n protected abstract getBucketName(): string\n\n protected configureS3Options(args: Array<string>) {\n // if explicitly set to null, do not add\n if (this.options.acl !== null) {\n args.push(\"--acl\", this.options.acl || \"public-read\")\n }\n }\n\n // http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/s3-example-creating-buckets.html\n async upload(task: UploadTask): Promise<any> {\n const fileName = path.basename(task.file)\n const cancellationToken = this.context.cancellationToken\n\n const target = (this.options.path == null ? \"\" : `${this.options.path}/`) + fileName\n\n const args = [\"publish-s3\", \"--bucket\", this.getBucketName(), \"--key\", target, \"--file\", task.file]\n this.configureS3Options(args)\n\n if (process.env.__TEST_S3_PUBLISHER__ != null) {\n const testFile = path.join(process.env.__TEST_S3_PUBLISHER__, target)\n await mkdir(path.dirname(testFile), { recursive: true })\n await symlink(task.file, testFile)\n return\n }\n\n // https://github.com/aws/aws-sdk-go/issues/279\n this.createProgressBar(fileName, -1)\n // if (progressBar != null) {\n // const callback = new ProgressCallback(progressBar)\n // uploader.on(\"progress\", () => {\n // if (!cancellationToken.cancelled) {\n // callback.update(uploader.loaded, uploader.contentLength)\n // }\n // })\n // }\n\n return await cancellationToken.createPromise((resolve, reject, onCancel) => {\n executeAppBuilder(args, process => {\n onCancel(() => {\n process.kill(\"SIGINT\")\n })\n })\n .then(() => {\n try {\n log.debug({ provider: this.providerName, file: fileName, bucket: this.getBucketName() }, \"uploaded\")\n } finally {\n resolve(undefined)\n }\n })\n .catch(reject)\n })\n }\n\n toString() {\n return `${this.providerName} (bucket: ${this.getBucketName()})`\n }\n}\n"]}
+12
View File
@@ -0,0 +1,12 @@
import { S3Options } from "builder-util-runtime";
import { PublishContext } from "..";
import { BaseS3Publisher } from "./baseS3Publisher";
export declare class S3Publisher extends BaseS3Publisher {
private readonly info;
readonly providerName = "s3";
constructor(context: PublishContext, info: S3Options);
static checkAndResolveOptions(options: S3Options, channelFromAppVersion: string | null, errorIfCannot: boolean): Promise<void>;
protected getBucketName(): string;
protected configureS3Options(args: Array<string>): void;
toString(): string;
}
+70
View File
@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.S3Publisher = void 0;
const builder_util_1 = require("builder-util");
const baseS3Publisher_1 = require("./baseS3Publisher");
class S3Publisher extends baseS3Publisher_1.BaseS3Publisher {
constructor(context, info) {
super(context, info);
this.info = info;
this.providerName = "s3";
}
static async checkAndResolveOptions(options, channelFromAppVersion, errorIfCannot) {
const bucket = options.bucket;
if (bucket == null) {
throw new builder_util_1.InvalidConfigurationError(`Please specify "bucket" for "s3" publish provider`);
}
if (options.endpoint == null && bucket.includes(".") && options.region == null) {
// on dotted bucket names, we need to use a path-based endpoint URL. Path-based endpoint URLs need to include the region.
try {
options.region = await (0, builder_util_1.executeAppBuilder)(["get-bucket-location", "--bucket", bucket]);
}
catch (e) {
if (errorIfCannot) {
throw e;
}
else {
builder_util_1.log.warn(`cannot compute region for bucket (required because on dotted bucket names, we need to use a path-based endpoint URL): ${e}`);
}
}
}
if (options.channel == null && channelFromAppVersion != null) {
options.channel = channelFromAppVersion;
}
if (options.endpoint != null && options.endpoint.endsWith("/")) {
;
options.endpoint = options.endpoint.slice(0, -1);
}
}
getBucketName() {
return this.info.bucket;
}
configureS3Options(args) {
super.configureS3Options(args);
if (this.info.endpoint != null) {
args.push("--endpoint", this.info.endpoint);
}
if (this.info.region != null) {
args.push("--region", this.info.region);
}
if (this.info.storageClass != null) {
args.push("--storageClass", this.info.storageClass);
}
if (this.info.encryption != null) {
args.push("--encryption", this.info.encryption);
}
if (this.info.forcePathStyle != null) {
args.push("--forcePathStyle", this.info.forcePathStyle ? "true" : "false");
}
}
toString() {
const result = super.toString();
const endpoint = this.info.endpoint;
if (endpoint != null) {
return result.substring(0, result.length - 1) + `, endpoint: ${endpoint})`;
}
return result;
}
}
exports.S3Publisher = S3Publisher;
//# sourceMappingURL=s3Publisher.js.map
File diff suppressed because one or more lines are too long
+11
View File
@@ -0,0 +1,11 @@
import { SpacesOptions } from "builder-util-runtime";
import { PublishContext } from "../";
import { BaseS3Publisher } from "./baseS3Publisher";
export declare class SpacesPublisher extends BaseS3Publisher {
private readonly info;
readonly providerName = "spaces";
constructor(context: PublishContext, info: SpacesOptions);
static checkAndResolveOptions(options: SpacesOptions, channelFromAppVersion: string | null, errorIfCannot: boolean): Promise<void>;
protected getBucketName(): string;
protected configureS3Options(args: Array<string>): void;
}
+45
View File
@@ -0,0 +1,45 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpacesPublisher = void 0;
const builder_util_1 = require("builder-util");
const baseS3Publisher_1 = require("./baseS3Publisher");
class SpacesPublisher extends baseS3Publisher_1.BaseS3Publisher {
constructor(context, info) {
super(context, info);
this.info = info;
this.providerName = "spaces";
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
static checkAndResolveOptions(options, channelFromAppVersion, errorIfCannot) {
if (options.name == null) {
throw new builder_util_1.InvalidConfigurationError(`Please specify "name" for "spaces" publish provider (see https://www.electron.build/publish#spacesoptions)`);
}
if (options.region == null) {
throw new builder_util_1.InvalidConfigurationError(`Please specify "region" for "spaces" publish provider (see https://www.electron.build/publish#spacesoptions)`);
}
if (options.channel == null && channelFromAppVersion != null) {
options.channel = channelFromAppVersion;
}
return Promise.resolve();
}
getBucketName() {
return this.info.name;
}
configureS3Options(args) {
super.configureS3Options(args);
args.push("--endpoint", `${this.info.region}.digitaloceanspaces.com`);
args.push("--region", this.info.region);
const accessKey = process.env.DO_KEY_ID;
const secretKey = process.env.DO_SECRET_KEY;
if ((0, builder_util_1.isEmptyOrSpaces)(accessKey)) {
throw new builder_util_1.InvalidConfigurationError("Please set env DO_KEY_ID (see https://www.electron.build/publish#spacesoptions)");
}
if ((0, builder_util_1.isEmptyOrSpaces)(secretKey)) {
throw new builder_util_1.InvalidConfigurationError("Please set env DO_SECRET_KEY (see https://www.electron.build/publish#spacesoptions)");
}
args.push("--accessKey", accessKey);
args.push("--secretKey", secretKey);
}
}
exports.SpacesPublisher = SpacesPublisher;
//# sourceMappingURL=spacesPublisher.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"spacesPublisher.js","sourceRoot":"","sources":["../../src/s3/spacesPublisher.ts"],"names":[],"mappings":";;;AAAA,+CAAyE;AAGzE,uDAAmD;AAEnD,MAAa,eAAgB,SAAQ,iCAAe;IAGlD,YACE,OAAuB,EACN,IAAmB;QAEpC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;QAFH,SAAI,GAAJ,IAAI,CAAe;QAJ7B,iBAAY,GAAG,QAAQ,CAAA;IAOhC,CAAC;IAED,6DAA6D;IAC7D,MAAM,CAAC,sBAAsB,CAAC,OAAsB,EAAE,qBAAoC,EAAE,aAAsB;QAChH,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;YACzB,MAAM,IAAI,wCAAyB,CAAC,4GAA4G,CAAC,CAAA;QACnJ,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;YAC3B,MAAM,IAAI,wCAAyB,CAAC,8GAA8G,CAAC,CAAA;QACrJ,CAAC;QAED,IAAI,OAAO,CAAC,OAAO,IAAI,IAAI,IAAI,qBAAqB,IAAI,IAAI,EAAE,CAAC;YAC7D,OAAO,CAAC,OAAO,GAAG,qBAAqB,CAAA;QACzC,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAES,aAAa;QACrB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;IACvB,CAAC;IAES,kBAAkB,CAAC,IAAmB;QAC9C,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAA;QAE9B,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,yBAAyB,CAAC,CAAA;QACrE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAEvC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAA;QACvC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAA;QAC3C,IAAI,IAAA,8BAAe,EAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,wCAAyB,CAAC,iFAAiF,CAAC,CAAA;QACxH,CAAC;QACD,IAAI,IAAA,8BAAe,EAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,wCAAyB,CAAC,qFAAqF,CAAC,CAAA;QAC5H,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAA;IACrC,CAAC;CACF;AA9CD,0CA8CC","sourcesContent":["import { InvalidConfigurationError, isEmptyOrSpaces } from \"builder-util\"\nimport { SpacesOptions } from \"builder-util-runtime\"\nimport { PublishContext } from \"../\"\nimport { BaseS3Publisher } from \"./baseS3Publisher\"\n\nexport class SpacesPublisher extends BaseS3Publisher {\n readonly providerName = \"spaces\"\n\n constructor(\n context: PublishContext,\n private readonly info: SpacesOptions\n ) {\n super(context, info)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n static checkAndResolveOptions(options: SpacesOptions, channelFromAppVersion: string | null, errorIfCannot: boolean) {\n if (options.name == null) {\n throw new InvalidConfigurationError(`Please specify \"name\" for \"spaces\" publish provider (see https://www.electron.build/publish#spacesoptions)`)\n }\n if (options.region == null) {\n throw new InvalidConfigurationError(`Please specify \"region\" for \"spaces\" publish provider (see https://www.electron.build/publish#spacesoptions)`)\n }\n\n if (options.channel == null && channelFromAppVersion != null) {\n options.channel = channelFromAppVersion\n }\n return Promise.resolve()\n }\n\n protected getBucketName(): string {\n return this.info.name\n }\n\n protected configureS3Options(args: Array<string>): void {\n super.configureS3Options(args)\n\n args.push(\"--endpoint\", `${this.info.region}.digitaloceanspaces.com`)\n args.push(\"--region\", this.info.region)\n\n const accessKey = process.env.DO_KEY_ID\n const secretKey = process.env.DO_SECRET_KEY\n if (isEmptyOrSpaces(accessKey)) {\n throw new InvalidConfigurationError(\"Please set env DO_KEY_ID (see https://www.electron.build/publish#spacesoptions)\")\n }\n if (isEmptyOrSpaces(secretKey)) {\n throw new InvalidConfigurationError(\"Please set env DO_SECRET_KEY (see https://www.electron.build/publish#spacesoptions)\")\n }\n args.push(\"--accessKey\", accessKey)\n args.push(\"--secretKey\", secretKey)\n }\n}\n"]}
+10
View File
@@ -0,0 +1,10 @@
import { SnapStoreOptions } from "builder-util-runtime/out/publishOptions";
import { PublishContext, UploadTask } from ".";
import { Publisher } from "./publisher";
export declare class SnapStorePublisher extends Publisher {
private options;
readonly providerName = "snapStore";
constructor(context: PublishContext, options: SnapStoreOptions);
upload(task: UploadTask): Promise<any>;
toString(): string;
}
+35
View File
@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SnapStorePublisher = void 0;
const builder_util_1 = require("builder-util");
const path = require("path");
const publisher_1 = require("./publisher");
class SnapStorePublisher extends publisher_1.Publisher {
constructor(context, options) {
super(context);
this.options = options;
this.providerName = "snapStore";
}
upload(task) {
this.createProgressBar(path.basename(task.file), -1);
const args = ["publish-snap", "-f", task.file];
let channels = this.options.channels;
if (channels == null) {
channels = ["edge"];
}
else {
if (typeof channels === "string") {
channels = channels.split(",");
}
}
for (const channel of channels) {
args.push("-c", channel);
}
return (0, builder_util_1.executeAppBuilder)(args);
}
toString() {
return "Snap Store";
}
}
exports.SnapStorePublisher = SnapStorePublisher;
//# sourceMappingURL=snapStorePublisher.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"snapStorePublisher.js","sourceRoot":"","sources":["../src/snapStorePublisher.ts"],"names":[],"mappings":";;;AAAA,+CAAgD;AAEhD,6BAA4B;AAE5B,2CAAuC;AAEvC,MAAa,kBAAmB,SAAQ,qBAAS;IAG/C,YACE,OAAuB,EACf,OAAyB;QAEjC,KAAK,CAAC,OAAO,CAAC,CAAA;QAFN,YAAO,GAAP,OAAO,CAAkB;QAJ1B,iBAAY,GAAG,WAAW,CAAA;IAOnC,CAAC;IAED,MAAM,CAAC,IAAgB;QACrB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAEpD,MAAM,IAAI,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAE9C,IAAI,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAA;QACpC,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,QAAQ,GAAG,CAAC,MAAM,CAAC,CAAA;QACrB,CAAC;aAAM,CAAC;YACN,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACjC,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YAChC,CAAC;QACH,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAC1B,CAAC;QAED,OAAO,IAAA,gCAAiB,EAAC,IAAI,CAAC,CAAA;IAChC,CAAC;IAED,QAAQ;QACN,OAAO,YAAY,CAAA;IACrB,CAAC;CACF;AAlCD,gDAkCC","sourcesContent":["import { executeAppBuilder } from \"builder-util\"\nimport { SnapStoreOptions } from \"builder-util-runtime/out/publishOptions\"\nimport * as path from \"path\"\nimport { PublishContext, UploadTask } from \".\"\nimport { Publisher } from \"./publisher\"\n\nexport class SnapStorePublisher extends Publisher {\n readonly providerName = \"snapStore\"\n\n constructor(\n context: PublishContext,\n private options: SnapStoreOptions\n ) {\n super(context)\n }\n\n upload(task: UploadTask): Promise<any> {\n this.createProgressBar(path.basename(task.file), -1)\n\n const args = [\"publish-snap\", \"-f\", task.file]\n\n let channels = this.options.channels\n if (channels == null) {\n channels = [\"edge\"]\n } else {\n if (typeof channels === \"string\") {\n channels = channels.split(\",\")\n }\n }\n\n for (const channel of channels) {\n args.push(\"-c\", channel)\n }\n\n return executeAppBuilder(args)\n }\n\n toString(): string {\n return \"Snap Store\"\n }\n}\n"]}