/
KeygenPublisher.ts
119 lines (110 loc) · 4.1 KB
/
KeygenPublisher.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
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")
}
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")) {
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})`
}
}