pkg.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.prepareProductBuildArgs = exports.PkgTarget = void 0;
  4. const builder_util_1 = require("builder-util");
  5. const fs_1 = require("builder-util/out/fs");
  6. const appBuilder_1 = require("../util/appBuilder");
  7. const license_1 = require("../util/license");
  8. const promises_1 = require("fs/promises");
  9. const path = require("path");
  10. const appInfo_1 = require("../appInfo");
  11. const macCodeSign_1 = require("../codeSign/macCodeSign");
  12. const core_1 = require("../core");
  13. const certType = "Developer ID Installer";
  14. // http://www.shanekirk.com/2013/10/creating-flat-packages-in-osx/
  15. // to use --scripts, we must build .app bundle separately using pkgbuild
  16. // productbuild --scripts doesn't work (because scripts in this case not added to our package)
  17. // https://github.com/electron-userland/@electron/osx-sign/issues/96#issuecomment-274986942
  18. class PkgTarget extends core_1.Target {
  19. constructor(packager, outDir) {
  20. super("pkg");
  21. this.packager = packager;
  22. this.outDir = outDir;
  23. this.options = {
  24. allowAnywhere: true,
  25. allowCurrentUserHome: true,
  26. allowRootDirectory: true,
  27. ...this.packager.config.pkg,
  28. };
  29. }
  30. async build(appPath, arch) {
  31. const packager = this.packager;
  32. const options = this.options;
  33. const appInfo = packager.appInfo;
  34. // pkg doesn't like not ASCII symbols (Could not open package to list files: /Volumes/test/t-gIjdGK/test-project-0/dist/Test App ßW-1.1.0.pkg)
  35. const artifactName = packager.expandArtifactNamePattern(options, "pkg", arch);
  36. const artifactPath = path.join(this.outDir, artifactName);
  37. await packager.info.callArtifactBuildStarted({
  38. targetPresentableName: "pkg",
  39. file: artifactPath,
  40. arch,
  41. });
  42. const keychainFile = (await packager.codeSigningInfo.value).keychainFile;
  43. const appOutDir = this.outDir;
  44. // https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html
  45. const distInfoFile = path.join(appOutDir, "distribution.xml");
  46. const innerPackageFile = path.join(appOutDir, `${(0, appInfo_1.filterCFBundleIdentifier)(appInfo.id)}.pkg`);
  47. const componentPropertyListFile = path.join(appOutDir, `${(0, appInfo_1.filterCFBundleIdentifier)(appInfo.id)}.plist`);
  48. const identity = (await Promise.all([
  49. (0, macCodeSign_1.findIdentity)(certType, options.identity || packager.platformSpecificBuildOptions.identity, keychainFile),
  50. this.customizeDistributionConfiguration(distInfoFile, appPath),
  51. this.buildComponentPackage(appPath, componentPropertyListFile, innerPackageFile),
  52. ]))[0];
  53. if (identity == null && packager.forceCodeSigning) {
  54. throw new Error(`Cannot find valid "${certType}" to sign standalone installer, please see https://electron.build/code-signing`);
  55. }
  56. const args = prepareProductBuildArgs(identity, keychainFile);
  57. args.push("--distribution", distInfoFile);
  58. args.push(artifactPath);
  59. (0, builder_util_1.use)(options.productbuild, it => args.push(...it));
  60. await (0, builder_util_1.exec)("productbuild", args, {
  61. cwd: appOutDir,
  62. });
  63. await Promise.all([(0, promises_1.unlink)(innerPackageFile), (0, promises_1.unlink)(distInfoFile)]);
  64. await packager.dispatchArtifactCreated(artifactPath, this, arch, packager.computeSafeArtifactName(artifactName, "pkg", arch));
  65. }
  66. async customizeDistributionConfiguration(distInfoFile, appPath) {
  67. await (0, builder_util_1.exec)("productbuild", ["--synthesize", "--component", appPath, distInfoFile], {
  68. cwd: this.outDir,
  69. });
  70. const options = this.options;
  71. let distInfo = await (0, promises_1.readFile)(distInfoFile, "utf-8");
  72. if (options.mustClose != null && options.mustClose.length !== 0) {
  73. const startContent = ` <pkg-ref id="${this.packager.appInfo.id}">\n <must-close>\n`;
  74. const endContent = " </must-close>\n </pkg-ref>\n</installer-gui-script>";
  75. let mustCloseContent = "";
  76. options.mustClose.forEach(appId => {
  77. mustCloseContent += ` <app id="${appId}"/>\n`;
  78. });
  79. distInfo = distInfo.replace("</installer-gui-script>", `${startContent}${mustCloseContent}${endContent}`);
  80. }
  81. const insertIndex = distInfo.lastIndexOf("</installer-gui-script>");
  82. distInfo =
  83. distInfo.substring(0, insertIndex) +
  84. ` <domains enable_anywhere="${options.allowAnywhere}" enable_currentUserHome="${options.allowCurrentUserHome}" enable_localSystem="${options.allowRootDirectory}" />\n` +
  85. distInfo.substring(insertIndex);
  86. if (options.background != null) {
  87. const background = await this.packager.getResource(options.background.file);
  88. if (background != null) {
  89. const alignment = options.background.alignment || "center";
  90. // noinspection SpellCheckingInspection
  91. const scaling = options.background.scaling || "tofit";
  92. distInfo = distInfo.substring(0, insertIndex) + ` <background file="${background}" alignment="${alignment}" scaling="${scaling}"/>\n` + distInfo.substring(insertIndex);
  93. distInfo =
  94. distInfo.substring(0, insertIndex) + ` <background-darkAqua file="${background}" alignment="${alignment}" scaling="${scaling}"/>\n` + distInfo.substring(insertIndex);
  95. }
  96. }
  97. const welcome = await this.packager.getResource(options.welcome);
  98. if (welcome != null) {
  99. distInfo = distInfo.substring(0, insertIndex) + ` <welcome file="${welcome}"/>\n` + distInfo.substring(insertIndex);
  100. }
  101. const license = await (0, license_1.getNotLocalizedLicenseFile)(options.license, this.packager);
  102. if (license != null) {
  103. distInfo = distInfo.substring(0, insertIndex) + ` <license file="${license}"/>\n` + distInfo.substring(insertIndex);
  104. }
  105. const conclusion = await this.packager.getResource(options.conclusion);
  106. if (conclusion != null) {
  107. distInfo = distInfo.substring(0, insertIndex) + ` <conclusion file="${conclusion}"/>\n` + distInfo.substring(insertIndex);
  108. }
  109. (0, builder_util_1.debug)(distInfo);
  110. await (0, promises_1.writeFile)(distInfoFile, distInfo);
  111. }
  112. async buildComponentPackage(appPath, propertyListOutputFile, packageOutputFile) {
  113. const options = this.options;
  114. const rootPath = path.dirname(appPath);
  115. // first produce a component plist template
  116. await (0, builder_util_1.exec)("pkgbuild", ["--analyze", "--root", rootPath, propertyListOutputFile]);
  117. // process the template plist
  118. const plistInfo = (await (0, appBuilder_1.executeAppBuilderAsJson)(["decode-plist", "-f", propertyListOutputFile]))[0].filter((it) => it.RootRelativeBundlePath !== "Electron.dSYM");
  119. if (plistInfo.length > 0) {
  120. const packageInfo = plistInfo[0];
  121. // ChildBundles lists all of electron binaries within the .app.
  122. // There is no particular reason for removing that key, except to be as close as possible to
  123. // the PackageInfo generated by previous versions of electron-builder.
  124. delete packageInfo.ChildBundles;
  125. if (options.isRelocatable != null) {
  126. packageInfo.BundleIsRelocatable = options.isRelocatable;
  127. }
  128. if (options.isVersionChecked != null) {
  129. packageInfo.BundleIsVersionChecked = options.isVersionChecked;
  130. }
  131. if (options.hasStrictIdentifier != null) {
  132. packageInfo.BundleHasStrictIdentifier = options.hasStrictIdentifier;
  133. }
  134. if (options.overwriteAction != null) {
  135. packageInfo.BundleOverwriteAction = options.overwriteAction;
  136. }
  137. await (0, appBuilder_1.executeAppBuilderAndWriteJson)(["encode-plist"], { [propertyListOutputFile]: plistInfo });
  138. }
  139. // now build the package
  140. const args = ["--root", rootPath, "--identifier", this.packager.appInfo.id, "--component-plist", propertyListOutputFile];
  141. (0, builder_util_1.use)(this.options.installLocation || "/Applications", it => args.push("--install-location", it));
  142. if (options.scripts != null) {
  143. args.push("--scripts", path.resolve(this.packager.info.buildResourcesDir, options.scripts));
  144. }
  145. else if (options.scripts !== null) {
  146. const dir = path.join(this.packager.info.buildResourcesDir, "pkg-scripts");
  147. const stat = await (0, fs_1.statOrNull)(dir);
  148. if (stat != null && stat.isDirectory()) {
  149. args.push("--scripts", dir);
  150. }
  151. }
  152. args.push(packageOutputFile);
  153. await (0, builder_util_1.exec)("pkgbuild", args);
  154. }
  155. }
  156. exports.PkgTarget = PkgTarget;
  157. function prepareProductBuildArgs(identity, keychain) {
  158. const args = [];
  159. if (identity != null) {
  160. args.push("--sign", identity.hash);
  161. if (keychain != null) {
  162. args.push("--keychain", keychain);
  163. }
  164. }
  165. return args;
  166. }
  167. exports.prepareProductBuildArgs = prepareProductBuildArgs;
  168. //# sourceMappingURL=pkg.js.map