FpmTarget.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const _7zip_bin_1 = require("7zip-bin");
  4. const builder_util_1 = require("builder-util");
  5. const fs_1 = require("builder-util/out/fs");
  6. const fs_extra_1 = require("fs-extra");
  7. const promises_1 = require("fs/promises");
  8. const path = require("path");
  9. const appInfo_1 = require("../appInfo");
  10. const core_1 = require("../core");
  11. const errorMessages = require("../errorMessages");
  12. const appBuilder_1 = require("../util/appBuilder");
  13. const bundledTool_1 = require("../util/bundledTool");
  14. const macosVersion_1 = require("../util/macosVersion");
  15. const pathManager_1 = require("../util/pathManager");
  16. const LinuxTargetHelper_1 = require("./LinuxTargetHelper");
  17. const tools_1 = require("./tools");
  18. const hash_1 = require("../util/hash");
  19. const PublishManager_1 = require("../publish/PublishManager");
  20. class FpmTarget extends core_1.Target {
  21. constructor(name, packager, helper, outDir) {
  22. super(name, false);
  23. this.packager = packager;
  24. this.helper = helper;
  25. this.outDir = outDir;
  26. this.options = { ...this.packager.platformSpecificBuildOptions, ...this.packager.config[this.name] };
  27. this.scriptFiles = this.createScripts();
  28. }
  29. async createScripts() {
  30. const defaultTemplatesDir = (0, pathManager_1.getTemplatePath)("linux");
  31. const packager = this.packager;
  32. const templateOptions = {
  33. // old API compatibility
  34. executable: packager.executableName,
  35. sanitizedProductName: packager.appInfo.sanitizedProductName,
  36. productFilename: packager.appInfo.productFilename,
  37. ...packager.platformSpecificBuildOptions,
  38. };
  39. function getResource(value, defaultFile) {
  40. if (value == null) {
  41. return path.join(defaultTemplatesDir, defaultFile);
  42. }
  43. return path.resolve(packager.projectDir, value);
  44. }
  45. return await Promise.all([
  46. writeConfigFile(packager.info.tempDirManager, getResource(this.options.afterInstall, "after-install.tpl"), templateOptions),
  47. writeConfigFile(packager.info.tempDirManager, getResource(this.options.afterRemove, "after-remove.tpl"), templateOptions),
  48. ]);
  49. }
  50. checkOptions() {
  51. return this.computeFpmMetaInfoOptions();
  52. }
  53. async computeFpmMetaInfoOptions() {
  54. var _a;
  55. const packager = this.packager;
  56. const projectUrl = await packager.appInfo.computePackageUrl();
  57. const errors = [];
  58. if (projectUrl == null) {
  59. errors.push("Please specify project homepage, see https://electron.build/configuration/configuration#Metadata-homepage");
  60. }
  61. const options = this.options;
  62. let author = options.maintainer;
  63. if (author == null) {
  64. const a = packager.info.metadata.author;
  65. if (a == null || a.email == null) {
  66. errors.push(errorMessages.authorEmailIsMissed);
  67. }
  68. else {
  69. author = `${a.name} <${a.email}>`;
  70. }
  71. }
  72. if (errors.length > 0) {
  73. throw new Error(errors.join("\n\n"));
  74. }
  75. return {
  76. name: (_a = options.packageName) !== null && _a !== void 0 ? _a : this.packager.appInfo.linuxPackageName,
  77. maintainer: author,
  78. url: projectUrl,
  79. vendor: options.vendor || author,
  80. };
  81. }
  82. async build(appOutDir, arch) {
  83. var _a;
  84. const target = this.name;
  85. // tslint:disable:no-invalid-template-strings
  86. let nameFormat = "${name}-${version}-${arch}.${ext}";
  87. let isUseArchIfX64 = false;
  88. if (target === "deb") {
  89. nameFormat = "${name}_${version}_${arch}.${ext}";
  90. isUseArchIfX64 = true;
  91. }
  92. else if (target === "rpm") {
  93. nameFormat = "${name}-${version}.${arch}.${ext}";
  94. isUseArchIfX64 = true;
  95. }
  96. const packager = this.packager;
  97. const artifactName = packager.expandArtifactNamePattern(this.options, target, arch, nameFormat, !isUseArchIfX64);
  98. const artifactPath = path.join(this.outDir, artifactName);
  99. await packager.info.callArtifactBuildStarted({
  100. targetPresentableName: target,
  101. file: artifactPath,
  102. arch,
  103. });
  104. await (0, fs_1.unlinkIfExists)(artifactPath);
  105. if (packager.packagerOptions.prepackaged != null) {
  106. await (0, promises_1.mkdir)(this.outDir, { recursive: true });
  107. }
  108. const publishConfig = this.supportsAutoUpdate(target)
  109. ? await (0, PublishManager_1.getAppUpdatePublishConfiguration)(packager, arch, false /* in any case validation will be done on publish */)
  110. : null;
  111. if (publishConfig != null) {
  112. const linuxDistType = this.packager.packagerOptions.prepackaged || path.join(this.outDir, `linux${(0, builder_util_1.getArchSuffix)(arch)}-unpacked`);
  113. const resourceDir = packager.getResourcesDir(linuxDistType);
  114. builder_util_1.log.info({ resourceDir }, `adding autoupdate files for: ${target}. (Beta feature)`);
  115. await (0, fs_extra_1.outputFile)(path.join(resourceDir, "app-update.yml"), (0, builder_util_1.serializeToYaml)(publishConfig));
  116. // Extra file needed for auto-updater to detect installation method
  117. await (0, fs_extra_1.outputFile)(path.join(resourceDir, "package-type"), target);
  118. }
  119. const scripts = await this.scriptFiles;
  120. const appInfo = packager.appInfo;
  121. const options = this.options;
  122. const synopsis = options.synopsis;
  123. const args = [
  124. "--architecture",
  125. (0, builder_util_1.toLinuxArchString)(arch, target),
  126. "--after-install",
  127. scripts[0],
  128. "--after-remove",
  129. scripts[1],
  130. "--description",
  131. (0, appInfo_1.smarten)(target === "rpm" ? this.helper.getDescription(options) : `${synopsis || ""}\n ${this.helper.getDescription(options)}`),
  132. "--version",
  133. this.helper.getSanitizedVersion(target),
  134. "--package",
  135. artifactPath,
  136. ];
  137. (0, appBuilder_1.objectToArgs)(args, (await this.computeFpmMetaInfoOptions()));
  138. const packageCategory = options.packageCategory;
  139. if (packageCategory != null) {
  140. args.push("--category", packageCategory);
  141. }
  142. if (target === "deb") {
  143. args.push("--deb-priority", (_a = options.priority) !== null && _a !== void 0 ? _a : "optional");
  144. }
  145. else if (target === "rpm") {
  146. if (synopsis != null) {
  147. args.push("--rpm-summary", (0, appInfo_1.smarten)(synopsis));
  148. }
  149. }
  150. const fpmConfiguration = {
  151. args,
  152. target,
  153. };
  154. if (options.compression != null) {
  155. fpmConfiguration.compression = options.compression;
  156. }
  157. // noinspection JSDeprecatedSymbols
  158. const depends = options.depends;
  159. if (depends != null) {
  160. if (Array.isArray(depends)) {
  161. fpmConfiguration.customDepends = depends;
  162. }
  163. else {
  164. // noinspection SuspiciousTypeOfGuard
  165. if (typeof depends === "string") {
  166. fpmConfiguration.customDepends = [depends];
  167. }
  168. else {
  169. throw new Error(`depends must be Array or String, but specified as: ${depends}`);
  170. }
  171. }
  172. }
  173. if (target === "deb") {
  174. const recommends = options.recommends;
  175. if (recommends) {
  176. fpmConfiguration.customRecommends = (0, builder_util_1.asArray)(recommends);
  177. }
  178. }
  179. (0, builder_util_1.use)(packager.info.metadata.license, it => args.push("--license", it));
  180. (0, builder_util_1.use)(appInfo.buildNumber, it => args.push("--iteration",
  181. // dashes are not supported for iteration in older versions of fpm
  182. // https://github.com/jordansissel/fpm/issues/1833
  183. it.split("-").join("_")));
  184. (0, builder_util_1.use)(options.fpm, it => args.push(...it));
  185. args.push(`${appOutDir}/=${LinuxTargetHelper_1.installPrefix}/${appInfo.sanitizedProductName}`);
  186. for (const icon of await this.helper.icons) {
  187. const extWithDot = path.extname(icon.file);
  188. const sizeName = extWithDot === ".svg" ? "scalable" : `${icon.size}x${icon.size}`;
  189. args.push(`${icon.file}=/usr/share/icons/hicolor/${sizeName}/apps/${packager.executableName}${extWithDot}`);
  190. }
  191. const mimeTypeFilePath = await this.helper.mimeTypeFiles;
  192. if (mimeTypeFilePath != null) {
  193. args.push(`${mimeTypeFilePath}=/usr/share/mime/packages/${packager.executableName}.xml`);
  194. }
  195. const desktopFilePath = await this.helper.writeDesktopEntry(this.options);
  196. args.push(`${desktopFilePath}=/usr/share/applications/${packager.executableName}.desktop`);
  197. if (packager.packagerOptions.effectiveOptionComputed != null && (await packager.packagerOptions.effectiveOptionComputed([args, desktopFilePath]))) {
  198. return;
  199. }
  200. const env = {
  201. ...process.env,
  202. SZA_PATH: _7zip_bin_1.path7za,
  203. SZA_COMPRESSION_LEVEL: packager.compression === "store" ? "0" : "9",
  204. };
  205. // rpmbuild wants directory rpm with some default config files. Even if we can use dylibbundler, path to such config files are not changed (we need to replace in the binary)
  206. // so, for now, brew install rpm is still required.
  207. if (target !== "rpm" && (await (0, macosVersion_1.isMacOsSierra)())) {
  208. const linuxToolsPath = await (0, tools_1.getLinuxToolsPath)();
  209. Object.assign(env, {
  210. PATH: (0, bundledTool_1.computeEnv)(process.env.PATH, [path.join(linuxToolsPath, "bin")]),
  211. DYLD_LIBRARY_PATH: (0, bundledTool_1.computeEnv)(process.env.DYLD_LIBRARY_PATH, [path.join(linuxToolsPath, "lib")]),
  212. });
  213. }
  214. await (0, builder_util_1.executeAppBuilder)(["fpm", "--configuration", JSON.stringify(fpmConfiguration)], undefined, { env });
  215. let info = {
  216. file: artifactPath,
  217. target: this,
  218. arch,
  219. packager,
  220. };
  221. if (publishConfig != null) {
  222. info = {
  223. ...info,
  224. safeArtifactName: packager.computeSafeArtifactName(artifactName, target, arch, !isUseArchIfX64),
  225. isWriteUpdateInfo: true,
  226. updateInfo: {
  227. sha512: await (0, hash_1.hashFile)(artifactPath),
  228. size: (await (0, fs_extra_1.stat)(artifactPath)).size,
  229. },
  230. };
  231. }
  232. await packager.info.callArtifactBuildCompleted(info);
  233. }
  234. supportsAutoUpdate(target) {
  235. return ["deb", "rpm"].includes(target);
  236. }
  237. }
  238. exports.default = FpmTarget;
  239. async function writeConfigFile(tmpDir, templatePath, options) {
  240. //noinspection JSUnusedLocalSymbols
  241. function replacer(match, p1) {
  242. if (p1 in options) {
  243. return options[p1];
  244. }
  245. else {
  246. throw new Error(`Macro ${p1} is not defined`);
  247. }
  248. }
  249. const config = (await (0, promises_1.readFile)(templatePath, "utf8")).replace(/\${([a-zA-Z]+)}/g, replacer).replace(/<%=([a-zA-Z]+)%>/g, (match, p1) => {
  250. builder_util_1.log.warn("<%= varName %> is deprecated, please use ${varName} instead");
  251. return replacer(match, p1.trim());
  252. });
  253. const outputPath = await tmpDir.getTempFile({ suffix: path.basename(templatePath, ".tpl") });
  254. await (0, fs_extra_1.outputFile)(outputPath, config);
  255. return outputPath;
  256. }
  257. //# sourceMappingURL=FpmTarget.js.map