Skip to content

Commit

Permalink
feat(publisher-gcs): add Google Cloud Storage publisher (#2100)
Browse files Browse the repository at this point in the history
Co-authored-by: Erick Zhao <erick@hotmail.ca>
  • Loading branch information
mahnunchik and erickzhao authored Nov 16, 2023
1 parent 694d549 commit 601314e
Show file tree
Hide file tree
Showing 5 changed files with 425 additions and 14 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@
"@doyensec/electronegativity": "^1.9.1",
"@electron/get": "^3.0.0",
"@electron/osx-sign": "^1.0.5",
"@electron/rebuild": "^3.2.10",
"@electron/packager": "^18.0.0",
"@electron/rebuild": "^3.2.10",
"@google-cloud/storage": "^7.5.0",
"@malept/cross-spawn-promise": "^2.0.0",
"@octokit/core": "^3.2.4",
"@octokit/plugin-retry": "^3.0.9",
Expand Down
30 changes: 30 additions & 0 deletions packages/publisher/gcs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@electron-forge/publisher-gcs",
"version": "7.0.0",
"description": "Google Cloud Storage publisher for Electron Forge",
"repository": "https://github.com/electron/forge",
"author": "Evgeny Vlasenko",
"license": "MIT",
"main": "dist/PublisherGCS.js",
"typings": "dist/PublisherGCS.d.ts",
"devDependencies": {
"chai": "^4.3.3",
"mocha": "^9.0.1"
},
"engines": {
"node": ">= 14.17.5"
},
"dependencies": {
"@electron-forge/publisher-base": "7.0.0",
"@electron-forge/shared-types": "7.0.0",
"@google-cloud/storage": "^7.5.0",
"debug": "^4.3.1"
},
"publishConfig": {
"access": "public"
},
"files": [
"dist",
"src"
]
}
37 changes: 37 additions & 0 deletions packages/publisher/gcs/src/Config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { PredefinedAcl, StorageOptions } from '@google-cloud/storage';

export interface PublisherGCSConfig {
/**
* Options passed into the `Storage` client constructor.
* See https://cloud.google.com/nodejs/docs/reference/storage/latest/storage/storage for full reference.
*/
storageOptions: StorageOptions;
/**
* The name of the Google Cloud Storage bucket where artifacts are uploaded.
*/
bucket?: string;
/**
* The key prefix where artifacts are uploaded, e.g., `my/prefix`.
*
* Defaults to the application `version` specified in the app's `package.json`.
*/
folder?: string;
/**
* Apply a predefined set of access controls to this object.
*/
predefinedAcl?: PredefinedAcl;
/**
* Whether to make uploaded artifacts public to the internet.
* Alias for config.predefinedAcl = 'publicRead'.
*/
public?: boolean;
/**
* Whether to make uploaded artifacts private.
* Alias for config.predefinedAcl = 'private'.
*/
private?: boolean;
/**
* Custom function to provide the key to upload a given file to
*/
keyResolver?: (fileName: string, platform: string, arch: string) => string;
}
80 changes: 80 additions & 0 deletions packages/publisher/gcs/src/PublisherGCS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import path from 'path';

import { PublisherBase, PublisherOptions } from '@electron-forge/publisher-base';
import { Storage } from '@google-cloud/storage';
import debug from 'debug';

import { PublisherGCSConfig } from './Config';

const d = debug('electron-forge:publish:gcs');

type GCSArtifact = {
path: string;
keyPrefix: string;
platform: string;
arch: string;
};

export default class PublisherGCS extends PublisherBase<PublisherGCSConfig> {
name = 'gcs';

private GCSKeySafe = (key: string) => {
return key.replace(/@/g, '_').replace(/\//g, '_');
};

async publish({ makeResults, setStatusLine }: PublisherOptions): Promise<void> {
const artifacts: GCSArtifact[] = [];

if (!this.config.bucket) {
throw new Error('In order to publish to Google Cloud Storage you must set the "bucket" property in your Forge config.');
}

for (const makeResult of makeResults) {
artifacts.push(
...makeResult.artifacts.map((artifact) => ({
path: artifact,
keyPrefix: this.config.folder || this.GCSKeySafe(makeResult.packageJSON.name),
platform: makeResult.platform,
arch: makeResult.arch,
}))
);
}

const storage = new Storage(this.config.storageOptions);

const bucket = storage.bucket(this.config.bucket);

d('creating Google Cloud Storage client with options:', this.config);

let uploaded = 0;
const updateStatusLine = () => setStatusLine(`Uploading distributable (${uploaded}/${artifacts.length})`);

updateStatusLine();
await Promise.all(
artifacts.map(async (artifact) => {
d('uploading:', artifact.path);

await bucket.upload(artifact.path, {
gzip: true,
destination: this.keyForArtifact(artifact),
predefinedAcl: this.config.predefinedAcl,
public: this.config.public,
private: this.config.private,
});

uploaded += 1;
updateStatusLine();
})
);
}

keyForArtifact(artifact: GCSArtifact): string {
if (this.config.keyResolver) {
return this.config.keyResolver(path.basename(artifact.path), artifact.platform, artifact.arch);
}

return `${artifact.keyPrefix}/${artifact.platform}/${artifact.arch}/${path.basename(artifact.path)}`;
}
}

export { PublisherGCS, PublisherGCSConfig };
Loading

0 comments on commit 601314e

Please sign in to comment.