Skip to content

Commit

Permalink
feat: Adding Keygen as an official publisher/updater for electron-bui…
Browse files Browse the repository at this point in the history
…lder #6167
  • Loading branch information
mmaietta committed Aug 19, 2021
1 parent 2656b5e commit 7f81021
Show file tree
Hide file tree
Showing 28 changed files with 23,199 additions and 4,409 deletions.
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -16,7 +16,6 @@ out/

/packages/dmg-builder/vendor/
/packages/electron-builder/README.md
#/packages/app-builder-lib/scheme.json

/scripts/jsdoc/out/
/scripts/renderer/out/
Expand Down
5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -25,7 +25,7 @@
"generate-changeset": "pnpm changeset",
"ci:version": "pnpm changelog && changeset version && node scripts/update-package-version-export.js && git add .",
"ci:publish": "pnpm compile && pnpm publish -r",
"schema": "typescript-json-schema packages/app-builder-lib/tsconfig.json Configuration --out packages/app-builder-lib/scheme.json --noExtraProps --useTypeOfKeyword --strictNullChecks --required && node ./scripts/fix-schema.js",
"schema": "ts-generate-schema packages/app-builder-lib/src/configuration.ts",
"jsdoc": "ts2jsdoc packages/builder-util-runtime packages/builder-util packages/app-builder-lib packages/electron-builder packages/electron-publish",
"jsdoc2md": "node scripts/jsdoc2md.js",
"/////": "git clone --single-branch -b docs git@github.com:electron-userland/electron-builder.git docs",
Expand All @@ -43,6 +43,7 @@
"dmg-license": "1.0.9"
},
"devDependencies": {
"@babel/core": "^7",
"@babel/plugin-transform-modules-commonjs": "7.14.5",
"@changesets/changelog-git": "0.1.7",
"@changesets/cli": "2.16.0",
Expand All @@ -61,9 +62,9 @@
"path-sort": "0.1.0",
"prettier": "2.3.2",
"source-map-support": "0.5.19",
"ts-generate-schema": "^2.0.0",
"ts-jsdoc": "3.2.2",
"typescript": "4.3.5",
"typescript-json-schema": "0.50.1",
"v8-compile-cache": "2.3.0"
},
"engines": {
Expand Down
1 change: 0 additions & 1 deletion packages/app-builder-lib/package.json
Expand Up @@ -6,7 +6,6 @@
"files": [
"out",
"templates",
"scheme.json",
"electron-osx-sign",
"certs/root_certs.keychain"
],
Expand Down
119 changes: 119 additions & 0 deletions packages/app-builder-lib/src/publish/KeygenPublisher.ts
@@ -0,0 +1,119 @@
import { Arch, InvalidConfigurationError, log, isEmptyOrSpaces } from "builder-util"
import { httpExecutor } from "builder-util/out/nodeHttpExecutor"
import { ClientRequest, RequestOptions } from "http"
import { HttpPublisher, PublishContext } from "electron-publish"
import { KeygenOptions } from "builder-util-runtime/out/publishOptions"
import { configureRequestOptions, HttpError, parseJson } from "builder-util-runtime"
import * as path from "path"
export class KeygenPublisher extends HttpPublisher {
readonly providerName = "keygen"
readonly hostname = "api.keygen.sh"

private readonly info: KeygenOptions
private readonly auth: string
private readonly version: string
private readonly basePath: string

constructor(context: PublishContext, info: KeygenOptions, version: string) {
super(context)

const token = process.env.KEYGEN_TOKEN
if (isEmptyOrSpaces(token)) {
throw new InvalidConfigurationError(`Keygen token is not set using env "KEYGEN_TOKEN" (see https://www.electron.build/configuration/publish#KeygenOptions)`)
}

this.info = info
this.auth = `Bearer ${token.trim()}`
this.version = version
this.basePath = `/v1/accounts/${this.info.account}/releases`
}

protected async doUpload(
fileName: string,
arch: Arch,
dataLength: number,
requestProcessor: (request: ClientRequest, reject: (error: Error) => void) => void,
file?: string
): Promise<any> {
for (let attemptNumber = 0; ; attemptNumber++) {
try {
const { data, errors } = await this.upsertVersion(fileName, dataLength)
const releaseId = data.id

This comment has been minimized.

Copy link
@ezekg

ezekg Aug 19, 2021

Contributor

The data and errors properties will never be returned at the same time. So on API error, this will be accessing undefined.id, which will throw.

I suggest moving the if (errors) { ... } block before accessing data.id.

if (releaseId == null) {
log.warn({ file: fileName, reason: "UUID doesn't exist and was not created" }, "upserting release failed")
return
}
if (errors) {
log.error({ ...errors }, "upserting release returned errors. Continuing to attempt upload since UUID is present")

This comment has been minimized.

Copy link
@ezekg

ezekg Aug 19, 2021

Contributor

The id will never be present if there is an error, so the message here can be adjusted.

}
const upload: RequestOptions = {
hostname: this.hostname,
path: `${this.basePath}/${releaseId}/artifact`,
headers: {
Accept: "application/vnd.api+json",
"Content-Length": dataLength,
},
}
await httpExecutor.doApiRequest(configureRequestOptions(upload, this.auth, "PUT"), this.context.cancellationToken, requestProcessor)
return releaseId
} catch (e) {
if (attemptNumber < 3 && ((e instanceof HttpError && e.statusCode === 502) || e.code === "EPIPE")) {

This comment has been minimized.

Copy link
@ezekg

ezekg Aug 19, 2021

Contributor

If you want to ignore 5xx errors, I'd suggest also checking for other common 500s: 500, 503 and 504.
Common as in commonly used, not commonly occurring. 🙂

continue
}

throw e
}
}
}

private async upsertVersion(fileName: string, dataLength: number): Promise<{ data: any; errors: any }> {
const req: RequestOptions = {
hostname: this.hostname,
method: "PUT",
path: this.basePath,
headers: {
"Content-Type": "application/vnd.api+json",
Accept: "application/vnd.api+json",
},
}
const data = {
data: {
type: "release",
attributes: {
filename: fileName,
filetype: path.extname(fileName),
filesize: dataLength,
version: this.version,
platform: this.info.platform,
channel: this.info.channel || "stable",
},
relationships: {
product: {
data: {
type: "product",
id: this.info.product,
},
},
},
},
}
log.debug({ data: JSON.stringify(data) }, "Keygen upsert release")
return parseJson(httpExecutor.request(configureRequestOptions(req, this.auth, "PUT"), this.context.cancellationToken, data))
}

async deleteRelease(releaseId: string): Promise<void> {
const req: RequestOptions = {
hostname: this.hostname,
path: `${this.basePath}/${releaseId}`,
headers: {
Accept: "application/vnd.api+json",
},
}
await httpExecutor.request(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})`
}
}
17 changes: 17 additions & 0 deletions packages/app-builder-lib/src/publish/PublishManager.ts
Expand Up @@ -7,6 +7,7 @@ import {
getS3LikeProviderBaseUrl,
GithubOptions,
githubUrl,
KeygenOptions,
PublishConfiguration,
PublishProvider,
} from "builder-util-runtime"
Expand All @@ -29,6 +30,7 @@ import { expandMacro } from "../util/macroExpander"
import { WinPackager } from "../winPackager"
import { SnapStoreOptions, SnapStorePublisher } from "./SnapStorePublisher"
import { createUpdateInfoTasks, UpdateInfoFileTask, writeUpdateInfoFiles } from "./updateInfoBuilder"
import { KeygenPublisher } from "./KeygenPublisher"

const publishForPrWarning =
"There are serious security concerns with PUBLISH_FOR_PULL_REQUEST=true (see the CircleCI documentation (https://circleci.com/docs/1.0/fork-pr-builds/) for details)" +
Expand Down Expand Up @@ -297,6 +299,9 @@ export function createPublisher(context: PublishContext, version: string, publis
case "bintray":
return new BintrayPublisher(context, publishConfig as BintrayOptions, version, options)

case "keygen":
return new KeygenPublisher(context, publishConfig as KeygenOptions, version)

case "generic":
return null

Expand All @@ -321,6 +326,9 @@ function requireProviderClass(provider: string, packager: Packager): any | null
case "generic":
return null

case "keygen":
return KeygenPublisher

case "s3":
return S3Publisher

Expand Down Expand Up @@ -419,6 +427,8 @@ async function resolvePublishConfigurations(
serviceName = "github"
} else if (!isEmptyOrSpaces(process.env.BT_TOKEN)) {
serviceName = "bintray"
} else if (!isEmptyOrSpaces(process.env.KEYGEN_TOKEN)) {
serviceName = "keygen"
}

if (serviceName != null) {
Expand Down Expand Up @@ -499,6 +509,13 @@ async function getResolvedPublishConfig(
return options
}

if (provider === "keygen") {
return {
...options,
platform: platformPackager?.platform.name,
} as KeygenOptions
}

const isGithub = provider === "github"
if (!isGithub && provider !== "bintray") {
return options
Expand Down

0 comments on commit 7f81021

Please sign in to comment.