Skip to content

Commit c8511a7

Browse files
committed
package: fix macOS usage dropping
Electron-builder v26.0.10 changed their plist implementation, and it is no longer possible to remove Info.plist keys by setting the value to null. Manually replicate the functionality using an afterPack hook to edit the plist file instead. Signed-off-by: Mark Yen <mark.yen@suse.com>
1 parent 5562de7 commit c8511a7

File tree

4 files changed

+44
-11
lines changed

4 files changed

+44
-11
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@
163163
"node-gyp-build": "4.8.4",
164164
"node-loader": "^2.1.0",
165165
"octokit": "4.1.3",
166+
"plist": "3.1.0",
166167
"ps-tree": "1.2.0",
167168
"raw-loader": "4.0.2",
168169
"sass": "1.86.3",

packaging/electron-builder.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,6 @@ mac:
2424
darkModeSupport: true
2525
hardenedRuntime: true
2626
gatekeeperAssess: false
27-
extendInfo:
28-
NSBluetoothAlwaysUsageDescription: ~
29-
NSBluetoothPeripheralUsageDescription: ~
30-
NSCameraUsageDescription: ~
31-
NSMicrophoneUsageDescription: ~
3227
icon: ./resources/icons/mac-icon.png
3328
target: [ dmg, zip ]
3429
identity: ~ # We sign in a separate step

scripts/lib/sign-macos.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,17 @@ async function isBundleExecutable(fullPath: string): Promise<boolean> {
259259
const infoPlist = path.sep + path.join(...parts.slice(2).reverse(), 'Info.plist');
260260

261261
try {
262-
const { stdout } = await spawnFile('/usr/bin/plutil',
263-
['-extract', 'CFBundleExecutable', 'raw', '-expect', 'string', infoPlist],
264-
{ stdio: 'pipe' });
262+
const executableKey = 'CFBundleExecutable';
263+
const plistContents = await fs.promises.readFile(infoPlist, 'utf-8');
264+
const value = plist.parse(plistContents);
265265

266-
return stdout.trimEnd() === parts[0];
267-
} catch {
268-
log.info({ infoPlist }, 'Failed to read Info.plist, assuming not the bundle executable.');
266+
if (typeof value !== 'object' || !(executableKey in value)) {
267+
return false;
268+
}
269+
270+
return value[executableKey] === parts[0];
271+
} catch (ex) {
272+
log.info({ ex, infoPlist }, 'Failed to read Info.plist, assuming not the bundle executable.');
269273

270274
return false;
271275
}

scripts/package.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
AfterPackContext, Arch, build, CliOptions, Configuration, LinuxTargetSpecificOptions,
1616
} from 'electron-builder';
1717
import _ from 'lodash';
18+
import plist from 'plist';
1819
import yaml from 'yaml';
1920

2021
import buildUtils from './lib/build-utils';
@@ -98,9 +99,41 @@ class Builder {
9899
await helper.writeDesktopEntry(options, context.packager.executableName, destination);
99100
}
100101

102+
/**
103+
* Edit the application's `Info.plist` file to remove the UsageDescription
104+
* keys; there is no reason for the application to get any of those permissions.
105+
*/
106+
protected async removeMacUsageDescriptions(context: AfterPackContext) {
107+
const { MacPackager } = await import('app-builder-lib/out/macPackager');
108+
const { packager } = context;
109+
const config = packager.config.mac;
110+
111+
if (!(packager instanceof MacPackager) || !config) {
112+
return;
113+
}
114+
115+
const { productFilename } = packager.appInfo;
116+
const plistPath = path.join(context.appOutDir, `${ productFilename }.app`, 'Contents', 'Info.plist');
117+
const plistContents = await fs.promises.readFile(plistPath, 'utf-8');
118+
const plistData = plist.parse(plistContents);
119+
120+
if (typeof plistData !== 'object' || !('CFBundleName' in plistData)) {
121+
return;
122+
}
123+
const plistCopy: Record<string, plist.PlistValue> = structuredClone(plistData);
124+
125+
for (const key in plistData) {
126+
if (/^NS.*UsageDescription$/.test(key)) {
127+
delete plistCopy[key];
128+
}
129+
}
130+
await fs.promises.writeFile(plistPath, plist.build(plistCopy), 'utf-8');
131+
}
132+
101133
protected async afterPack(context: AfterPackContext) {
102134
await this.flipFuses(context);
103135
await this.writeLinuxDesktopFile(context);
136+
await this.removeMacUsageDescriptions(context);
104137
}
105138

106139
async package(): Promise<CliOptions> {

0 commit comments

Comments
 (0)