Skip to content

Commit

Permalink
feat(s3): Ability to not add "x-amz-acl": "public-read" to the hea…
Browse files Browse the repository at this point in the history
…der when uploading artefacts to S3 bucket

Close #1822, Close #1618
  • Loading branch information
develar committed Jul 12, 2017
1 parent 6e3581f commit 203c8c4
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 22 deletions.
4 changes: 3 additions & 1 deletion docs/Publishing Artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
5 changes: 4 additions & 1 deletion packages/electron-builder-http/src/publishOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 17 additions & 5 deletions packages/electron-publisher-s3/src/s3Publisher.ts
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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) {
Expand Down
24 changes: 9 additions & 15 deletions packages/electron-publisher-s3/src/uploader.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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")
Expand All @@ -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)
}

Expand All @@ -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
Expand Down

0 comments on commit 203c8c4

Please sign in to comment.