From 203c8c4a81e4e57fc6717222a9b62e76e9874d24 Mon Sep 17 00:00:00 2001 From: develar <develar@gmail.com> Date: Wed, 12 Jul 2017 10:14:13 +0200 Subject: [PATCH] feat(s3): Ability to not add ` "x-amz-acl": "public-read"` to the header when uploading artefacts to S3 bucket Close #1822, Close #1618 --- docs/Publishing Artifacts.md | 4 +++- .../src/publishOptions.ts | 5 +++- .../electron-publisher-s3/src/s3Publisher.ts | 22 +++++++++++++---- .../electron-publisher-s3/src/uploader.ts | 24 +++++++------------ 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/docs/Publishing Artifacts.md b/docs/Publishing Artifacts.md index 44cd39aa488..9222345dc7d 100644 --- a/docs/Publishing Artifacts.md +++ b/docs/Publishing Artifacts.md @@ -143,7 +143,9 @@ Or in the [~/.aws/credentials](http://docs.aws.amazon.com/sdk-for-javascript/v2/ * <a name="S3Options-path"></a>`path` = `/` String - The directory path. * <a name="S3Options-region"></a>`region` String - The region. Is determined and set automatically when publishing. * <a name="S3Options-channel"></a>`channel` = `latest` String - The channel. -* <a name="S3Options-acl"></a>`acl` = `public-read` "private" | "public-read" - The ACL. +* <a name="S3Options-acl"></a>`acl` = `public-read` "private" | "public-read" - The ACL. Set to `null` to not [add](https://github.com/electron-userland/electron-builder/issues/1822). + + Please see [required permissions for the S3 provider](https://github.com/electron-userland/electron-builder/issues/1618#issuecomment-314679128). * <a name="S3Options-storageClass"></a>`storageClass` = `STANDARD` "STANDARD" | "REDUCED_REDUNDANCY" | "STANDARD_IA" - The type of storage to use for the object. * <a name="S3Options-provider"></a>**`provider`** "github" | "bintray" | "s3" | "generic" - The provider. diff --git a/packages/electron-builder-http/src/publishOptions.ts b/packages/electron-builder-http/src/publishOptions.ts index 2967d1a40c7..959ebc1e47e 100644 --- a/packages/electron-builder-http/src/publishOptions.ts +++ b/packages/electron-builder-http/src/publishOptions.ts @@ -116,7 +116,10 @@ export interface S3Options extends PublishConfiguration { readonly channel?: string | null /** - * The ACL. + * The ACL. Set to `null` to not [add](https://github.com/electron-userland/electron-builder/issues/1822). + * + * Please see [required permissions for the S3 provider](https://github.com/electron-userland/electron-builder/issues/1618#issuecomment-314679128). + * * @default public-read */ readonly acl?: "private" | "public-read" | null diff --git a/packages/electron-publisher-s3/src/s3Publisher.ts b/packages/electron-publisher-s3/src/s3Publisher.ts index 39d3b554e17..6aa8b0d242a 100644 --- a/packages/electron-publisher-s3/src/s3Publisher.ts +++ b/packages/electron-publisher-s3/src/s3Publisher.ts @@ -1,12 +1,14 @@ import { S3 } from "aws-sdk" +import { CreateMultipartUploadRequest, ObjectCannedACL, StorageClass } from "aws-sdk/clients/s3" import { S3Options } from "electron-builder-http/out/publishOptions" import { debug } from "electron-builder-util" import { PublishContext, Publisher } from "electron-publish" import { ProgressCallback } from "electron-publish/out/progress" import { ensureDir, stat, symlink } from "fs-extra-p" +import mime from "mime" import * as path from "path" import { basename } from "path" -import { S3Client } from "./uploader" +import { S3Client, Uploader } from "./uploader" export default class S3Publisher extends Publisher { readonly providerName = "S3" @@ -50,11 +52,21 @@ export default class S3Publisher extends Publisher { return } - const uploader = client.createFileUploader(file, target, { + const s3Options: CreateMultipartUploadRequest = { + Key: target, Bucket: this.info.bucket!, - ACL: this.info.acl || "public-read", - StorageClass: this.info.storageClass || undefined - }) + ContentType: mime.lookup(file) + } + + // if explicitly set to null, do not add + if (this.info.acl !== null) { + s3Options.ACL = this.info.acl as ObjectCannedACL || "public-read" + } + if (this.info.storageClass != null) { + s3Options.StorageClass = this.info.storageClass as StorageClass + } + + const uploader = new Uploader(client, s3Options, file, fileStat) const progressBar = this.createProgressBar(fileName, fileStat) if (progressBar != null) { diff --git a/packages/electron-publisher-s3/src/uploader.ts b/packages/electron-publisher-s3/src/uploader.ts index 2375328a5d2..e60d9400109 100644 --- a/packages/electron-publisher-s3/src/uploader.ts +++ b/packages/electron-publisher-s3/src/uploader.ts @@ -1,9 +1,9 @@ import { config as awsConfig, S3 } from "aws-sdk" +import { CreateMultipartUploadRequest } from "aws-sdk/clients/s3" import BluebirdPromise from "bluebird-lst" import { createHash } from "crypto" import { EventEmitter } from "events" -import { createReadStream, stat } from "fs-extra-p" -import mime from "mime" +import { createReadStream, Stats } from "fs-extra-p" import { cpus } from "os" const MAX_PUT_OBJECT_SIZE = 5 * 1024 * 1024 * 1024 @@ -45,10 +45,6 @@ export class S3Client { throw new Error("Maximum multipartUploadSize is 5GB.") } } - - createFileUploader(localFile: string, target: string, s3Options: any) { - return new Uploader(this, {Key: target, ...s3Options}, localFile) - } } export class Uploader extends EventEmitter { @@ -57,16 +53,15 @@ export class Uploader extends EventEmitter { private cancelled = false - /** @readonly */ - contentLength: number + readonly contentLength: number - constructor(private readonly client: S3Client, private readonly s3Options: any, private readonly localFile: string) { + constructor(private readonly client: S3Client, private readonly s3Options: CreateMultipartUploadRequest, private readonly localFile: string, localFileStat: Stats) { super() + + this.contentLength = localFileStat.size } async upload() { - this.contentLength = (await stat(this.localFile)).size - const client = this.client if (this.contentLength < client.multipartUploadThreshold) { const md5 = await hashFile(this.localFile, "md5", "base64") @@ -83,7 +78,7 @@ export class Uploader extends EventEmitter { throw new Error(`File size exceeds maximum object size: ${this.localFile}`) } - const data = await this.runOrRetry(() => client.s3.createMultipartUpload({ContentType: mime.lookup(this.localFile), ...this.s3Options}).promise()) + const data = await this.runOrRetry(() => client.s3.createMultipartUpload(this.s3Options).promise()) await this.multipartUpload(data.UploadId!, multipartUploadSize) } @@ -95,10 +90,9 @@ export class Uploader extends EventEmitter { this.loaded = 0 return new BluebirdPromise<any>((resolve, reject) => { this.client.s3.putObject({ - ContentType: mime.lookup(this.localFile), - ContentLength: this.contentLength, Body: createReadStream(this.localFile), - ContentMD5: md5, ...this.s3Options + ContentMD5: md5, + ...this.s3Options, }) .on("httpUploadProgress", progress => { this.loaded = progress.loaded