Skip to content

Commit 3ce33ed

Browse files
beyondkmpmmaietta
andauthored
chore(plist): replace the plist functionality in app-builder-bin with plist (#8890)
Co-authored-by: Mike Maietta <[email protected]>
1 parent ea86783 commit 3ce33ed

File tree

8 files changed

+125
-70
lines changed

8 files changed

+125
-70
lines changed

.changeset/chilly-starfishes-lie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"app-builder-lib": patch
3+
---
4+
5+
chore: replace the plist functionality in app-builder-bin with plist

packages/app-builder-lib/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@
7777
"semver": "^7.3.8",
7878
"tar": "^6.1.12",
7979
"temp-file": "^3.4.0",
80-
"tiny-async-pool": "1.3.0"
80+
"tiny-async-pool": "1.3.0",
81+
"plist":"3.1.0"
8182
},
8283
"///": "babel in devDependencies for proton tests",
8384
"devDependencies": {
@@ -108,6 +109,7 @@
108109
"@types/semver": "7.3.8",
109110
"@types/tar": "^6.1.3",
110111
"@types/tiny-async-pool": "^1",
112+
"@types/plist": "3.0.5",
111113
"dmg-builder": "workspace:*",
112114
"electron-builder-squirrel-windows": "workspace:*",
113115
"toml": "^3.0.0"

packages/app-builder-lib/src/electron/electronMac.ts

Lines changed: 59 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { asArray, copyOrLinkFile, getPlatformIconFileName, InvalidConfigurationError, log, unlinkIfExists } from "builder-util"
22
import { rename, utimes } from "fs/promises"
33
import * as path from "path"
4+
import * as fs from "fs"
45
import { filterCFBundleIdentifier } from "../appInfo"
56
import { AsarIntegrity } from "../asar/integrity"
67
import { MacPackager } from "../macPackager"
78
import { normalizeExt } from "../platformPackager"
8-
import { executeAppBuilderAndWriteJson, executeAppBuilderAsJson } from "../util/appBuilder"
9+
import { savePlistFile, parsePlistFile, PlistObject, PlistValue } from "../util/plist"
910
import { createBrandingOpts } from "./ElectronFramework"
1011

1112
function doRename(basePath: string, oldName: string, newName: string) {
@@ -22,11 +23,11 @@ function moveHelpers(helperSuffixes: Array<string>, frameworksPath: string, appN
2223
}
2324

2425
function getAvailableHelperSuffixes(
25-
helperEHPlist: string | null,
26-
helperNPPlist: string | null,
27-
helperRendererPlist: string | null,
28-
helperPluginPlist: string | null,
29-
helperGPUPlist: string | null
26+
helperEHPlist: PlistObject | null,
27+
helperNPPlist: PlistObject | null,
28+
helperRendererPlist: PlistObject | null,
29+
helperPluginPlist: PlistObject | null,
30+
helperGPUPlist: PlistObject | null
3031
) {
3132
const result = [" Helper"]
3233
if (helperEHPlist != null) {
@@ -69,38 +70,26 @@ export async function createMacApp(packager: MacPackager, appOutDir: string, asa
6970
const helperGPUPlistFilename = path.join(frameworksPath, `${electronBranding.productName} Helper (GPU).app`, "Contents", "Info.plist")
7071
const helperLoginPlistFilename = path.join(loginItemPath, `${electronBranding.productName} Login Helper.app`, "Contents", "Info.plist")
7172

72-
const plistContent: Array<any> = await executeAppBuilderAsJson([
73-
"decode-plist",
74-
"-f",
75-
appPlistFilename,
76-
"-f",
77-
helperPlistFilename,
78-
"-f",
79-
helperEHPlistFilename,
80-
"-f",
81-
helperNPPlistFilename,
82-
"-f",
83-
helperRendererPlistFilename,
84-
"-f",
85-
helperPluginPlistFilename,
86-
"-f",
87-
helperGPUPlistFilename,
88-
"-f",
89-
helperLoginPlistFilename,
90-
])
73+
const safeParsePlistFile = async (filePath: string): Promise<PlistObject | null> => {
74+
if (!fs.existsSync(filePath)) {
75+
return null
76+
}
77+
return await parsePlistFile(filePath)
78+
}
9179

92-
if (plistContent[0] == null) {
80+
const appPlist = (await safeParsePlistFile(appPlistFilename))!
81+
if (appPlist == null) {
9382
throw new Error("corrupted Electron dist")
9483
}
9584

96-
const appPlist = plistContent[0]!
97-
const helperPlist = plistContent[1]!
98-
const helperEHPlist = plistContent[2]
99-
const helperNPPlist = plistContent[3]
100-
const helperRendererPlist = plistContent[4]
101-
const helperPluginPlist = plistContent[5]
102-
const helperGPUPlist = plistContent[6]
103-
const helperLoginPlist = plistContent[7]
85+
// Replace the multiple parsePlistFile calls with:
86+
const helperPlist = await safeParsePlistFile(helperPlistFilename)
87+
const helperEHPlist = await safeParsePlistFile(helperEHPlistFilename)
88+
const helperNPPlist = await safeParsePlistFile(helperNPPlistFilename)
89+
const helperRendererPlist = await safeParsePlistFile(helperRendererPlistFilename)
90+
const helperPluginPlist = await safeParsePlistFile(helperPluginPlistFilename)
91+
const helperGPUPlist = await safeParsePlistFile(helperGPUPlistFilename)
92+
const helperLoginPlist = await safeParsePlistFile(helperLoginPlistFilename)
10493

10594
const buildMetadata = packager.config
10695

@@ -133,10 +122,12 @@ export async function createMacApp(packager: MacPackager, appOutDir: string, asa
133122
configureLocalhostAts(appPlist)
134123
}
135124

136-
helperPlist.CFBundleExecutable = `${appFilename} Helper`
137-
helperPlist.CFBundleDisplayName = `${appInfo.productName} Helper`
138-
helperPlist.CFBundleIdentifier = helperBundleIdentifier
139-
helperPlist.CFBundleVersion = appPlist.CFBundleVersion
125+
if (helperPlist != null) {
126+
helperPlist.CFBundleExecutable = `${appFilename} Helper`
127+
helperPlist.CFBundleDisplayName = `${appInfo.productName} Helper`
128+
helperPlist.CFBundleIdentifier = helperBundleIdentifier
129+
helperPlist.CFBundleVersion = appPlist.CFBundleVersion
130+
}
140131

141132
/**
142133
* Configure bundleIdentifier for Electron 5+ Helper processes
@@ -222,39 +213,55 @@ export async function createMacApp(packager: MacPackager, appOutDir: string, asa
222213
)
223214

224215
// `CFBundleDocumentTypes` may be defined in `mac.extendInfo`, so we need to merge it in that case
225-
appPlist.CFBundleDocumentTypes = [...(appPlist.CFBundleDocumentTypes || []), ...documentTypes]
216+
appPlist.CFBundleDocumentTypes = [...((appPlist.CFBundleDocumentTypes as PlistValue[]) || []), ...documentTypes]
226217
}
227218

228-
if (asarIntegrity != null) {
229-
appPlist.ElectronAsarIntegrity = asarIntegrity
219+
const toPlistObject = (asarIntegrity: AsarIntegrity): PlistObject => {
220+
const result: PlistObject = {}
221+
for (const [filePath, headerHash] of Object.entries(asarIntegrity)) {
222+
result[filePath] = {
223+
algorithm: headerHash.algorithm,
224+
hash: headerHash.hash,
225+
}
226+
}
227+
return result
230228
}
231229

232-
const plistDataToWrite: any = {
233-
[appPlistFilename]: appPlist,
234-
[helperPlistFilename]: helperPlist,
230+
if (asarIntegrity != null) {
231+
appPlist.ElectronAsarIntegrity = toPlistObject(asarIntegrity)
235232
}
233+
236234
if (helperEHPlist != null) {
237-
plistDataToWrite[helperEHPlistFilename] = helperEHPlist
235+
await savePlistFile(helperEHPlistFilename, helperEHPlist)
238236
}
237+
239238
if (helperNPPlist != null) {
240-
plistDataToWrite[helperNPPlistFilename] = helperNPPlist
239+
await savePlistFile(helperNPPlistFilename, helperNPPlist)
241240
}
241+
242242
if (helperRendererPlist != null) {
243-
plistDataToWrite[helperRendererPlistFilename] = helperRendererPlist
243+
await savePlistFile(helperRendererPlistFilename, helperRendererPlist)
244244
}
245+
245246
if (helperPluginPlist != null) {
246-
plistDataToWrite[helperPluginPlistFilename] = helperPluginPlist
247+
await savePlistFile(helperPluginPlistFilename, helperPluginPlist)
247248
}
249+
248250
if (helperGPUPlist != null) {
249-
plistDataToWrite[helperGPUPlistFilename] = helperGPUPlist
251+
await savePlistFile(helperGPUPlistFilename, helperGPUPlist)
250252
}
253+
251254
if (helperLoginPlist != null) {
252-
plistDataToWrite[helperLoginPlistFilename] = helperLoginPlist
255+
await savePlistFile(helperLoginPlistFilename, helperLoginPlist)
256+
}
257+
258+
await savePlistFile(appPlistFilename, appPlist)
259+
if (helperPlist != null) {
260+
await savePlistFile(helperPlistFilename, helperPlist)
253261
}
254262

255263
await Promise.all([
256-
executeAppBuilderAndWriteJson(["encode-plist"], plistDataToWrite),
257-
doRename(path.join(contentsPath, "MacOS"), electronBranding.productName, appPlist.CFBundleExecutable),
264+
doRename(path.join(contentsPath, "MacOS"), electronBranding.productName, appPlist.CFBundleExecutable as string),
258265
unlinkIfExists(path.join(appOutDir, "LICENSE")),
259266
unlinkIfExists(path.join(appOutDir, "LICENSES.chromium.html")),
260267
])

packages/app-builder-lib/src/frameworks/LibUiFramework.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Platform } from "../core"
77
import { Framework, PrepareApplicationStageDirectoryOptions } from "../Framework"
88
import { LinuxPackager } from "../linuxPackager"
99
import { MacPackager } from "../macPackager"
10-
import { executeAppBuilderAndWriteJson } from "../util/appBuilder"
10+
import { savePlistFile } from "../util/plist"
1111

1212
export class LibUiFramework implements Framework {
1313
readonly name: string = "libui"
@@ -71,16 +71,14 @@ export class LibUiFramework implements Framework {
7171
NSHighResolutionCapable: true,
7272
}
7373
await packager.applyCommonInfo(appPlist, appContentsDir)
74-
await Promise.all([
75-
executeAppBuilderAndWriteJson(["encode-plist"], { [path.join(appContentsDir, "Info.plist")]: appPlist }),
76-
writeExecutableMain(
77-
path.join(appContentsDir, "MacOS", appPlist.CFBundleExecutable),
78-
`#!/bin/sh
74+
await savePlistFile(path.join(appContentsDir, "Info.plist"), appPlist)
75+
await writeExecutableMain(
76+
path.join(appContentsDir, "MacOS", appPlist.CFBundleExecutable),
77+
`#!/bin/sh
7978
DIR=$(dirname "$0")
8079
"$DIR/node" "$DIR/../Resources/app/${options.packager.info.metadata.main || "index.js"}"
8180
`
82-
),
83-
])
81+
)
8482
}
8583

8684
private async prepareLinuxApplicationStageDirectory(options: PrepareApplicationStageDirectoryOptions) {

packages/app-builder-lib/src/targets/pkg.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { findIdentity, Identity } from "../codeSign/macCodeSign"
88
import { Target } from "../core"
99
import { MacPackager } from "../macPackager"
1010
import { PkgOptions } from "../options/pkgOptions"
11-
import { executeAppBuilderAndWriteJson, executeAppBuilderAsJson } from "../util/appBuilder"
11+
import { savePlistFile, parsePlistFile, PlistObject } from "../util/plist"
1212
import { getNotLocalizedLicenseFile } from "../util/license"
1313

1414
const certType = "Developer ID Installer"
@@ -182,9 +182,7 @@ export class PkgTarget extends Target {
182182
await exec("pkgbuild", ["--analyze", "--root", rootPath, propertyListOutputFile])
183183

184184
// process the template plist
185-
const plistInfo = (await executeAppBuilderAsJson<Array<any>>(["decode-plist", "-f", propertyListOutputFile]))[0].filter(
186-
(it: any) => it.RootRelativeBundlePath !== "Electron.dSYM"
187-
)
185+
const plistInfo = (await parsePlistFile<PlistObject[]>(propertyListOutputFile)).filter((it: PlistObject) => it.RootRelativeBundlePath !== "Electron.dSYM")
188186
let packageInfo: any = {}
189187
if (plistInfo.length > 0) {
190188
packageInfo = plistInfo[0]
@@ -237,7 +235,7 @@ export class PkgTarget extends Target {
237235
args.push("--scripts", scriptsDir)
238236
}
239237
if (plistInfo.length > 0) {
240-
await executeAppBuilderAndWriteJson(["encode-plist"], { [propertyListOutputFile]: plistInfo })
238+
await savePlistFile(propertyListOutputFile, plistInfo)
241239
}
242240

243241
args.push(packageOutputFile)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { build, parse } from "plist"
2+
import * as fs from "fs/promises"
3+
4+
type PlistValue = string | number | boolean | Date | PlistObject | PlistValue[]
5+
6+
interface PlistObject {
7+
[key: string]: PlistValue
8+
}
9+
10+
function sortObjectKeys(obj: PlistValue): PlistValue {
11+
if (obj === null || typeof obj !== "object") {
12+
return obj
13+
}
14+
15+
if (Array.isArray(obj)) {
16+
return obj.map(sortObjectKeys)
17+
}
18+
19+
const result: PlistObject = {}
20+
Object.keys(obj)
21+
.sort()
22+
.forEach(key => {
23+
result[key] = sortObjectKeys((obj as PlistObject)[key])
24+
})
25+
return result
26+
}
27+
28+
export async function savePlistFile(path: string, data: PlistValue): Promise<void> {
29+
const sortedData = sortObjectKeys(data)
30+
const plist = build(sortedData)
31+
await fs.writeFile(path, plist)
32+
}
33+
34+
export async function parsePlistFile<T>(file: string): Promise<T> {
35+
const data = await fs.readFile(file, "utf8")
36+
return parse(data) as T
37+
}
38+
39+
export type { PlistValue, PlistObject }

pnpm-lock.yaml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/src/helpers/packTester.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { PublishManager } from "app-builder-lib"
22
import { readAsar } from "app-builder-lib/out/asar/asar"
33
import { computeArchToTargetNamesMap } from "app-builder-lib/out/targets/targetFactory"
44
import { getLinuxToolsPath } from "app-builder-lib/out/targets/tools"
5-
import { executeAppBuilderAsJson } from "app-builder-lib/out/util/appBuilder"
5+
import { parsePlistFile, PlistObject } from "app-builder-lib/out/util/plist"
66
import { AsarIntegrity } from "app-builder-lib/src/asar/integrity"
77
import { addValue, copyDir, deepAssign, exec, executeFinally, exists, FileCopier, getPath7x, getPath7za, log, spawn, USE_HARD_LINKS, walk } from "builder-util"
88
import { CancellationToken, UpdateFileInfo } from "builder-util-runtime"
@@ -367,7 +367,7 @@ function parseDebControl(info: string): any {
367367
async function checkMacResult(packager: Packager, packagerOptions: PackagerOptions, checkOptions: AssertPackOptions, packedAppDir: string) {
368368
const appInfo = packager.appInfo
369369
const plistPath = path.join(packedAppDir, "Contents", "Info.plist")
370-
const info = (await executeAppBuilderAsJson<Array<any>>(["decode-plist", "-f", plistPath]))[0]
370+
const info = await parsePlistFile<PlistObject>(plistPath)
371371

372372
expect(info).toMatchObject({
373373
CFBundleVersion: info.CFBundleVersion === "50" ? "50" : `${appInfo.version}.${process.env.TRAVIS_BUILD_NUMBER || process.env.CIRCLE_BUILD_NUM}`,
@@ -389,7 +389,7 @@ async function checkMacResult(packager: Packager, packagerOptions: PackagerOptio
389389
delete info.NSRequiresAquaSystemAppearance
390390
delete info.NSQuitAlwaysKeepsWindows
391391
if (info.NSAppTransportSecurity != null) {
392-
delete info.NSAppTransportSecurity.NSAllowsArbitraryLoads
392+
delete (info.NSAppTransportSecurity as PlistObject).NSAllowsArbitraryLoads
393393
}
394394
// test value
395395
if (info.LSMinimumSystemVersion !== "10.12.0") {
@@ -400,7 +400,7 @@ async function checkMacResult(packager: Packager, packagerOptions: PackagerOptio
400400

401401
if (checksumData != null) {
402402
for (const name of Object.keys(checksumData)) {
403-
checksumData[name] = { algorithm: "SHA256", hash: "hash" }
403+
;(checksumData as Record<string, any>)[name] = { algorithm: "SHA256", hash: "hash" }
404404
}
405405
snapshot.ElectronAsarIntegrity = checksumData
406406
}

0 commit comments

Comments
 (0)