-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
appInfo.ts
186 lines (159 loc) · 6.47 KB
/
appInfo.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import { isEmptyOrSpaces, log } from "builder-util"
import { prerelease } from "semver"
import { PlatformSpecificBuildOptions } from "./options/PlatformSpecificBuildOptions"
import { Packager } from "./packager"
import { expandMacro } from "./util/macroExpander"
import { sanitizeFileName } from "./util/filename"
// fpm bug - rpm build --description is not escaped, well... decided to replace quite to smart quote
// http://leancrew.com/all-this/2010/11/smart-quotes-in-javascript/
export function smarten(s: string): string {
// opening singles
s = s.replace(/(^|[-\u2014\s(["])'/g, "$1\u2018")
// closing singles & apostrophes
s = s.replace(/'/g, "\u2019")
// opening doubles
s = s.replace(/(^|[-\u2014/[(\u2018\s])"/g, "$1\u201c")
// closing doubles
s = s.replace(/"/g, "\u201d")
return s
}
export class AppInfo {
readonly description = smarten(this.info.metadata.description || "")
readonly version: string
readonly type: string | undefined
readonly shortVersion: string | undefined
readonly shortVersionWindows: string | undefined
readonly buildNumber: string | undefined
readonly buildVersion: string
readonly productName: string
readonly sanitizedProductName: string
readonly productFilename: string
constructor(
private readonly info: Packager,
buildVersion: string | null | undefined,
private readonly platformSpecificOptions: PlatformSpecificBuildOptions | null = null,
normalizeNfd = false
) {
this.version = info.metadata.version!
this.type = info.metadata.type
if (buildVersion == null) {
buildVersion = info.config.buildVersion
}
const buildNumberEnvs =
process.env.BUILD_NUMBER ||
process.env.TRAVIS_BUILD_NUMBER ||
process.env.APPVEYOR_BUILD_NUMBER ||
process.env.CIRCLE_BUILD_NUM ||
process.env.BUILD_BUILDNUMBER ||
process.env.CI_PIPELINE_IID
this.buildNumber = info.config.buildNumber || buildNumberEnvs
if (buildVersion == null) {
buildVersion = this.version
if (!isEmptyOrSpaces(this.buildNumber)) {
buildVersion += `.${this.buildNumber}`
}
}
this.buildVersion = buildVersion
if (info.metadata.shortVersion) {
this.shortVersion = info.metadata.shortVersion
}
if (info.metadata.shortVersionWindows) {
this.shortVersionWindows = info.metadata.shortVersionWindows
}
this.productName = info.config.productName || info.metadata.productName || info.metadata.name!
this.sanitizedProductName = sanitizeFileName(this.productName, normalizeNfd)
const executableName = platformSpecificOptions?.executableName ?? info.config.executableName
this.productFilename = executableName != null ? sanitizeFileName(executableName, normalizeNfd) : this.sanitizedProductName
}
get channel(): string | null {
const prereleaseInfo = prerelease(this.version)
if (prereleaseInfo != null && prereleaseInfo.length > 0) {
return prereleaseInfo[0] as string | null
}
return null
}
getVersionInWeirdWindowsForm(isSetBuildNumber = true): string {
const [major, maybe_minor, maybe_patch] = this.version.split(".").map(versionPart => parseInt(versionPart))
// The major component must be present. Here it can be either NaN or undefined, which
// both returns true from isNaN.
if (isNaN(major)) {
throw new Error(`Invalid major number in: ${this.version}`)
}
// Allow missing version parts. Minor and patch can be left out and default to zero
const minor = maybe_minor ?? 0
const patch = maybe_patch ?? 0
// ... but reject non-integer version parts. '1.a' is not going to fly
if (isNaN(minor) || isNaN(patch)) {
throw new Error(`Invalid minor or patch number in: ${this.version}`)
}
// https://github.com/electron-userland/electron-builder/issues/2635#issuecomment-371792272
let buildNumber = isSetBuildNumber ? this.buildNumber : null
if (buildNumber == null || !/^\d+$/.test(buildNumber)) {
buildNumber = "0"
}
return `${major}.${minor}.${patch}.${buildNumber}`
}
private get notNullDevMetadata() {
return this.info.devMetadata || {}
}
get companyName(): string | null {
const author = this.info.metadata.author || this.notNullDevMetadata.author
return author == null ? null : author.name
}
get id(): string {
let appId: string | null | undefined = null
for (const options of [this.platformSpecificOptions, this.info.config]) {
if (options != null && appId == null) {
appId = options.appId
}
}
const generateDefaultAppId = () => {
const info = this.info
return `${info.framework.defaultAppIdPrefix}${info.metadata.name!.toLowerCase()}`
}
if (appId != null && (appId === "your.id" || isEmptyOrSpaces(appId))) {
const incorrectAppId = appId
appId = generateDefaultAppId()
log.warn(`do not use "${incorrectAppId}" as appId, "${appId}" will be used instead`)
}
return appId == null ? generateDefaultAppId() : appId
}
get macBundleIdentifier(): string {
return filterCFBundleIdentifier(this.id)
}
get name(): string {
return this.info.metadata.name!
}
get linuxPackageName(): string {
const name = this.name
// https://github.com/electron-userland/electron-builder/issues/2963
return name.startsWith("@") ? this.sanitizedProductName : name
}
get sanitizedName(): string {
return sanitizeFileName(this.name)
}
get updaterCacheDirName(): string {
return this.sanitizedName.toLowerCase() + "-updater"
}
get copyright(): string {
const copyright = this.info.config.copyright
if (copyright != null) {
return expandMacro(copyright, null, this)
}
return `Copyright © ${new Date().getFullYear()} ${this.companyName || this.productName}`
}
async computePackageUrl(): Promise<string | null> {
const url = this.info.metadata.homepage || this.notNullDevMetadata.homepage
if (url != null) {
return url
}
const info = await this.info.repositoryInfo
return info == null || info.type !== "github" ? null : `https://${info.domain}/${info.user}/${info.project}`
}
}
/** @internal */
export function filterCFBundleIdentifier(identifier: string) {
// Remove special characters and allow only alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.)
// Apple documentation: https://developer.apple.com/library/mac/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-102070
return identifier.replace(/ /g, "-").replace(/[^a-zA-Z0-9.-]/g, "")
}