craft-native 0.0.278 → 0.0.293

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -130,12 +130,15 @@ import { spawn } from "child_process";
130
130
  import { createHash } from "crypto";
131
131
  import {
132
132
  chmodSync,
133
+ closeSync,
133
134
  copyFileSync,
134
135
  cpSync,
135
136
  existsSync as existsSync4,
136
137
  lstatSync,
137
138
  mkdirSync,
139
+ openSync,
138
140
  readFileSync as readFileSync2,
141
+ readSync,
139
142
  readdirSync as readdirSync2,
140
143
  rmSync as rmSync2,
141
144
  writeFileSync as writeFileSync3
@@ -243,6 +246,8 @@ async function packageMacOS(config, outDir) {
243
246
  const { name, version, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
244
247
  const opts = config.macos || {};
245
248
  const hardenedRuntime = !opts.appStore;
249
+ if (opts.appStore && (!opts.entitlements || !opts.provisioningProfile))
250
+ return [{ success: false, platform: "macos", format: "app", error: "App Store packaging requires macos.entitlements and macos.provisioningProfile" }];
246
251
  const appBundlePath = join2(outDir, `${name}.app`);
247
252
  const appBundle = opts.appBundlePath ? copyMacOSAppBundle(opts.appBundlePath, appBundlePath) : createMacOSAppBundle({
248
253
  name,
@@ -268,25 +273,42 @@ async function packageMacOS(config, outDir) {
268
273
  });
269
274
  return results;
270
275
  }
271
- if (opts.signIdentity) {
272
- const signed = await runTool("codesign", codesignArguments({
273
- path: appBundlePath,
274
- identity: opts.signIdentity,
275
- entitlements: opts.entitlements,
276
- hardenedRuntime
277
- }));
278
- if (!signed.success) {
279
- results.push({ success: false, platform: "macos", format: "app", error: signed.error });
280
- return results;
276
+ try {
277
+ const contents = join2(appBundlePath, "Contents");
278
+ if (opts.provisioningProfile) {
279
+ const profile = join2(contents, "embedded.provisionprofile");
280
+ copyFileSync(opts.provisioningProfile, profile);
281
+ chmodSync(profile, 420);
282
+ }
283
+ if (opts.privacyManifest)
284
+ copyFileSync(opts.privacyManifest, join2(contents, "Resources", "PrivacyInfo.xcprivacy"));
285
+ if (opts.appBundlePath) {
286
+ const metadata = {
287
+ CFBundleVersion: opts.buildNumber,
288
+ LSApplicationCategoryType: opts.category,
289
+ LSMinimumSystemVersion: opts.minimumSystemVersion,
290
+ LSUIElement: opts.menuBarOnly
291
+ };
292
+ if (config.iconPath) {
293
+ const icon = basename(config.iconPath);
294
+ copyFileSync(config.iconPath, join2(contents, "Resources", icon));
295
+ metadata.CFBundleIconFile = icon;
296
+ }
297
+ for (const [key, value] of Object.entries(metadata)) {
298
+ if (value === undefined)
299
+ continue;
300
+ const result = await runTool("plutil", ["-replace", key, typeof value === "boolean" ? "-bool" : "-string", String(value), join2(contents, "Info.plist")]);
301
+ if (!result.success)
302
+ throw new Error(result.error);
303
+ }
281
304
  }
282
- } else if (opts.appStore) {
283
- results.push({
284
- success: false,
285
- platform: "macos",
286
- format: "pkg",
287
- error: 'App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)'
288
- });
289
- return results;
305
+ if (opts.signIdentity) {
306
+ await signMacOSAppBundle(appBundlePath, bundleId, opts, hardenedRuntime);
307
+ } else if (opts.appStore) {
308
+ throw new Error('App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)');
309
+ }
310
+ } catch (error) {
311
+ return [{ success: false, platform: "macos", format: "app", error: error.message }];
290
312
  }
291
313
  results.push({ success: true, platform: "macos", format: "app", outputPath: appBundlePath });
292
314
  if (opts.dmg !== false) {
@@ -310,7 +332,8 @@ async function packageMacOS(config, outDir) {
310
332
  identifier: bundleId,
311
333
  version,
312
334
  appStore: opts.appStore === true,
313
- installerIdentity: opts.installerIdentity
335
+ installerIdentity: opts.installerIdentity,
336
+ signingKeychain: opts.signingKeychain
314
337
  });
315
338
  results.push({
316
339
  success: pkgResult.success,
@@ -550,6 +573,59 @@ ${body}
550
573
  </dict>
551
574
  </plist>`;
552
575
  }
576
+ async function signMacOSAppBundle(app, bundleId, opts, hardenedRuntime) {
577
+ const scratch = createTempDir("craft-sign-");
578
+ try {
579
+ let helperEntitlements = opts.helperEntitlements;
580
+ if (opts.appStore && !helperEntitlements) {
581
+ helperEntitlements = join2(scratch, "inherit.plist");
582
+ writeFileSync3(helperEntitlements, '<?xml version="1.0"?><plist version="1.0"><dict><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.inherit</key><true/></dict></plist>');
583
+ }
584
+ const extracted = await runTool("plutil", ["-extract", "CFBundleExecutable", "raw", "-o", "-", join2(app, "Contents", "Info.plist")]);
585
+ if (!extracted.success)
586
+ throw new Error(extracted.error);
587
+ const main = join2(app, "Contents", "MacOS", extracted.stdout.trim());
588
+ const sign = async (path, entitlements, identifier) => {
589
+ const signed = await runTool("codesign", codesignArguments({ path, identity: opts.signIdentity, entitlements, hardenedRuntime, identifier, keychain: opts.signingKeychain, deep: false }));
590
+ if (!signed.success)
591
+ throw new Error(signed.error);
592
+ };
593
+ const walk = async (directory) => {
594
+ for (const entry of readdirSync2(directory)) {
595
+ const path = join2(directory, entry);
596
+ const stat = lstatSync(path);
597
+ if (stat.isSymbolicLink())
598
+ continue;
599
+ if (stat.isDirectory()) {
600
+ await walk(path);
601
+ if (/\.(?:app|framework|xpc|appex|bundle)$/.test(entry))
602
+ await sign(path, /\.(?:app|xpc|appex)$/.test(entry) ? helperEntitlements : undefined);
603
+ } else if (path !== main && stat.isFile() && isMachO(path)) {
604
+ const executable = (stat.mode & 73) !== 0 && !/\.(?:dylib|so)$/.test(entry);
605
+ await sign(path, executable ? helperEntitlements : undefined, `${bundleId}.${entry}`);
606
+ }
607
+ }
608
+ };
609
+ await walk(join2(app, "Contents"));
610
+ await sign(app, opts.entitlements);
611
+ const verified = await runTool("codesign", ["--verify", "--deep", "--strict", app]);
612
+ if (!verified.success)
613
+ throw new Error(verified.error);
614
+ } finally {
615
+ removeTempDir(scratch);
616
+ }
617
+ }
618
+ function isMachO(path) {
619
+ const fd = openSync(path, "r");
620
+ try {
621
+ const magic = Buffer.alloc(4);
622
+ if (readSync(fd, magic, 0, 4, 0) !== 4)
623
+ return false;
624
+ return [4277009102, 3472551422, 4277009103, 3489328638, 3405691582, 3199925962, 3405691583, 3216703178].includes(magic.readUInt32BE());
625
+ } finally {
626
+ closeSync(fd);
627
+ }
628
+ }
553
629
  function codesignArguments(opts) {
554
630
  return [
555
631
  "--force",
@@ -558,7 +634,9 @@ function codesignArguments(opts) {
558
634
  ...opts.identity === "-" ? [] : ["--timestamp"],
559
635
  ...opts.hardenedRuntime ? ["--options", "runtime"] : [],
560
636
  ...opts.entitlements ? ["--entitlements", opts.entitlements] : [],
561
- "--deep",
637
+ ...opts.identifier ? ["--identifier", opts.identifier] : [],
638
+ ...opts.keychain ? ["--keychain", opts.keychain] : [],
639
+ ...opts.deep === false ? [] : ["--deep"],
562
640
  opts.path
563
641
  ];
564
642
  }
@@ -569,6 +647,7 @@ function productbuildArguments(opts) {
569
647
  "/Applications",
570
648
  "--sign",
571
649
  opts.identity,
650
+ ...opts.keychain ? ["--keychain", opts.keychain] : [],
572
651
  opts.outputPath
573
652
  ];
574
653
  }
@@ -686,7 +765,7 @@ function runTool(tool, args) {
686
765
  });
687
766
  proc.on("close", (code) => {
688
767
  if (code === 0)
689
- resolve({ success: true });
768
+ resolve({ success: true, stdout });
690
769
  else
691
770
  resolve({ success: false, error: formatPackagingCommandError(tool, code, stdout, stderr) });
692
771
  });
@@ -760,7 +839,8 @@ async function createPKG(opts) {
760
839
  const built = await runTool("productbuild", productbuildArguments({
761
840
  appBundlePath: opts.appBundlePath,
762
841
  outputPath: opts.outputPath,
763
- identity: opts.installerIdentity
842
+ identity: opts.installerIdentity,
843
+ keychain: opts.signingKeychain
764
844
  }));
765
845
  return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
766
846
  }
@@ -2865,7 +2945,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
2865
2945
  `);
2866
2946
  }
2867
2947
  // package.json
2868
- var version = "0.0.278";
2948
+ var version = "0.0.293";
2869
2949
 
2870
2950
  // bin/cli.ts
2871
2951
  var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
package/dist/index.cjs CHANGED
@@ -374,7 +374,7 @@ function craftBinaryNotFoundMessage(triedPath) {
374
374
  `);
375
375
  }
376
376
  // package.json
377
- var version = "0.0.278";
377
+ var version = "0.0.293";
378
378
 
379
379
  // src/sdk-version.ts
380
380
  var SDK_VERSION = version;
@@ -611,6 +611,8 @@ async function packageMacOS(config, outDir) {
611
611
  const { name, version, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
612
612
  const opts = config.macos || {};
613
613
  const hardenedRuntime = !opts.appStore;
614
+ if (opts.appStore && (!opts.entitlements || !opts.provisioningProfile))
615
+ return [{ success: false, platform: "macos", format: "app", error: "App Store packaging requires macos.entitlements and macos.provisioningProfile" }];
614
616
  const appBundlePath = import_path.join(outDir, `${name}.app`);
615
617
  const appBundle = opts.appBundlePath ? copyMacOSAppBundle(opts.appBundlePath, appBundlePath) : createMacOSAppBundle({
616
618
  name,
@@ -636,25 +638,42 @@ async function packageMacOS(config, outDir) {
636
638
  });
637
639
  return results;
638
640
  }
639
- if (opts.signIdentity) {
640
- const signed = await runTool("codesign", codesignArguments({
641
- path: appBundlePath,
642
- identity: opts.signIdentity,
643
- entitlements: opts.entitlements,
644
- hardenedRuntime
645
- }));
646
- if (!signed.success) {
647
- results.push({ success: false, platform: "macos", format: "app", error: signed.error });
648
- return results;
641
+ try {
642
+ const contents = import_path.join(appBundlePath, "Contents");
643
+ if (opts.provisioningProfile) {
644
+ const profile = import_path.join(contents, "embedded.provisionprofile");
645
+ import_fs.copyFileSync(opts.provisioningProfile, profile);
646
+ import_fs.chmodSync(profile, 420);
647
+ }
648
+ if (opts.privacyManifest)
649
+ import_fs.copyFileSync(opts.privacyManifest, import_path.join(contents, "Resources", "PrivacyInfo.xcprivacy"));
650
+ if (opts.appBundlePath) {
651
+ const metadata = {
652
+ CFBundleVersion: opts.buildNumber,
653
+ LSApplicationCategoryType: opts.category,
654
+ LSMinimumSystemVersion: opts.minimumSystemVersion,
655
+ LSUIElement: opts.menuBarOnly
656
+ };
657
+ if (config.iconPath) {
658
+ const icon = import_path.basename(config.iconPath);
659
+ import_fs.copyFileSync(config.iconPath, import_path.join(contents, "Resources", icon));
660
+ metadata.CFBundleIconFile = icon;
661
+ }
662
+ for (const [key, value] of Object.entries(metadata)) {
663
+ if (value === undefined)
664
+ continue;
665
+ const result = await runTool("plutil", ["-replace", key, typeof value === "boolean" ? "-bool" : "-string", String(value), import_path.join(contents, "Info.plist")]);
666
+ if (!result.success)
667
+ throw new Error(result.error);
668
+ }
649
669
  }
650
- } else if (opts.appStore) {
651
- results.push({
652
- success: false,
653
- platform: "macos",
654
- format: "pkg",
655
- error: 'App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)'
656
- });
657
- return results;
670
+ if (opts.signIdentity) {
671
+ await signMacOSAppBundle(appBundlePath, bundleId, opts, hardenedRuntime);
672
+ } else if (opts.appStore) {
673
+ throw new Error('App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)');
674
+ }
675
+ } catch (error) {
676
+ return [{ success: false, platform: "macos", format: "app", error: error.message }];
658
677
  }
659
678
  results.push({ success: true, platform: "macos", format: "app", outputPath: appBundlePath });
660
679
  if (opts.dmg !== false) {
@@ -678,7 +697,8 @@ async function packageMacOS(config, outDir) {
678
697
  identifier: bundleId,
679
698
  version,
680
699
  appStore: opts.appStore === true,
681
- installerIdentity: opts.installerIdentity
700
+ installerIdentity: opts.installerIdentity,
701
+ signingKeychain: opts.signingKeychain
682
702
  });
683
703
  results.push({
684
704
  success: pkgResult.success,
@@ -919,6 +939,59 @@ ${body}
919
939
  </dict>
920
940
  </plist>`;
921
941
  }
942
+ async function signMacOSAppBundle(app, bundleId, opts, hardenedRuntime) {
943
+ const scratch = createTempDir("craft-sign-");
944
+ try {
945
+ let helperEntitlements = opts.helperEntitlements;
946
+ if (opts.appStore && !helperEntitlements) {
947
+ helperEntitlements = import_path.join(scratch, "inherit.plist");
948
+ import_fs.writeFileSync(helperEntitlements, '<?xml version="1.0"?><plist version="1.0"><dict><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.inherit</key><true/></dict></plist>');
949
+ }
950
+ const extracted = await runTool("plutil", ["-extract", "CFBundleExecutable", "raw", "-o", "-", import_path.join(app, "Contents", "Info.plist")]);
951
+ if (!extracted.success)
952
+ throw new Error(extracted.error);
953
+ const main = import_path.join(app, "Contents", "MacOS", extracted.stdout.trim());
954
+ const sign = async (path, entitlements, identifier) => {
955
+ const signed = await runTool("codesign", codesignArguments({ path, identity: opts.signIdentity, entitlements, hardenedRuntime, identifier, keychain: opts.signingKeychain, deep: false }));
956
+ if (!signed.success)
957
+ throw new Error(signed.error);
958
+ };
959
+ const walk = async (directory) => {
960
+ for (const entry of import_fs.readdirSync(directory)) {
961
+ const path = import_path.join(directory, entry);
962
+ const stat = import_fs.lstatSync(path);
963
+ if (stat.isSymbolicLink())
964
+ continue;
965
+ if (stat.isDirectory()) {
966
+ await walk(path);
967
+ if (/\.(?:app|framework|xpc|appex|bundle)$/.test(entry))
968
+ await sign(path, /\.(?:app|xpc|appex)$/.test(entry) ? helperEntitlements : undefined);
969
+ } else if (path !== main && stat.isFile() && isMachO(path)) {
970
+ const executable = (stat.mode & 73) !== 0 && !/\.(?:dylib|so)$/.test(entry);
971
+ await sign(path, executable ? helperEntitlements : undefined, `${bundleId}.${entry}`);
972
+ }
973
+ }
974
+ };
975
+ await walk(import_path.join(app, "Contents"));
976
+ await sign(app, opts.entitlements);
977
+ const verified = await runTool("codesign", ["--verify", "--deep", "--strict", app]);
978
+ if (!verified.success)
979
+ throw new Error(verified.error);
980
+ } finally {
981
+ removeTempDir(scratch);
982
+ }
983
+ }
984
+ function isMachO(path) {
985
+ const fd = import_fs.openSync(path, "r");
986
+ try {
987
+ const magic = Buffer.alloc(4);
988
+ if (import_fs.readSync(fd, magic, 0, 4, 0) !== 4)
989
+ return false;
990
+ return [4277009102, 3472551422, 4277009103, 3489328638, 3405691582, 3199925962, 3405691583, 3216703178].includes(magic.readUInt32BE());
991
+ } finally {
992
+ import_fs.closeSync(fd);
993
+ }
994
+ }
922
995
  function codesignArguments(opts) {
923
996
  return [
924
997
  "--force",
@@ -927,7 +1000,9 @@ function codesignArguments(opts) {
927
1000
  ...opts.identity === "-" ? [] : ["--timestamp"],
928
1001
  ...opts.hardenedRuntime ? ["--options", "runtime"] : [],
929
1002
  ...opts.entitlements ? ["--entitlements", opts.entitlements] : [],
930
- "--deep",
1003
+ ...opts.identifier ? ["--identifier", opts.identifier] : [],
1004
+ ...opts.keychain ? ["--keychain", opts.keychain] : [],
1005
+ ...opts.deep === false ? [] : ["--deep"],
931
1006
  opts.path
932
1007
  ];
933
1008
  }
@@ -938,6 +1013,7 @@ function productbuildArguments(opts) {
938
1013
  "/Applications",
939
1014
  "--sign",
940
1015
  opts.identity,
1016
+ ...opts.keychain ? ["--keychain", opts.keychain] : [],
941
1017
  opts.outputPath
942
1018
  ];
943
1019
  }
@@ -1057,7 +1133,7 @@ function runTool(tool, args) {
1057
1133
  });
1058
1134
  proc.on("close", (code) => {
1059
1135
  if (code === 0)
1060
- resolve({ success: true });
1136
+ resolve({ success: true, stdout });
1061
1137
  else
1062
1138
  resolve({ success: false, error: formatPackagingCommandError(tool, code, stdout, stderr) });
1063
1139
  });
@@ -1132,7 +1208,8 @@ async function createPKG(opts) {
1132
1208
  const built = await runTool("productbuild", productbuildArguments({
1133
1209
  appBundlePath: opts.appBundlePath,
1134
1210
  outputPath: opts.outputPath,
1135
- identity: opts.installerIdentity
1211
+ identity: opts.installerIdentity,
1212
+ keychain: opts.signingKeychain
1136
1213
  }));
1137
1214
  return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
1138
1215
  }
package/dist/index.d.cts CHANGED
@@ -85,4 +85,4 @@ export declare function show(html: string, options?: WindowOptions): Promise<voi
85
85
  * Quick helper to load a URL
86
86
  */
87
87
  export declare function loadURL(url: string, options?: WindowOptions): Promise<void>;
88
- export type { WindowOptions, CraftSidebarAPI, CraftNativeUIAPI, CraftContextMenuItem, CraftContextMenuResult, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CraftHapticsAPI, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftFsWatchEvent, CraftFsWatchHandle, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftCapabilities, CapabilityNamespace, CapabilityNamespaceStatus, CapabilityAction, CraftPreferencesAPI, CraftPreferencesInfo, CraftSettingsAPI, PreferenceValue, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
88
+ export type { WindowOptions, CraftSidebarAPI, CraftNativeUIAPI, CraftContextMenuItem, CraftContextMenuResult, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, CraftTimersAPI, Permission, HapticType, CraftHapticsAPI, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftFsWatchEvent, CraftFsWatchHandle, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftCapabilities, CapabilityNamespace, CapabilityNamespaceStatus, CapabilityAction, CraftPreferencesAPI, CraftPreferencesInfo, CraftSettingsAPI, PreferenceValue, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
package/dist/index.d.ts CHANGED
@@ -85,4 +85,4 @@ export declare function show(html: string, options?: WindowOptions): Promise<voi
85
85
  * Quick helper to load a URL
86
86
  */
87
87
  export declare function loadURL(url: string, options?: WindowOptions): Promise<void>;
88
- export type { WindowOptions, CraftSidebarAPI, CraftNativeUIAPI, CraftContextMenuItem, CraftContextMenuResult, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CraftHapticsAPI, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftFsWatchEvent, CraftFsWatchHandle, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftCapabilities, CapabilityNamespace, CapabilityNamespaceStatus, CapabilityAction, CraftPreferencesAPI, CraftPreferencesInfo, CraftSettingsAPI, PreferenceValue, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
88
+ export type { WindowOptions, CraftSidebarAPI, CraftNativeUIAPI, CraftContextMenuItem, CraftContextMenuResult, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, CraftTimersAPI, Permission, HapticType, CraftHapticsAPI, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftFsWatchEvent, CraftFsWatchHandle, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftCapabilities, CapabilityNamespace, CapabilityNamespaceStatus, CapabilityAction, CraftPreferencesAPI, CraftPreferencesInfo, CraftSettingsAPI, PreferenceValue, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
package/dist/index.js CHANGED
@@ -117,7 +117,7 @@ function craftBinaryNotFoundMessage(triedPath) {
117
117
  `);
118
118
  }
119
119
  // package.json
120
- var version = "0.0.278";
120
+ var version = "0.0.293";
121
121
 
122
122
  // src/sdk-version.ts
123
123
  var SDK_VERSION = version;
@@ -241,12 +241,15 @@ import { spawn } from "child_process";
241
241
  import { createHash } from "crypto";
242
242
  import {
243
243
  chmodSync,
244
+ closeSync,
244
245
  copyFileSync,
245
246
  cpSync,
246
247
  existsSync as existsSync3,
247
248
  lstatSync,
248
249
  mkdirSync,
250
+ openSync,
249
251
  readFileSync,
252
+ readSync,
250
253
  readdirSync as readdirSync2,
251
254
  rmSync as rmSync2,
252
255
  writeFileSync as writeFileSync2
@@ -365,6 +368,8 @@ async function packageMacOS(config, outDir) {
365
368
  const { name, version, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
366
369
  const opts = config.macos || {};
367
370
  const hardenedRuntime = !opts.appStore;
371
+ if (opts.appStore && (!opts.entitlements || !opts.provisioningProfile))
372
+ return [{ success: false, platform: "macos", format: "app", error: "App Store packaging requires macos.entitlements and macos.provisioningProfile" }];
368
373
  const appBundlePath = join2(outDir, `${name}.app`);
369
374
  const appBundle = opts.appBundlePath ? copyMacOSAppBundle(opts.appBundlePath, appBundlePath) : createMacOSAppBundle({
370
375
  name,
@@ -390,25 +395,42 @@ async function packageMacOS(config, outDir) {
390
395
  });
391
396
  return results;
392
397
  }
393
- if (opts.signIdentity) {
394
- const signed = await runTool("codesign", codesignArguments({
395
- path: appBundlePath,
396
- identity: opts.signIdentity,
397
- entitlements: opts.entitlements,
398
- hardenedRuntime
399
- }));
400
- if (!signed.success) {
401
- results.push({ success: false, platform: "macos", format: "app", error: signed.error });
402
- return results;
398
+ try {
399
+ const contents = join2(appBundlePath, "Contents");
400
+ if (opts.provisioningProfile) {
401
+ const profile = join2(contents, "embedded.provisionprofile");
402
+ copyFileSync(opts.provisioningProfile, profile);
403
+ chmodSync(profile, 420);
404
+ }
405
+ if (opts.privacyManifest)
406
+ copyFileSync(opts.privacyManifest, join2(contents, "Resources", "PrivacyInfo.xcprivacy"));
407
+ if (opts.appBundlePath) {
408
+ const metadata = {
409
+ CFBundleVersion: opts.buildNumber,
410
+ LSApplicationCategoryType: opts.category,
411
+ LSMinimumSystemVersion: opts.minimumSystemVersion,
412
+ LSUIElement: opts.menuBarOnly
413
+ };
414
+ if (config.iconPath) {
415
+ const icon = basename(config.iconPath);
416
+ copyFileSync(config.iconPath, join2(contents, "Resources", icon));
417
+ metadata.CFBundleIconFile = icon;
418
+ }
419
+ for (const [key, value] of Object.entries(metadata)) {
420
+ if (value === undefined)
421
+ continue;
422
+ const result = await runTool("plutil", ["-replace", key, typeof value === "boolean" ? "-bool" : "-string", String(value), join2(contents, "Info.plist")]);
423
+ if (!result.success)
424
+ throw new Error(result.error);
425
+ }
403
426
  }
404
- } else if (opts.appStore) {
405
- results.push({
406
- success: false,
407
- platform: "macos",
408
- format: "pkg",
409
- error: 'App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)'
410
- });
411
- return results;
427
+ if (opts.signIdentity) {
428
+ await signMacOSAppBundle(appBundlePath, bundleId, opts, hardenedRuntime);
429
+ } else if (opts.appStore) {
430
+ throw new Error('App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)');
431
+ }
432
+ } catch (error) {
433
+ return [{ success: false, platform: "macos", format: "app", error: error.message }];
412
434
  }
413
435
  results.push({ success: true, platform: "macos", format: "app", outputPath: appBundlePath });
414
436
  if (opts.dmg !== false) {
@@ -432,7 +454,8 @@ async function packageMacOS(config, outDir) {
432
454
  identifier: bundleId,
433
455
  version,
434
456
  appStore: opts.appStore === true,
435
- installerIdentity: opts.installerIdentity
457
+ installerIdentity: opts.installerIdentity,
458
+ signingKeychain: opts.signingKeychain
436
459
  });
437
460
  results.push({
438
461
  success: pkgResult.success,
@@ -673,6 +696,59 @@ ${body}
673
696
  </dict>
674
697
  </plist>`;
675
698
  }
699
+ async function signMacOSAppBundle(app, bundleId, opts, hardenedRuntime) {
700
+ const scratch = createTempDir("craft-sign-");
701
+ try {
702
+ let helperEntitlements = opts.helperEntitlements;
703
+ if (opts.appStore && !helperEntitlements) {
704
+ helperEntitlements = join2(scratch, "inherit.plist");
705
+ writeFileSync2(helperEntitlements, '<?xml version="1.0"?><plist version="1.0"><dict><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.inherit</key><true/></dict></plist>');
706
+ }
707
+ const extracted = await runTool("plutil", ["-extract", "CFBundleExecutable", "raw", "-o", "-", join2(app, "Contents", "Info.plist")]);
708
+ if (!extracted.success)
709
+ throw new Error(extracted.error);
710
+ const main = join2(app, "Contents", "MacOS", extracted.stdout.trim());
711
+ const sign = async (path, entitlements, identifier) => {
712
+ const signed = await runTool("codesign", codesignArguments({ path, identity: opts.signIdentity, entitlements, hardenedRuntime, identifier, keychain: opts.signingKeychain, deep: false }));
713
+ if (!signed.success)
714
+ throw new Error(signed.error);
715
+ };
716
+ const walk = async (directory) => {
717
+ for (const entry of readdirSync2(directory)) {
718
+ const path = join2(directory, entry);
719
+ const stat = lstatSync(path);
720
+ if (stat.isSymbolicLink())
721
+ continue;
722
+ if (stat.isDirectory()) {
723
+ await walk(path);
724
+ if (/\.(?:app|framework|xpc|appex|bundle)$/.test(entry))
725
+ await sign(path, /\.(?:app|xpc|appex)$/.test(entry) ? helperEntitlements : undefined);
726
+ } else if (path !== main && stat.isFile() && isMachO(path)) {
727
+ const executable = (stat.mode & 73) !== 0 && !/\.(?:dylib|so)$/.test(entry);
728
+ await sign(path, executable ? helperEntitlements : undefined, `${bundleId}.${entry}`);
729
+ }
730
+ }
731
+ };
732
+ await walk(join2(app, "Contents"));
733
+ await sign(app, opts.entitlements);
734
+ const verified = await runTool("codesign", ["--verify", "--deep", "--strict", app]);
735
+ if (!verified.success)
736
+ throw new Error(verified.error);
737
+ } finally {
738
+ removeTempDir(scratch);
739
+ }
740
+ }
741
+ function isMachO(path) {
742
+ const fd = openSync(path, "r");
743
+ try {
744
+ const magic = Buffer.alloc(4);
745
+ if (readSync(fd, magic, 0, 4, 0) !== 4)
746
+ return false;
747
+ return [4277009102, 3472551422, 4277009103, 3489328638, 3405691582, 3199925962, 3405691583, 3216703178].includes(magic.readUInt32BE());
748
+ } finally {
749
+ closeSync(fd);
750
+ }
751
+ }
676
752
  function codesignArguments(opts) {
677
753
  return [
678
754
  "--force",
@@ -681,7 +757,9 @@ function codesignArguments(opts) {
681
757
  ...opts.identity === "-" ? [] : ["--timestamp"],
682
758
  ...opts.hardenedRuntime ? ["--options", "runtime"] : [],
683
759
  ...opts.entitlements ? ["--entitlements", opts.entitlements] : [],
684
- "--deep",
760
+ ...opts.identifier ? ["--identifier", opts.identifier] : [],
761
+ ...opts.keychain ? ["--keychain", opts.keychain] : [],
762
+ ...opts.deep === false ? [] : ["--deep"],
685
763
  opts.path
686
764
  ];
687
765
  }
@@ -692,6 +770,7 @@ function productbuildArguments(opts) {
692
770
  "/Applications",
693
771
  "--sign",
694
772
  opts.identity,
773
+ ...opts.keychain ? ["--keychain", opts.keychain] : [],
695
774
  opts.outputPath
696
775
  ];
697
776
  }
@@ -811,7 +890,7 @@ function runTool(tool, args) {
811
890
  });
812
891
  proc.on("close", (code) => {
813
892
  if (code === 0)
814
- resolve({ success: true });
893
+ resolve({ success: true, stdout });
815
894
  else
816
895
  resolve({ success: false, error: formatPackagingCommandError(tool, code, stdout, stderr) });
817
896
  });
@@ -886,7 +965,8 @@ async function createPKG(opts) {
886
965
  const built = await runTool("productbuild", productbuildArguments({
887
966
  appBundlePath: opts.appBundlePath,
888
967
  outputPath: opts.outputPath,
889
- identity: opts.installerIdentity
968
+ identity: opts.installerIdentity,
969
+ keychain: opts.signingKeychain
890
970
  }));
891
971
  return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
892
972
  }