update electron to v43
All checks were successful
Android Build / publish (push) Successful in 55s
Linux Build / publish (push) Successful in 1m6s

This commit is contained in:
olcxja 2026-07-09 22:38:33 +02:00
commit fb6c8b6ee9
5385 changed files with 513060 additions and 123058 deletions

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;
}

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

View file

@ -1,9 +1,9 @@
/// <reference types="node" />
import { Arch } from "builder-util";
import { GithubOptions } from "builder-util-runtime";
import { ClientRequest } from "http";
import { Lazy } from "lazy-val";
import { HttpPublisher, PublishContext, PublishOptions } from "./publisher";
import { HttpPublisher } from "./httpPublisher";
import { PublishContext, PublishOptions } from "./index";
export interface Release {
id: number;
tag_name: string;
@ -16,13 +16,15 @@ export declare class GitHubPublisher extends HttpPublisher {
private readonly info;
private readonly version;
private readonly options;
private readonly releaseBody?;
private readonly releaseName?;
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);
constructor(context: PublishContext, info: GithubOptions, version: string, options?: PublishOptions, releaseBody?: string | null | undefined, releaseName?: string | null | undefined);
private getOrCreateRelease;
private overwriteArtifact;
protected doUpload(fileName: string, arch: Arch, dataLength: number, requestProcessor: (request: ClientRequest, reject: (error: Error) => void) => void): Promise<any>;

View file

@ -3,41 +3,44 @@ 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 nodeHttpExecutor_1 = require("builder-util/out/nodeHttpExecutor");
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 publisher_1.HttpPublisher {
constructor(context, info, version, options = {}) {
const util_1 = require("./util");
class GitHubPublisher extends httpPublisher_1.HttpPublisher {
constructor(context, info, version, options = {}, releaseBody, releaseName) {
super(context, true);
this.info = info;
this.version = version;
this.options = options;
this.releaseBody = releaseBody;
this.releaseName = releaseName;
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 (builder_util_1.isEmptyOrSpaces(token)) {
token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
if (builder_util_1.isEmptyOrSpaces(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 (!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"`);
if (!(0, builder_util_1.isTokenCharValid)(token)) {
throw new builder_util_1.InvalidConfigurationError(`GitHub Personal Access Token ${(0, builder_util_runtime_1.hashSensitiveValue)(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 = info.vPrefixedTagName === false ? version : `v${version}`;
if (builder_util_1.isEnvTrue(process.env.EP_DRAFT)) {
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 (builder_util_1.isEnvTrue(process.env.EP_PRE_RELEASE) || builder_util_1.isEnvTrue(process.env.EP_PRELEASE) /* https://github.com/electron-userland/electron-builder/issues/2878 */) {
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");
}
@ -82,7 +85,7 @@ class GitHubPublisher extends publisher_1.HttpPublisher {
// 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 (!builder_util_1.isEnvTrue(process.env.EP_GH_IGNORE_TIME) && publishedAt != null && Date.now() - publishedAt > 2 * 3600 * 1000) {
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",
@ -95,7 +98,7 @@ class GitHubPublisher extends publisher_1.HttpPublisher {
return release;
}
// https://github.com/electron-userland/electron-builder/issues/1835
if (this.options.publish === "always" || publisher_1.getCiTag() != null) {
if (this.options.publish === "always" || (0, publisher_1.getCiTag)() != null) {
builder_util_1.log.info({
reason: "release doesn't exist",
...logFields,
@ -126,12 +129,12 @@ class GitHubPublisher extends publisher_1.HttpPublisher {
builder_util_1.log.warn({ file: fileName, ...this.releaseLogFields }, "skipped publishing");
return;
}
const parsedUrl = url_1.parse(`${release.upload_url.substring(0, release.upload_url.indexOf("{"))}?name=${fileName}`);
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 nodeHttpExecutor_1.httpExecutor
.doApiRequest(builder_util_runtime_1.configureRequestOptions({
return builder_util_1.httpExecutor
.doApiRequest((0, builder_util_runtime_1.configureRequestOptions)({
protocol: parsedUrl.protocol,
hostname: parsedUrl.hostname,
path: parsedUrl.path,
@ -143,7 +146,7 @@ class GitHubPublisher extends publisher_1.HttpPublisher {
},
timeout: this.info.timeout || undefined,
}, this.token), this.context.cancellationToken, requestProcessor)
.catch(e => {
.catch((e) => {
if (attemptNumber > 3) {
return Promise.reject(e);
}
@ -169,12 +172,16 @@ class GitHubPublisher extends publisher_1.HttpPublisher {
return e.statusCode === 422 && descIncludesAlreadyExists;
}
createRelease() {
return this.githubRequest(`/repos/${this.info.owner}/${this.info.repo}/releases`, this.token, {
const data = {
tag_name: this.tag,
name: this.version,
name: this.releaseName || this.version,
draft: this.releaseType === "draft",
prerelease: this.releaseType === "prerelease",
});
};
if (this.releaseBody) {
data.body = (0, util_1.trimStringWithWarn)(this.releaseBody, 100000, "release body exceeds GitHub API limit, truncating");
}
return this.githubRequest(`/repos/${this.info.owner}/${this.info.repo}/releases`, this.token, data);
}
// test only
//noinspection JSUnusedGlobalSymbols
@ -208,8 +215,8 @@ class GitHubPublisher extends publisher_1.HttpPublisher {
}
githubRequest(path, token, data = null, method) {
// host can contains port, but node http doesn't support host as url does
const baseUrl = url_1.parse(`https://${this.info.host || "api.github.com"}`);
return builder_util_runtime_1.parseJson(nodeHttpExecutor_1.httpExecutor.request(builder_util_runtime_1.configureRequestOptions({
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,

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,38 @@
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 releaseBody?;
private readonly releaseName?;
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, releaseBody?: string | null | undefined, releaseName?: string | null | undefined);
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 {};

View file

@ -0,0 +1,295 @@
"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");
const util_1 = require("./util");
class GitlabPublisher extends httpPublisher_1.HttpPublisher {
constructor(context, info, version, releaseBody, releaseName) {
super(context, true);
this.info = info;
this.version = version;
this.releaseBody = releaseBody;
this.releaseName = releaseName;
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 ${(0, builder_util_runtime_1.hashSensitiveValue)(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 defaultName = this.info.vPrefixedTagName === false ? this.version : `v${this.version}`;
const releaseName = this.releaseName || defaultName;
const branchName = await this.getDefaultBranch();
const description = this.releaseBody ? (0, util_1.trimStringWithWarn)(this.releaseBody, 100000, "release body exceeds GitLab limit, truncating") : `Release ${releaseName}`;
const releaseData = {
tag_name: this.tag,
name: releaseName,
description,
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

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>;
}

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

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"]}

30
electron/node_modules/electron-publish/out/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,30 @@
import { Arch } from "builder-util";
import { CancellationToken } from "builder-util-runtime";
import { MultiProgress } from "./multiProgress";
export { MultiProgress };
export { ProgressBar } from "./progress";
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, resolveSnapCredentials } 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;
}

30
electron/node_modules/electron-publish/out/index.js generated vendored Normal file
View file

@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Publisher = exports.getCiTag = exports.HttpPublisher = exports.ProgressCallback = exports.resolveSnapCredentials = exports.SnapStorePublisher = exports.SpacesPublisher = exports.S3Publisher = exports.KeygenPublisher = exports.GitlabPublisher = exports.GitHubPublisher = exports.BitbucketPublisher = exports.ProgressBar = exports.MultiProgress = void 0;
const multiProgress_1 = require("./multiProgress");
Object.defineProperty(exports, "MultiProgress", { enumerable: true, get: function () { return multiProgress_1.MultiProgress; } });
var progress_1 = require("./progress");
Object.defineProperty(exports, "ProgressBar", { enumerable: true, get: function () { return progress_1.ProgressBar; } });
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; } });
Object.defineProperty(exports, "resolveSnapCredentials", { enumerable: true, get: function () { return snapStorePublisher_1.resolveSnapCredentials; } });
var progress_2 = require("./progress");
Object.defineProperty(exports, "ProgressCallback", { enumerable: true, get: function () { return progress_2.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

View file

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,mDAA+C;AAEtC,8FAFA,6BAAa,OAEA;AACtB,uCAAwC;AAA/B,uGAAA,WAAW,OAAA;AAEpB,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,2DAAiF;AAAxE,wHAAA,kBAAkB,OAAA;AAAE,4HAAA,sBAAsB,OAAA;AAInD,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 { MultiProgress }\nexport { ProgressBar } from \"./progress\"\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, resolveSnapCredentials } 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"]}

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;
}

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: (0, builder_util_runtime_1.safeStringifyJson)(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: (0, builder_util_runtime_1.safeStringifyJson)(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

View file

@ -1,7 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiProgress = void 0;
const log_1 = require("builder-util/out/log");
const builder_util_1 = require("builder-util");
const progress_1 = require("./progress");
class MultiProgress {
constructor() {
@ -31,7 +31,7 @@ class MultiProgress {
super.render();
if (!manager.isLogListenerAdded) {
manager.isLogListenerAdded = true;
log_1.setPrinter(message => {
(0, builder_util_1.setPrinter)(message => {
let newLineCount = 0;
let newLineIndex = message.indexOf("\n");
while (newLineIndex > -1) {
@ -49,7 +49,7 @@ class MultiProgress {
manager.allocateLines(1);
manager.totalLines = 0;
manager.cursor = 0;
log_1.setPrinter(null);
(0, builder_util_1.setPrinter)(null);
manager.isLogListenerAdded = false;
}
}

View file

@ -1 +1 @@
{"version":3,"file":"multiProgress.js","sourceRoot":"","sources":["../src/multiProgress.ts"],"names":[],"mappings":"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","sourcesContent":["import { setPrinter } from \"builder-util/out/log\"\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"]}
{"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"]}

File diff suppressed because one or more lines are too long

View file

@ -1,26 +1,7 @@
/// <reference types="node" />
import { Arch } from "builder-util";
import { CancellationToken, PublishProvider } from "builder-util-runtime";
import { PublishProvider } from "builder-util-runtime";
import { Stats } from "fs-extra";
import { ClientRequest } from "http";
import { MultiProgress } from "./multiProgress";
import { PublishContext, UploadTask } from ".";
import { ProgressBar } from "./progress";
export declare type PublishPolicy = "onTag" | "onTagOrDraft" | "always" | "never";
export { ProgressCallback } from "./progress";
export interface PublishOptions {
publish?: PublishPolicy | null;
}
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;
}
export declare abstract class Publisher {
protected readonly context: PublishContext;
protected constructor(context: PublishContext);
@ -30,11 +11,4 @@ export declare abstract class Publisher {
protected createReadStreamAndProgressBar(file: string, fileStat: Stats, progressBar: ProgressBar | null, reject: (error: Error) => void): NodeJS.ReadableStream;
abstract toString(): string;
}
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>;
}
export declare function getCiTag(): string | null;

View file

@ -1,14 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCiTag = exports.HttpPublisher = exports.Publisher = exports.ProgressCallback = void 0;
exports.Publisher = void 0;
exports.getCiTag = getCiTag;
const builder_util_1 = require("builder-util");
const builder_util_runtime_1 = require("builder-util-runtime");
const log_1 = require("builder-util/out/log");
const chalk = require("chalk");
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
var progress_1 = require("./progress");
Object.defineProperty(exports, "ProgressCallback", { enumerable: true, get: function () { return progress_1.ProgressCallback; } });
const progressBarOptions = {
incomplete: " ",
width: 20,
@ -22,13 +19,13 @@ class Publisher {
if (this.context.progress == null || size < 512 * 1024) {
return null;
}
return this.context.progress.createBar(`${" ".repeat(log_1.PADDING + 2)}[:bar] :percent :etas | ${chalk.green(fileName)} to ${this.providerName}`, {
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 = fs_extra_1.createReadStream(file);
const fileInputStream = (0, fs_extra_1.createReadStream)(file);
fileInputStream.on("error", reject);
if (progressBar == null) {
return fileInputStream;
@ -41,47 +38,15 @@ class Publisher {
}
}
exports.Publisher = Publisher;
class HttpPublisher extends Publisher {
constructor(context, useSafeArtifactName = false) {
super(context);
this.context = context;
this.useSafeArtifactName = useSafeArtifactName;
}
async upload(task) {
const fileName = (this.useSafeArtifactName ? task.safeArtifactName : null) || 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 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;
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 || process.env.BITBUCKET_TAG;
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;
}
exports.getCiTag = getCiTag;
//# sourceMappingURL=publisher.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,14 @@
export interface AwsCredentials {
accessKeyId: string;
secretAccessKey: string;
sessionToken?: string;
}
/**
* Resolves AWS credentials using the standard provider chain:
* 1. Environment variables (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)
* 2. Shared credentials file (~/.aws/credentials, respects AWS_PROFILE)
*
* Returns undefined when no credentials are found callers should let aws4 sign
* with anonymous credentials or throw a descriptive error as appropriate.
*/
export declare function resolveAwsCredentials(): AwsCredentials | undefined;

View file

@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveAwsCredentials = resolveAwsCredentials;
const fs = require("fs");
const os = require("os");
const path = require("path");
function parseIniSection(content, sectionName) {
let inSection = false;
let found = false;
const result = {};
for (const rawLine of content.split(/\r?\n/)) {
const line = rawLine.trim();
if (line.startsWith("[")) {
const name = line.slice(1, line.indexOf("]")).trim();
inSection = name === sectionName;
if (inSection) {
found = true;
}
}
else if (inSection && line && !line.startsWith("#") && !line.startsWith(";")) {
const eq = line.indexOf("=");
if (eq > 0) {
result[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
}
}
}
return found ? result : null;
}
/**
* Resolves AWS credentials using the standard provider chain:
* 1. Environment variables (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)
* 2. Shared credentials file (~/.aws/credentials, respects AWS_PROFILE)
*
* Returns undefined when no credentials are found callers should let aws4 sign
* with anonymous credentials or throw a descriptive error as appropriate.
*/
function resolveAwsCredentials() {
var _a;
if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {
return {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
sessionToken: process.env.AWS_SESSION_TOKEN || undefined,
};
}
try {
const profile = (_a = process.env.AWS_PROFILE) !== null && _a !== void 0 ? _a : "default";
const credsPath = path.join(os.homedir(), ".aws", "credentials");
const raw = fs.readFileSync(credsPath, "utf8");
const section = parseIniSection(raw, profile);
if ((section === null || section === void 0 ? void 0 : section.aws_access_key_id) && (section === null || section === void 0 ? void 0 : section.aws_secret_access_key)) {
return {
accessKeyId: section.aws_access_key_id,
secretAccessKey: section.aws_secret_access_key,
sessionToken: section.aws_session_token || undefined,
};
}
}
catch {
// file absent or unreadable — fall through
}
return undefined;
}
//# sourceMappingURL=awsCredentials.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"awsCredentials.js","sourceRoot":"","sources":["../../src/s3/awsCredentials.ts"],"names":[],"mappings":";;AA0CA,sDA0BC;AApED,yBAAwB;AACxB,yBAAwB;AACxB,6BAA4B;AAQ5B,SAAS,eAAe,CAAC,OAAe,EAAE,WAAmB;IAC3D,IAAI,SAAS,GAAG,KAAK,CAAA;IACrB,IAAI,KAAK,GAAG,KAAK,CAAA;IACjB,MAAM,MAAM,GAA2B,EAAE,CAAA;IAEzC,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;YACpD,SAAS,GAAG,IAAI,KAAK,WAAW,CAAA;YAChC,IAAI,SAAS,EAAE,CAAC;gBACd,KAAK,GAAG,IAAI,CAAA;YACd,CAAC;QACH,CAAC;aAAM,IAAI,SAAS,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/E,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC5B,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBACX,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;YAC9D,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;AAC9B,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,qBAAqB;;IACnC,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC;QACvE,OAAO;YACL,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;YAC1C,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB;YAClD,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,SAAS;SACzD,CAAA;IACH,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAA,OAAO,CAAC,GAAG,CAAC,WAAW,mCAAI,SAAS,CAAA;QACpD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,CAAA;QAChE,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QAC9C,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAC7C,IAAI,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,iBAAiB,MAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,qBAAqB,CAAA,EAAE,CAAC;YACjE,OAAO;gBACL,WAAW,EAAE,OAAO,CAAC,iBAAiB;gBACtC,eAAe,EAAE,OAAO,CAAC,qBAAqB;gBAC9C,YAAY,EAAE,OAAO,CAAC,iBAAiB,IAAI,SAAS;aACrD,CAAA;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2CAA2C;IAC7C,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC","sourcesContent":["import * as fs from \"fs\"\nimport * as os from \"os\"\nimport * as path from \"path\"\n\nexport interface AwsCredentials {\n accessKeyId: string\n secretAccessKey: string\n sessionToken?: string\n}\n\nfunction parseIniSection(content: string, sectionName: string): Record<string, string> | null {\n let inSection = false\n let found = false\n const result: Record<string, string> = {}\n\n for (const rawLine of content.split(/\\r?\\n/)) {\n const line = rawLine.trim()\n if (line.startsWith(\"[\")) {\n const name = line.slice(1, line.indexOf(\"]\")).trim()\n inSection = name === sectionName\n if (inSection) {\n found = true\n }\n } else if (inSection && line && !line.startsWith(\"#\") && !line.startsWith(\";\")) {\n const eq = line.indexOf(\"=\")\n if (eq > 0) {\n result[line.slice(0, eq).trim()] = line.slice(eq + 1).trim()\n }\n }\n }\n\n return found ? result : null\n}\n\n/**\n * Resolves AWS credentials using the standard provider chain:\n * 1. Environment variables (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)\n * 2. Shared credentials file (~/.aws/credentials, respects AWS_PROFILE)\n *\n * Returns undefined when no credentials are found — callers should let aws4 sign\n * with anonymous credentials or throw a descriptive error as appropriate.\n */\nexport function resolveAwsCredentials(): AwsCredentials | undefined {\n if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {\n return {\n accessKeyId: process.env.AWS_ACCESS_KEY_ID,\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n sessionToken: process.env.AWS_SESSION_TOKEN || undefined,\n }\n }\n\n try {\n const profile = process.env.AWS_PROFILE ?? \"default\"\n const credsPath = path.join(os.homedir(), \".aws\", \"credentials\")\n const raw = fs.readFileSync(credsPath, \"utf8\")\n const section = parseIniSection(raw, profile)\n if (section?.aws_access_key_id && section?.aws_secret_access_key) {\n return {\n accessKeyId: section.aws_access_key_id,\n secretAccessKey: section.aws_secret_access_key,\n sessionToken: section.aws_session_token || undefined,\n }\n }\n } catch {\n // file absent or unreadable — fall through\n }\n\n return undefined\n}\n"]}

View file

@ -0,0 +1,24 @@
import { BaseS3Options } from "builder-util-runtime";
import { PublishContext, UploadTask } from "..";
import { Publisher } from "../publisher";
import type { AwsCredentials } from "./awsCredentials";
export interface S3UploadConfig {
region: string;
endpoint?: string;
forcePathStyle?: boolean;
credentials?: AwsCredentials;
}
export interface S3UploadExtraParams {
acl?: string;
storageClass?: string;
serverSideEncryption?: string;
}
export declare abstract class BaseS3Publisher extends Publisher {
private readonly options;
protected constructor(context: PublishContext, options: BaseS3Options);
protected abstract getBucketName(): string;
abstract getS3UploadConfig(): S3UploadConfig;
getUploadExtraParams(): S3UploadExtraParams;
upload(task: UploadTask): Promise<any>;
toString(): string;
}

View file

@ -0,0 +1,61 @@
"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");
const s3UploadHelper_1 = require("./s3UploadHelper");
class BaseS3Publisher extends publisher_1.Publisher {
constructor(context, options) {
super(context);
this.options = options;
}
getUploadExtraParams() {
var _a;
return {
acl: this.options.acl !== null ? ((_a = this.options.acl) !== null && _a !== void 0 ? _a : "public-read") : undefined,
};
}
// 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 key = (this.options.path == null ? "" : `${this.options.path}/`) + fileName;
if (process.env.__TEST_S3_PUBLISHER__ != null) {
const testFile = path.join(process.env.__TEST_S3_PUBLISHER__, key);
await (0, promises_1.mkdir)(path.dirname(testFile), { recursive: true });
await (0, promises_1.symlink)(task.file, testFile);
return;
}
this.createProgressBar(fileName, -1);
const config = this.getS3UploadConfig();
const extraParams = this.getUploadExtraParams();
return await cancellationToken.createPromise((resolve, reject, onCancel) => {
const { req, done } = (0, s3UploadHelper_1.startS3PutObject)({
bucket: this.getBucketName(),
key,
file: task.file,
contentType: (0, s3UploadHelper_1.getS3ContentType)(task.file),
...config,
...extraParams,
});
onCancel(() => req.destroy());
done
.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

View file

@ -0,0 +1 @@
{"version":3,"file":"baseS3Publisher.js","sourceRoot":"","sources":["../../src/s3/baseS3Publisher.ts"],"names":[],"mappings":";;;AAAA,+CAAkC;AAElC,0CAA4C;AAC5C,6BAA4B;AAE5B,4CAAwC;AAExC,qDAAqE;AAerE,MAAsB,eAAgB,SAAQ,qBAAS;IACrD,YACE,OAAuB,EACN,OAAsB;QAEvC,KAAK,CAAC,OAAO,CAAC,CAAA;QAFG,YAAO,GAAP,OAAO,CAAe;IAGzC,CAAC;IAMM,oBAAoB;;QACzB,OAAO;YACL,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,MAAA,IAAI,CAAC,OAAO,CAAC,GAAG,mCAAI,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS;SACjF,CAAA;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;QACxD,MAAM,GAAG,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;QAEjF,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,IAAI,EAAE,CAAC;YAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAA;YAClE,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,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAA;QAEpC,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAA;QACvC,MAAM,WAAW,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAA;QAE/C,OAAO,MAAM,iBAAiB,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;YACzE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,IAAA,iCAAgB,EAAC;gBACrC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE;gBAC5B,GAAG;gBACH,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,IAAA,iCAAgB,EAAC,IAAI,CAAC,IAAI,CAAC;gBACxC,GAAG,MAAM;gBACT,GAAG,WAAW;aACf,CAAC,CAAA;YAEF,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;YAE7B,IAAI;iBACD,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;AA/DD,0CA+DC","sourcesContent":["import { 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\"\nimport type { AwsCredentials } from \"./awsCredentials\"\nimport { getS3ContentType, startS3PutObject } from \"./s3UploadHelper\"\n\nexport interface S3UploadConfig {\n region: string\n endpoint?: string\n forcePathStyle?: boolean\n credentials?: AwsCredentials\n}\n\nexport interface S3UploadExtraParams {\n acl?: string\n storageClass?: string\n serverSideEncryption?: string\n}\n\nexport abstract class BaseS3Publisher extends Publisher {\n protected constructor(\n context: PublishContext,\n private readonly options: BaseS3Options\n ) {\n super(context)\n }\n\n protected abstract getBucketName(): string\n\n public abstract getS3UploadConfig(): S3UploadConfig\n\n public getUploadExtraParams(): S3UploadExtraParams {\n return {\n acl: this.options.acl !== null ? (this.options.acl ?? \"public-read\") : undefined,\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 const key = (this.options.path == null ? \"\" : `${this.options.path}/`) + fileName\n\n if (process.env.__TEST_S3_PUBLISHER__ != null) {\n const testFile = path.join(process.env.__TEST_S3_PUBLISHER__, key)\n await mkdir(path.dirname(testFile), { recursive: true })\n await symlink(task.file, testFile)\n return\n }\n\n this.createProgressBar(fileName, -1)\n\n const config = this.getS3UploadConfig()\n const extraParams = this.getUploadExtraParams()\n\n return await cancellationToken.createPromise((resolve, reject, onCancel) => {\n const { req, done } = startS3PutObject({\n bucket: this.getBucketName(),\n key,\n file: task.file,\n contentType: getS3ContentType(task.file),\n ...config,\n ...extraParams,\n })\n\n onCancel(() => req.destroy())\n\n done\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"]}

View file

@ -0,0 +1,8 @@
/**
* Resolves the AWS region for a bucket via the S3 GetBucketLocation API (SigV4-signed).
* Uses path-style endpoint so dotted bucket names pass TLS hostname validation.
* Credentials are resolved via the standard provider chain (env vars ~/.aws/credentials).
* AWS returns an empty LocationConstraint element for us-east-1 (the implicit default region).
* Mirrors the behaviour of the `get-bucket-location` app-builder subcommand.
*/
export declare function getBucketLocation(bucket: string): Promise<string>;

View file

@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBucketLocation = getBucketLocation;
const aws4_1 = require("aws4");
const https_1 = require("https");
const awsCredentials_1 = require("./awsCredentials");
/**
* Resolves the AWS region for a bucket via the S3 GetBucketLocation API (SigV4-signed).
* Uses path-style endpoint so dotted bucket names pass TLS hostname validation.
* Credentials are resolved via the standard provider chain (env vars ~/.aws/credentials).
* AWS returns an empty LocationConstraint element for us-east-1 (the implicit default region).
* Mirrors the behaviour of the `get-bucket-location` app-builder subcommand.
*/
function getBucketLocation(bucket) {
return new Promise((resolve, reject) => {
let settled = false;
const settle = (fn, v) => {
if (!settled) {
settled = true;
fn(v);
}
};
const opts = (0, aws4_1.sign)({
service: "s3",
region: "us-east-1",
method: "GET",
host: "s3.amazonaws.com",
path: `/${bucket}?location`,
}, (0, awsCredentials_1.resolveAwsCredentials)());
const req = (0, https_1.request)({
hostname: opts.host,
path: opts.path,
method: opts.method,
headers: opts.headers,
}, res => {
let body = "";
let bodySize = 0;
const MAX_BODY = 65536;
res.on("data", (chunk) => {
bodySize += chunk.length;
if (bodySize > MAX_BODY) {
req.destroy();
settle(reject, new Error("GetBucketLocation response too large"));
return;
}
body += chunk;
});
res.on("end", () => {
if (res.statusCode !== 200) {
settle(reject, new Error(`GetBucketLocation failed (HTTP ${res.statusCode}): ${body}`));
return;
}
// Response: <LocationConstraint>us-west-2</LocationConstraint> or empty element for us-east-1
const match = body.match(/<LocationConstraint[^>]*>([^<]*)<\/LocationConstraint>/);
const rawRegion = (match === null || match === void 0 ? void 0 : match[1]) || "";
// "EU" is the only non-standard token GetBucketLocation ever returns — a legacy alias
// for eu-west-1 used by buckets created before August 2014. Simple lowercasing is not
// sufficient because "eu" is not a valid region identifier.
const region = rawRegion === "EU" ? "eu-west-1" : rawRegion;
if (region !== "" && !/^[a-z][a-z0-9-]+$/.test(region)) {
settle(reject, new Error(`GetBucketLocation returned unexpected region: ${region}`));
return;
}
settle(resolve, region || "us-east-1");
});
});
req.on("error", e => settle(reject, e));
req.end();
});
}
//# sourceMappingURL=bucketLocation.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"bucketLocation.js","sourceRoot":"","sources":["../../src/s3/bucketLocation.ts"],"names":[],"mappings":";;AAWA,8CAiEC;AA5ED,+BAA2B;AAC3B,iCAA+B;AAC/B,qDAAwD;AAExD;;;;;;GAMG;AACH,SAAgB,iBAAiB,CAAC,MAAc;IAC9C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,MAAM,MAAM,GAAG,CAAC,EAAoB,EAAE,CAAM,EAAE,EAAE;YAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAA;gBACd,EAAE,CAAC,CAAC,CAAC,CAAA;YACP,CAAC;QACH,CAAC,CAAA;QAED,MAAM,IAAI,GAAG,IAAA,WAAI,EACf;YACE,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,WAAW;YACnB,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,kBAAkB;YACxB,IAAI,EAAE,IAAI,MAAM,WAAW;SAC5B,EACD,IAAA,sCAAqB,GAAE,CACxB,CAAA;QAED,MAAM,GAAG,GAAG,IAAA,eAAO,EACjB;YACE,QAAQ,EAAE,IAAI,CAAC,IAAI;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,EACD,GAAG,CAAC,EAAE;YACJ,IAAI,IAAI,GAAG,EAAE,CAAA;YACb,IAAI,QAAQ,GAAG,CAAC,CAAA;YAChB,MAAM,QAAQ,GAAG,KAAK,CAAA;YACtB,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBAC/B,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAA;gBACxB,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC;oBACxB,GAAG,CAAC,OAAO,EAAE,CAAA;oBACb,MAAM,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC,CAAA;oBACjE,OAAM;gBACR,CAAC;gBACD,IAAI,IAAI,KAAK,CAAA;YACf,CAAC,CAAC,CAAA;YACF,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;oBAC3B,MAAM,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,kCAAkC,GAAG,CAAC,UAAU,MAAM,IAAI,EAAE,CAAC,CAAC,CAAA;oBACvF,OAAM;gBACR,CAAC;gBACD,8FAA8F;gBAC9F,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAA;gBAClF,MAAM,SAAS,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAG,CAAC,CAAC,KAAI,EAAE,CAAA;gBAClC,sFAAsF;gBACtF,sFAAsF;gBACtF,4DAA4D;gBAC5D,MAAM,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAA;gBAC3D,IAAI,MAAM,KAAK,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;oBACvD,MAAM,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,iDAAiD,MAAM,EAAE,CAAC,CAAC,CAAA;oBACpF,OAAM;gBACR,CAAC;gBACD,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,WAAW,CAAC,CAAA;YACxC,CAAC,CAAC,CAAA;QACJ,CAAC,CACF,CAAA;QAED,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAA;QACvC,GAAG,CAAC,GAAG,EAAE,CAAA;IACX,CAAC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { sign } from \"aws4\"\nimport { request } from \"https\"\nimport { resolveAwsCredentials } from \"./awsCredentials\"\n\n/**\n * Resolves the AWS region for a bucket via the S3 GetBucketLocation API (SigV4-signed).\n * Uses path-style endpoint so dotted bucket names pass TLS hostname validation.\n * Credentials are resolved via the standard provider chain (env vars → ~/.aws/credentials).\n * AWS returns an empty LocationConstraint element for us-east-1 (the implicit default region).\n * Mirrors the behaviour of the `get-bucket-location` app-builder subcommand.\n */\nexport function getBucketLocation(bucket: string): Promise<string> {\n return new Promise((resolve, reject) => {\n let settled = false\n const settle = (fn: (v: any) => void, v: any) => {\n if (!settled) {\n settled = true\n fn(v)\n }\n }\n\n const opts = sign(\n {\n service: \"s3\",\n region: \"us-east-1\",\n method: \"GET\",\n host: \"s3.amazonaws.com\",\n path: `/${bucket}?location`,\n },\n resolveAwsCredentials()\n )\n\n const req = request(\n {\n hostname: opts.host,\n path: opts.path,\n method: opts.method,\n headers: opts.headers,\n },\n res => {\n let body = \"\"\n let bodySize = 0\n const MAX_BODY = 65536\n res.on(\"data\", (chunk: string) => {\n bodySize += chunk.length\n if (bodySize > MAX_BODY) {\n req.destroy()\n settle(reject, new Error(\"GetBucketLocation response too large\"))\n return\n }\n body += chunk\n })\n res.on(\"end\", () => {\n if (res.statusCode !== 200) {\n settle(reject, new Error(`GetBucketLocation failed (HTTP ${res.statusCode}): ${body}`))\n return\n }\n // Response: <LocationConstraint>us-west-2</LocationConstraint> or empty element for us-east-1\n const match = body.match(/<LocationConstraint[^>]*>([^<]*)<\\/LocationConstraint>/)\n const rawRegion = match?.[1] || \"\"\n // \"EU\" is the only non-standard token GetBucketLocation ever returns — a legacy alias\n // for eu-west-1 used by buckets created before August 2014. Simple lowercasing is not\n // sufficient because \"eu\" is not a valid region identifier.\n const region = rawRegion === \"EU\" ? \"eu-west-1\" : rawRegion\n if (region !== \"\" && !/^[a-z][a-z0-9-]+$/.test(region)) {\n settle(reject, new Error(`GetBucketLocation returned unexpected region: ${region}`))\n return\n }\n settle(resolve, region || \"us-east-1\")\n })\n }\n )\n\n req.on(\"error\", e => settle(reject, e))\n req.end()\n })\n}\n"]}

View file

@ -0,0 +1,13 @@
import { S3Options } from "builder-util-runtime";
import { PublishContext } from "..";
import { BaseS3Publisher, S3UploadConfig, S3UploadExtraParams } 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;
getS3UploadConfig(): S3UploadConfig;
getUploadExtraParams(): S3UploadExtraParams;
toString(): string;
}

View file

@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.S3Publisher = void 0;
const builder_util_1 = require("builder-util");
const awsCredentials_1 = require("./awsCredentials");
const baseS3Publisher_1 = require("./baseS3Publisher");
const bucketLocation_1 = require("./bucketLocation");
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, bucketLocation_1.getBucketLocation)(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;
}
getS3UploadConfig() {
var _a, _b, _c;
return {
region: (_a = this.info.region) !== null && _a !== void 0 ? _a : "us-east-1",
endpoint: (_b = this.info.endpoint) !== null && _b !== void 0 ? _b : undefined,
forcePathStyle: (_c = this.info.forcePathStyle) !== null && _c !== void 0 ? _c : undefined,
credentials: (0, awsCredentials_1.resolveAwsCredentials)(),
};
}
getUploadExtraParams() {
var _a, _b;
const base = super.getUploadExtraParams();
return {
...base,
storageClass: (_a = this.info.storageClass) !== null && _a !== void 0 ? _a : undefined,
serverSideEncryption: (_b = this.info.encryption) !== null && _b !== void 0 ? _b : undefined,
};
}
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

View file

@ -0,0 +1 @@
{"version":3,"file":"s3Publisher.js","sourceRoot":"","sources":["../../src/s3/s3Publisher.ts"],"names":[],"mappings":";;;AAAA,+CAA6D;AAG7D,qDAAwD;AACxD,uDAAwF;AACxF,qDAAoD;AAEpD,MAAa,WAAY,SAAQ,iCAAe;IAG9C,YACE,OAAuB,EACN,IAAe;QAEhC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;QAFH,SAAI,GAAJ,IAAI,CAAW;QAJzB,iBAAY,GAAG,IAAI,CAAA;IAO5B,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,OAAkB,EAAE,qBAAoC,EAAE,aAAsB;QAClH,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;QAC7B,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,wCAAyB,CAAC,mDAAmD,CAAC,CAAA;QAC1F,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;YAC/E,yHAAyH;YACzH,IAAI,CAAC;gBACH,OAAO,CAAC,MAAM,GAAG,MAAM,IAAA,kCAAiB,EAAC,MAAM,CAAC,CAAA;YAClD,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBAChB,IAAI,aAAa,EAAE,CAAC;oBAClB,MAAM,CAAC,CAAA;gBACT,CAAC;qBAAM,CAAC;oBACN,kBAAG,CAAC,IAAI,CAAC,yHAAyH,CAAC,EAAE,CAAC,CAAA;gBACxI,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,OAAO,IAAI,IAAI,IAAI,qBAAqB,IAAI,IAAI,EAAE,CAAC;YAC7D,OAAO,CAAC,OAAO,GAAG,qBAAqB,CAAA;QACzC,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,IAAI,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/D,CAAC;YAAC,OAAe,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAC5D,CAAC;IACH,CAAC;IAES,aAAa;QACrB,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAA;IACzB,CAAC;IAEM,iBAAiB;;QACtB,OAAO;YACL,MAAM,EAAE,MAAA,IAAI,CAAC,IAAI,CAAC,MAAM,mCAAI,WAAW;YACvC,QAAQ,EAAE,MAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,mCAAI,SAAS;YACzC,cAAc,EAAE,MAAA,IAAI,CAAC,IAAI,CAAC,cAAc,mCAAI,SAAS;YACrD,WAAW,EAAE,IAAA,sCAAqB,GAAE;SACrC,CAAA;IACH,CAAC;IAEM,oBAAoB;;QACzB,MAAM,IAAI,GAAG,KAAK,CAAC,oBAAoB,EAAE,CAAA;QACzC,OAAO;YACL,GAAG,IAAI;YACP,YAAY,EAAE,MAAA,IAAI,CAAC,IAAI,CAAC,YAAY,mCAAI,SAAS;YACjD,oBAAoB,EAAE,MAAA,IAAI,CAAC,IAAI,CAAC,UAAU,mCAAI,SAAS;SACxD,CAAA;IACH,CAAC;IAED,QAAQ;QACN,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAA;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAA;QACnC,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,eAAe,QAAQ,GAAG,CAAA;QAC5E,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AApED,kCAoEC","sourcesContent":["import { InvalidConfigurationError, log } from \"builder-util\"\nimport { S3Options } from \"builder-util-runtime\"\nimport { PublishContext } from \"..\"\nimport { resolveAwsCredentials } from \"./awsCredentials\"\nimport { BaseS3Publisher, S3UploadConfig, S3UploadExtraParams } from \"./baseS3Publisher\"\nimport { getBucketLocation } from \"./bucketLocation\"\n\nexport class S3Publisher extends BaseS3Publisher {\n readonly providerName = \"s3\"\n\n constructor(\n context: PublishContext,\n private readonly info: S3Options\n ) {\n super(context, info)\n }\n\n static async checkAndResolveOptions(options: S3Options, channelFromAppVersion: string | null, errorIfCannot: boolean) {\n const bucket = options.bucket\n if (bucket == null) {\n throw new InvalidConfigurationError(`Please specify \"bucket\" for \"s3\" publish provider`)\n }\n\n if (options.endpoint == null && bucket.includes(\".\") && options.region == null) {\n // on dotted bucket names, we need to use a path-based endpoint URL. Path-based endpoint URLs need to include the region.\n try {\n options.region = await getBucketLocation(bucket)\n } catch (e: any) {\n if (errorIfCannot) {\n throw e\n } else {\n log.warn(`cannot compute region for bucket (required because on dotted bucket names, we need to use a path-based endpoint URL): ${e}`)\n }\n }\n }\n\n if (options.channel == null && channelFromAppVersion != null) {\n options.channel = channelFromAppVersion\n }\n\n if (options.endpoint != null && options.endpoint.endsWith(\"/\")) {\n ;(options as any).endpoint = options.endpoint.slice(0, -1)\n }\n }\n\n protected getBucketName(): string {\n return this.info.bucket\n }\n\n public getS3UploadConfig(): S3UploadConfig {\n return {\n region: this.info.region ?? \"us-east-1\",\n endpoint: this.info.endpoint ?? undefined,\n forcePathStyle: this.info.forcePathStyle ?? undefined,\n credentials: resolveAwsCredentials(),\n }\n }\n\n public getUploadExtraParams(): S3UploadExtraParams {\n const base = super.getUploadExtraParams()\n return {\n ...base,\n storageClass: this.info.storageClass ?? undefined,\n serverSideEncryption: this.info.encryption ?? undefined,\n }\n }\n\n toString() {\n const result = super.toString()\n const endpoint = this.info.endpoint\n if (endpoint != null) {\n return result.substring(0, result.length - 1) + `, endpoint: ${endpoint})`\n }\n return result\n }\n}\n"]}

View file

@ -0,0 +1,31 @@
import * as http from "http";
import type { AwsCredentials } from "./awsCredentials";
export interface S3PutObjectParams {
bucket: string;
key: string;
file: string;
region: string;
endpoint?: string;
forcePathStyle?: boolean;
contentType: string;
acl?: string;
storageClass?: string;
serverSideEncryption?: string;
credentials?: AwsCredentials;
}
/**
* Uploads a file to S3 (or S3-compatible storage) using a single PutObject request.
* Suitable for files up to 5 GB the S3 single-part upload limit.
* Returns the underlying ClientRequest so callers can abort mid-flight.
* Mirrors the behaviour of the `publish-s3` app-builder subcommand.
*/
export declare function startS3PutObject(params: S3PutObjectParams): {
req: http.ClientRequest;
done: Promise<void>;
};
/**
* Returns the MIME content-type for an S3 upload key, using explicit overrides
* for formats that mime databases commonly misidentify. Mirrors the Go binary's
* getContentType() function in pkg/publisher/s3.go.
*/
export declare function getS3ContentType(file: string): string;

View file

@ -0,0 +1,117 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.startS3PutObject = startS3PutObject;
exports.getS3ContentType = getS3ContentType;
const aws4_1 = require("aws4");
const fs = require("fs");
const http = require("http");
const https = require("https");
const mime = require("mime");
const path = require("path");
/**
* Uploads a file to S3 (or S3-compatible storage) using a single PutObject request.
* Suitable for files up to 5 GB the S3 single-part upload limit.
* Returns the underlying ClientRequest so callers can abort mid-flight.
* Mirrors the behaviour of the `publish-s3` app-builder subcommand.
*/
function startS3PutObject(params) {
const stat = fs.statSync(params.file);
const region = params.region;
let hostname;
let rawPath;
let isHttp = false;
if (params.endpoint != null) {
const u = new URL(params.endpoint);
isHttp = u.protocol === "http:";
hostname = u.port ? `${u.hostname}:${u.port}` : u.hostname;
// path-style for custom endpoints (handles buckets whose names contain dots)
rawPath = `/${params.bucket}/${params.key}`;
}
else if (params.forcePathStyle) {
hostname = `s3.${region}.amazonaws.com`;
rawPath = `/${params.bucket}/${params.key}`;
}
else {
hostname = `${params.bucket}.s3.${region}.amazonaws.com`;
rawPath = `/${params.key}`;
}
// URL-encode each path segment individually so forward-slashes in keys are preserved
const urlPath = "/" + rawPath.slice(1).split("/").map(encodeURIComponent).join("/");
const headers = {
"Content-Type": params.contentType,
"Content-Length": String(stat.size),
// Declare the payload as unsigned so aws4 signs over this literal string
// rather than defaulting to SHA256("") — which would not match the streamed body.
"x-amz-content-sha256": "UNSIGNED-PAYLOAD",
};
if (params.acl != null) {
headers["x-amz-acl"] = params.acl;
}
if (params.storageClass != null) {
headers["x-amz-storage-class"] = params.storageClass;
}
if (params.serverSideEncryption != null) {
headers["x-amz-server-side-encryption"] = params.serverSideEncryption;
}
const signed = (0, aws4_1.sign)({
service: "s3",
region,
method: "PUT",
host: hostname,
path: urlPath,
headers,
}, params.credentials);
const transport = isHttp ? http : https;
let resolvePromise;
let rejectPromise;
const done = new Promise((res, rej) => {
resolvePromise = res;
rejectPromise = rej;
});
const req = transport.request({
hostname: hostname.split(":")[0],
port: hostname.includes(":") ? Number(hostname.split(":")[1]) : undefined,
path: urlPath,
method: "PUT",
headers: signed.headers,
}, res => {
if (res.statusCode === 200) {
res.resume();
res.on("end", resolvePromise);
}
else {
let body = "";
let bodySize = 0;
const MAX_ERROR_BODY = 65536;
res.on("data", (chunk) => {
bodySize += chunk.length;
if (bodySize <= MAX_ERROR_BODY) {
body += chunk;
}
});
res.on("end", () => rejectPromise(new Error(`S3 PutObject failed (HTTP ${res.statusCode}): ${body.slice(0, 512)}`)));
}
});
req.on("error", rejectPromise);
const fileStream = fs.createReadStream(params.file);
fileStream.on("error", rejectPromise);
req.on("close", () => fileStream.destroy());
fileStream.pipe(req);
return { req, done };
}
/**
* Returns the MIME content-type for an S3 upload key, using explicit overrides
* for formats that mime databases commonly misidentify. Mirrors the Go binary's
* getContentType() function in pkg/publisher/s3.go.
*/
function getS3ContentType(file) {
var _a, _b;
const ext = path.extname(file).toLowerCase();
const overrides = {
".appimage": "application/vnd.appimage",
".blockmap": "application/gzip",
".snap": "application/vnd.snap",
};
return (_b = (_a = overrides[ext]) !== null && _a !== void 0 ? _a : mime.getType(file)) !== null && _b !== void 0 ? _b : "application/octet-stream";
}
//# sourceMappingURL=s3UploadHelper.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,11 @@
import { SpacesOptions } from "builder-util-runtime";
import { PublishContext } from "../";
import { BaseS3Publisher, S3UploadConfig } 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;
getS3UploadConfig(): S3UploadConfig;
}

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;
}
getS3UploadConfig() {
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)");
}
return {
region: this.info.region,
endpoint: `https://${this.info.region}.digitaloceanspaces.com`,
credentials: { accessKeyId: accessKey, secretAccessKey: secretKey },
};
}
}
exports.SpacesPublisher = SpacesPublisher;
//# sourceMappingURL=spacesPublisher.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"spacesPublisher.js","sourceRoot":"","sources":["../../src/s3/spacesPublisher.ts"],"names":[],"mappings":";;;AAAA,+CAAyE;AAGzE,uDAAmE;AAEnE,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,cAAuB;QACjH,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;IAEM,iBAAiB;QACtB,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,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM;YACxB,QAAQ,EAAE,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,yBAAyB;YAC9D,WAAW,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE;SACpE,CAAA;IACH,CAAC;CACF;AA5CD,0CA4CC","sourcesContent":["import { InvalidConfigurationError, isEmptyOrSpaces } from \"builder-util\"\nimport { SpacesOptions } from \"builder-util-runtime\"\nimport { PublishContext } from \"../\"\nimport { BaseS3Publisher, S3UploadConfig } 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 public getS3UploadConfig(): S3UploadConfig {\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 return {\n region: this.info.region,\n endpoint: `https://${this.info.region}.digitaloceanspaces.com`,\n credentials: { accessKeyId: accessKey, secretAccessKey: secretKey },\n }\n }\n}\n"]}

View file

@ -0,0 +1,16 @@
import { SnapStoreOptions } from "builder-util-runtime/out/publishOptions";
import { PublishContext, UploadTask } from ".";
import { Publisher } from "./publisher";
export declare class SnapStorePublisher extends Publisher {
readonly context: PublishContext;
private readonly options;
private readonly credentials;
readonly providerName = "snapStore";
constructor(context: PublishContext, options: SnapStoreOptions, credentials: {
cscLink: string | undefined;
resourcesDir: string;
});
upload(task: UploadTask): Promise<any>;
toString(): string;
}
export declare function resolveSnapCredentials(cscLink: string | undefined, resourcesDir: string | undefined): Promise<Record<string, string>>;

View file

@ -0,0 +1,97 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SnapStorePublisher = void 0;
exports.resolveSnapCredentials = resolveSnapCredentials;
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, credentials) {
super(context);
this.context = context;
this.options = options;
this.credentials = credentials;
this.providerName = "snapStore";
}
async upload(task) {
var _a;
this.createProgressBar(path.basename(task.file), -1);
await checkSnapcraft();
// Credentials are injected via SNAPCRAFT_STORE_CREDENTIALS so that the
// snapcraft subprocess authenticates without an interactive login session.
// Generate credentials with: snapcraft export-login -
// https://documentation.ubuntu.com/snapcraft/stable/how-to/publishing/authenticate/
const credEnv = await resolveSnapCredentials(this.credentials.cscLink, this.credentials.resourcesDir);
// Channel format: [<track>/]<risk>[/<branch>] e.g. "stable", "edge", "lts/stable"
// Multiple channels are comma-separated: "beta,edge"
let channels = (_a = this.options.channels) !== null && _a !== void 0 ? _a : ["edge"];
if (typeof channels === "string") {
channels = channels.split(",");
}
// `snapcraft upload <snap-file> --release <channels>` uploads the snap and
// immediately releases it to the specified channels upon store review.
// https://documentation.ubuntu.com/snapcraft/stable/reference/commands/upload/
const args = ["upload", task.file];
if (channels.length > 0) {
args.push("--release", channels.join(","));
}
return (0, builder_util_1.spawn)("snapcraft", args, {
stdio: ["ignore", "inherit", "inherit"],
env: { ...process.env, ...credEnv },
});
}
toString() {
return "Snap Store";
}
}
exports.SnapStorePublisher = SnapStorePublisher;
// Resolves Snap Store credentials from cscLink / SNAP_CSC_LINK and returns
// them as { SNAPCRAFT_STORE_CREDENTIALS } so they can be injected into the
// snapcraft subprocess environment. The value is the raw export-login output
// (base64-encoded or a file path handled by loadCscLink).
// https://documentation.ubuntu.com/snapcraft/stable/how-to/publishing/authenticate/
async function resolveSnapCredentials(cscLink, resourcesDir) {
var _a;
const link = (_a = (cscLink !== null && cscLink !== void 0 ? cscLink : process.env.SNAP_CSC_LINK)) === null || _a === void 0 ? void 0 : _a.trim();
if (!link) {
return {};
}
const credentials = await (0, builder_util_1.loadCscLink)(link, resourcesDir);
const trimmed = credentials.trim();
if (!trimmed) {
throw new Error("Resolved snap store credentials are empty");
}
return { SNAPCRAFT_STORE_CREDENTIALS: trimmed };
}
// Snapcraft 7 introduced SNAPCRAFT_STORE_CREDENTIALS as the standard
// non-interactive credential mechanism. Earlier versions used a different
// auth format that is no longer compatible with this publisher.
// https://documentation.ubuntu.com/snapcraft/stable/how-to/publishing/authenticate/
const REQUIRED_SNAPCRAFT_MAJOR = 7;
async function checkSnapcraft() {
const installMessage = process.platform === "darwin" ? "brew install snapcraft" : "sudo snap install snapcraft --classic";
let versionOutput;
try {
versionOutput = await (0, builder_util_1.exec)("snapcraft", ["--version"]);
}
catch {
throw new Error(`snapcraft is not installed, please: ${installMessage}`);
}
const trimmed = versionOutput.trim();
// Edge-channel installs report "snapcraft, version edge" with no semver — skip the check.
if (trimmed === "snapcraft, version edge") {
return;
}
// Handles both output formats:
// "snapcraft, version X.Y.Z" (snapcraft ≤ 6)
// "snapcraft X.Y.Z" (snapcraft 7+)
let s = trimmed.replace(/^snapcraft/, "").trim();
s = s.replace(/^,/, "").trim();
s = s.replace(/^version/, "").trim();
s = s.replace(/^'|'$/g, "");
const major = parseInt(s.split(".")[0], 10);
if (!Number.isFinite(major) || major < REQUIRED_SNAPCRAFT_MAJOR) {
throw new Error(`at least snapcraft ${REQUIRED_SNAPCRAFT_MAJOR}.0.0 is required, but ${trimmed} installed, please: ${installMessage}`);
}
}
//# sourceMappingURL=snapStorePublisher.js.map

File diff suppressed because one or more lines are too long

1
electron/node_modules/electron-publish/out/util.d.ts generated vendored Normal file
View file

@ -0,0 +1 @@
export declare const trimStringWithWarn: (str: string, maxLength: number, warnMessage: string) => string;

13
electron/node_modules/electron-publish/out/util.js generated vendored Normal file
View file

@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.trimStringWithWarn = void 0;
const builder_util_1 = require("builder-util");
const trimStringWithWarn = (str, maxLength, warnMessage) => {
if (str.length <= maxLength) {
return str;
}
builder_util_1.log.warn({ length: str.length, maxLength }, warnMessage);
return str.substring(0, maxLength);
};
exports.trimStringWithWarn = trimStringWithWarn;
//# sourceMappingURL=util.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;;AAAA,+CAAkC;AAE3B,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAE,SAAiB,EAAE,WAAmB,EAAU,EAAE;IAChG,IAAI,GAAG,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,kBAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,WAAW,CAAC,CAAA;IACxD,OAAO,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;AACpC,CAAC,CAAA;AANY,QAAA,kBAAkB,sBAM9B","sourcesContent":["import { log } from \"builder-util\"\n\nexport const trimStringWithWarn = (str: string, maxLength: number, warnMessage: string): string => {\n if (str.length <= maxLength) {\n return str\n }\n log.warn({ length: str.length, maxLength }, warnMessage)\n return str.substring(0, maxLength)\n}\n"]}

View file

@ -1,7 +1,7 @@
{
"name": "electron-publish",
"version": "23.6.0",
"main": "out/publisher.js",
"version": "26.15.3",
"main": "out/index.js",
"author": "Vladimir Krivosheev",
"license": "MIT",
"repository": {
@ -16,15 +16,18 @@
],
"dependencies": {
"@types/fs-extra": "^9.0.11",
"builder-util": "23.6.0",
"builder-util-runtime": "9.1.1",
"chalk": "^4.1.1",
"fs-extra": "^10.0.0",
"aws4": "^1.13.2",
"chalk": "^4.1.2",
"form-data": "^4.0.5",
"fs-extra": "^10.1.0",
"lazy-val": "^1.0.5",
"mime": "^2.5.2"
"mime": "^2.5.2",
"builder-util": "26.15.3",
"builder-util-runtime": "9.7.0"
},
"typings": "./out/publisher.d.ts",
"typings": "./out/index.d.ts",
"devDependencies": {
"@types/aws4": "^1.11.6",
"@types/mime": "2.0.3"
}
}

View file

@ -1,7 +1,7 @@
# electron-publish
Part of [electron-builder](https://github.com/electron-userland/electron-builder).
Part of [electron-builder](https://github.com/electron-userland/electron-builder).
See the [Publishing Artifacts](https://www.electron.build/configuration/publish) for more information.
See the [Publishing Artifacts](https://www.electron.build/publish) for more information.
Can be used standalone.