app-builder-lib 26.7.0 → 26.8.0

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.
Files changed (54) hide show
  1. package/out/codeSign/windowsSignToolManager.d.ts +4 -5
  2. package/out/codeSign/windowsSignToolManager.js +76 -86
  3. package/out/codeSign/windowsSignToolManager.js.map +1 -1
  4. package/out/configuration.d.ts +34 -0
  5. package/out/configuration.js.map +1 -1
  6. package/out/index.d.ts +1 -1
  7. package/out/index.js.map +1 -1
  8. package/out/linuxPackager.js +1 -1
  9. package/out/linuxPackager.js.map +1 -1
  10. package/out/node-module-collector/index.d.ts +1 -2
  11. package/out/node-module-collector/nodeModulesCollector.js +2 -2
  12. package/out/node-module-collector/nodeModulesCollector.js.map +1 -1
  13. package/out/node-module-collector/types.d.ts +0 -4
  14. package/out/node-module-collector/types.js.map +1 -1
  15. package/out/node-module-collector/yarnNodeModulesCollector.d.ts +4 -10
  16. package/out/node-module-collector/yarnNodeModulesCollector.js +7 -197
  17. package/out/node-module-collector/yarnNodeModulesCollector.js.map +1 -1
  18. package/out/targets/AppxTarget.js +8 -7
  19. package/out/targets/AppxTarget.js.map +1 -1
  20. package/out/targets/FpmTarget.js +3 -3
  21. package/out/targets/FpmTarget.js.map +1 -1
  22. package/out/targets/MsiWrappedTarget.js +4 -3
  23. package/out/targets/MsiWrappedTarget.js.map +1 -1
  24. package/out/targets/appimage/AppImageTarget.d.ts +16 -0
  25. package/out/targets/{AppImageTarget.js → appimage/AppImageTarget.js} +65 -28
  26. package/out/targets/appimage/AppImageTarget.js.map +1 -0
  27. package/out/targets/appimage/appImageUtil.d.ts +23 -0
  28. package/out/targets/appimage/appImageUtil.js +266 -0
  29. package/out/targets/appimage/appImageUtil.js.map +1 -0
  30. package/out/targets/appimage/appLauncher.d.ts +3 -0
  31. package/out/targets/appimage/appLauncher.js +91 -0
  32. package/out/targets/appimage/appLauncher.js.map +1 -0
  33. package/out/targets/archive.js +2 -2
  34. package/out/targets/archive.js.map +1 -1
  35. package/out/toolsets/linux.d.ts +9 -0
  36. package/out/toolsets/linux.js +68 -0
  37. package/out/toolsets/linux.js.map +1 -0
  38. package/out/toolsets/windows.d.ts +42 -0
  39. package/out/toolsets/windows.js +143 -0
  40. package/out/toolsets/windows.js.map +1 -0
  41. package/out/util/packageMetadata.js +9 -5
  42. package/out/util/packageMetadata.js.map +1 -1
  43. package/out/version.d.ts +1 -1
  44. package/out/version.js +1 -1
  45. package/out/version.js.map +1 -1
  46. package/out/winPackager.js +4 -3
  47. package/out/winPackager.js.map +1 -1
  48. package/package.json +7 -7
  49. package/scheme.json +51 -0
  50. package/out/targets/AppImageTarget.d.ts +0 -14
  51. package/out/targets/AppImageTarget.js.map +0 -1
  52. package/out/targets/tools.d.ts +0 -2
  53. package/out/targets/tools.js +0 -46
  54. package/out/targets/tools.js.map +0 -1
@@ -1,17 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.APP_RUN_ENTRYPOINT = void 0;
3
4
  const builder_util_1 = require("builder-util");
4
5
  const fs_extra_1 = require("fs-extra");
5
6
  const lazy_val_1 = require("lazy-val");
6
7
  const path = require("path");
7
- const core_1 = require("../core");
8
- const PublishManager_1 = require("../publish/PublishManager");
9
- const appBuilder_1 = require("../util/appBuilder");
10
- const license_1 = require("../util/license");
11
- const targetUtil_1 = require("./targetUtil");
8
+ const core_1 = require("../../core");
9
+ const PublishManager_1 = require("../../publish/PublishManager");
10
+ const appBuilder_1 = require("../../util/appBuilder");
11
+ const license_1 = require("../../util/license");
12
+ const targetUtil_1 = require("../targetUtil");
13
+ const appImageUtil_1 = require("./appImageUtil");
12
14
  // https://unix.stackexchange.com/questions/375191/append-to-sub-directory-inside-squashfs-file
15
+ exports.APP_RUN_ENTRYPOINT = "AppRun";
13
16
  class AppImageTarget extends core_1.Target {
14
- constructor(ignored, packager, helper, outDir) {
17
+ constructor(_ignored, packager, helper, outDir) {
15
18
  super("appImage");
16
19
  this.packager = packager;
17
20
  this.helper = helper;
@@ -20,17 +23,17 @@ class AppImageTarget extends core_1.Target {
20
23
  this.desktopEntry = new lazy_val_1.Lazy(() => {
21
24
  var _a;
22
25
  const args = ((_a = this.options.executableArgs) === null || _a === void 0 ? void 0 : _a.join(" ")) || "--no-sandbox";
23
- return helper.computeDesktopEntry(this.options, `AppRun ${args} %U`, {
26
+ return helper.computeDesktopEntry(this.options, `${exports.APP_RUN_ENTRYPOINT} ${args} %U`, {
24
27
  "X-AppImage-Version": `${packager.appInfo.buildVersion}`,
25
28
  });
26
29
  });
27
30
  }
28
31
  async build(appOutDir, arch) {
32
+ var _a;
29
33
  const packager = this.packager;
30
34
  const options = this.options;
31
35
  // https://github.com/electron-userland/electron-builder/issues/775
32
36
  // https://github.com/electron-userland/electron-builder/issues/1726
33
- // tslint:disable-next-line:no-invalid-template-strings
34
37
  const artifactName = packager.expandArtifactNamePattern(options, "AppImage", arch);
35
38
  const artifactPath = path.join(this.outDir, artifactName);
36
39
  await packager.info.emitArtifactBuildStarted({
@@ -38,23 +41,64 @@ class AppImageTarget extends core_1.Target {
38
41
  file: artifactPath,
39
42
  arch,
40
43
  });
41
- const c = await Promise.all([
44
+ // Parallelize independent async operations
45
+ const [publishConfig, stageDir, desktopEntry, icons, license] = await Promise.all([
46
+ (0, PublishManager_1.getAppUpdatePublishConfiguration)(packager, options, arch, false),
47
+ (0, targetUtil_1.createStageDir)(this, packager, arch),
42
48
  this.desktopEntry.value,
43
49
  this.helper.icons,
44
- (0, PublishManager_1.getAppUpdatePublishConfiguration)(packager, options, arch, false /* in any case validation will be done on publish */),
45
50
  (0, license_1.getNotLocalizedLicenseFile)(options.license, this.packager, ["txt", "html"]),
46
- (0, targetUtil_1.createStageDir)(this, packager, arch),
47
51
  ]);
48
- const license = c[3];
49
- const stageDir = c[4];
50
- const publishConfig = c[2];
51
52
  if (publishConfig != null) {
52
- await (0, fs_extra_1.outputFile)(path.join(packager.getResourcesDir(stageDir.dir), "app-update.yml"), (0, builder_util_1.serializeToYaml)(publishConfig));
53
+ await (0, fs_extra_1.outputFile)(path.join(packager.getResourcesDir(appOutDir), "app-update.yml"), (0, builder_util_1.serializeToYaml)(publishConfig));
53
54
  }
54
- if (this.packager.packagerOptions.effectiveOptionComputed != null &&
55
- (await this.packager.packagerOptions.effectiveOptionComputed({ desktop: await this.desktopEntry.value }))) {
55
+ if (this.packager.packagerOptions.effectiveOptionComputed != null && (await this.packager.packagerOptions.effectiveOptionComputed({ desktop: desktopEntry }))) {
56
+ await stageDir.cleanup();
56
57
  return;
57
58
  }
59
+ let updateInfo;
60
+ try {
61
+ const appimageTool = (_a = this.packager.config.toolsets) === null || _a === void 0 ? void 0 : _a.appimage;
62
+ if (appimageTool == null || appimageTool === "0.0.0") {
63
+ updateInfo = await this.buildFuse2AppImage({ stageDir, arch, artifactPath, appOutDir, options, packager, desktopEntry, icons, license });
64
+ }
65
+ else {
66
+ updateInfo = await (0, appImageUtil_1.buildAppImage)({
67
+ appDir: appOutDir,
68
+ stageDir: stageDir.dir,
69
+ arch,
70
+ output: artifactPath,
71
+ options: {
72
+ productName: this.packager.appInfo.productName,
73
+ productFilename: this.packager.appInfo.productFilename,
74
+ executableName: this.packager.executableName,
75
+ license,
76
+ desktopEntry,
77
+ icons,
78
+ fileAssociations: this.packager.fileAssociations,
79
+ compression: this.packager.compression === "maximum" ? "xz" : undefined,
80
+ },
81
+ });
82
+ }
83
+ }
84
+ catch (error) {
85
+ builder_util_1.log.error({ error: error.message }, "failed to build AppImage");
86
+ await stageDir.cleanup().catch(() => { });
87
+ throw error;
88
+ }
89
+ await stageDir.cleanup();
90
+ await packager.info.emitArtifactBuildCompleted({
91
+ file: artifactPath,
92
+ safeArtifactName: packager.computeSafeArtifactName(artifactName, "AppImage", arch, false),
93
+ target: this,
94
+ arch,
95
+ packager,
96
+ isWriteUpdateInfo: true,
97
+ updateInfo,
98
+ });
99
+ }
100
+ async buildFuse2AppImage(props) {
101
+ const { stageDir, arch, artifactPath, appOutDir, options, packager, desktopEntry, icons, license } = props;
58
102
  const args = [
59
103
  "appimage",
60
104
  "--stage",
@@ -69,9 +113,9 @@ class AppImageTarget extends core_1.Target {
69
113
  JSON.stringify({
70
114
  productName: this.packager.appInfo.productName,
71
115
  productFilename: this.packager.appInfo.productFilename,
72
- desktopEntry: c[0],
116
+ desktopEntry,
73
117
  executableName: this.packager.executableName,
74
- icons: c[1],
118
+ icons,
75
119
  fileAssociations: this.packager.fileAssociations,
76
120
  ...options,
77
121
  }),
@@ -82,15 +126,8 @@ class AppImageTarget extends core_1.Target {
82
126
  if (packager.compression === "maximum") {
83
127
  args.push("--compression", "xz");
84
128
  }
85
- await packager.info.emitArtifactBuildCompleted({
86
- file: artifactPath,
87
- safeArtifactName: packager.computeSafeArtifactName(artifactName, "AppImage", arch, false),
88
- target: this,
89
- arch,
90
- packager,
91
- isWriteUpdateInfo: true,
92
- updateInfo: await (0, appBuilder_1.executeAppBuilderAsJson)(args),
93
- });
129
+ const updateInfo = await (0, appBuilder_1.executeAppBuilderAsJson)(args);
130
+ return updateInfo;
94
131
  }
95
132
  }
96
133
  exports.default = AppImageTarget;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppImageTarget.js","sourceRoot":"","sources":["../../../src/targets/appimage/AppImageTarget.ts"],"names":[],"mappings":"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","sourcesContent":["import { IconInfo } from \"../../platformPackager\"\nimport { Arch, log, serializeToYaml } from \"builder-util\"\nimport { outputFile } from \"fs-extra\"\nimport { Lazy } from \"lazy-val\"\nimport * as path from \"path\"\nimport { Target } from \"../../core\"\nimport { LinuxPackager } from \"../../linuxPackager\"\nimport { AppImageOptions } from \"../../options/linuxOptions\"\nimport { getAppUpdatePublishConfiguration } from \"../../publish/PublishManager\"\nimport { executeAppBuilderAsJson, objectToArgs } from \"../../util/appBuilder\"\nimport { getNotLocalizedLicenseFile } from \"../../util/license\"\nimport { LinuxTargetHelper } from \"../LinuxTargetHelper\"\nimport { createStageDir, StageDir } from \"../targetUtil\"\nimport { buildAppImage } from \"./appImageUtil\"\nimport { BlockMapDataHolder } from \"builder-util-runtime\"\n\n// https://unix.stackexchange.com/questions/375191/append-to-sub-directory-inside-squashfs-file\n\nexport const APP_RUN_ENTRYPOINT = \"AppRun\"\n\nexport default class AppImageTarget extends Target {\n readonly options: AppImageOptions = { ...this.packager.platformSpecificBuildOptions, ...(this.packager.config as any)[this.name] }\n\n private readonly desktopEntry: Lazy<string>\n\n constructor(\n _ignored: string,\n private readonly packager: LinuxPackager,\n private readonly helper: LinuxTargetHelper,\n readonly outDir: string\n ) {\n super(\"appImage\")\n\n this.desktopEntry = new Lazy<string>(() => {\n const args = this.options.executableArgs?.join(\" \") || \"--no-sandbox\"\n return helper.computeDesktopEntry(this.options, `${APP_RUN_ENTRYPOINT} ${args} %U`, {\n \"X-AppImage-Version\": `${packager.appInfo.buildVersion}`,\n })\n })\n }\n\n async build(appOutDir: string, arch: Arch): Promise<any> {\n const packager = this.packager\n\n const options = this.options\n // https://github.com/electron-userland/electron-builder/issues/775\n // https://github.com/electron-userland/electron-builder/issues/1726\n const artifactName = packager.expandArtifactNamePattern(options, \"AppImage\", arch)\n const artifactPath = path.join(this.outDir, artifactName)\n\n await packager.info.emitArtifactBuildStarted({\n targetPresentableName: \"AppImage\",\n file: artifactPath,\n arch,\n })\n\n // Parallelize independent async operations\n const [publishConfig, stageDir, desktopEntry, icons, license] = await Promise.all([\n getAppUpdatePublishConfiguration(packager, options, arch, false),\n createStageDir(this, packager, arch),\n this.desktopEntry.value,\n this.helper.icons,\n getNotLocalizedLicenseFile(options.license, this.packager, [\"txt\", \"html\"]),\n ])\n\n if (publishConfig != null) {\n await outputFile(path.join(packager.getResourcesDir(appOutDir), \"app-update.yml\"), serializeToYaml(publishConfig))\n }\n\n if (this.packager.packagerOptions.effectiveOptionComputed != null && (await this.packager.packagerOptions.effectiveOptionComputed({ desktop: desktopEntry }))) {\n await stageDir.cleanup()\n return\n }\n\n let updateInfo: BlockMapDataHolder\n try {\n const appimageTool = this.packager.config.toolsets?.appimage\n if (appimageTool == null || appimageTool === \"0.0.0\") {\n updateInfo = await this.buildFuse2AppImage({ stageDir, arch, artifactPath, appOutDir, options, packager, desktopEntry, icons, license })\n } else {\n updateInfo = await buildAppImage({\n appDir: appOutDir,\n stageDir: stageDir.dir,\n arch,\n output: artifactPath,\n options: {\n productName: this.packager.appInfo.productName,\n productFilename: this.packager.appInfo.productFilename,\n executableName: this.packager.executableName,\n license,\n desktopEntry,\n icons,\n fileAssociations: this.packager.fileAssociations,\n compression: this.packager.compression === \"maximum\" ? \"xz\" : undefined,\n },\n })\n }\n } catch (error: any) {\n log.error({ error: error.message }, \"failed to build AppImage\")\n await stageDir.cleanup().catch(() => {})\n throw error\n }\n await stageDir.cleanup()\n\n await packager.info.emitArtifactBuildCompleted({\n file: artifactPath,\n safeArtifactName: packager.computeSafeArtifactName(artifactName, \"AppImage\", arch, false),\n target: this,\n arch,\n packager,\n isWriteUpdateInfo: true,\n updateInfo,\n })\n }\n\n private async buildFuse2AppImage(props: {\n stageDir: StageDir\n arch: Arch\n artifactPath: string\n appOutDir: string\n options: AppImageOptions\n packager: LinuxPackager\n desktopEntry: string\n icons: IconInfo[]\n license: string | null\n }): Promise<BlockMapDataHolder> {\n const { stageDir, arch, artifactPath, appOutDir, options, packager, desktopEntry, icons, license } = props\n\n const args = [\n \"appimage\",\n \"--stage\",\n stageDir.dir,\n \"--arch\",\n Arch[arch],\n \"--output\",\n artifactPath,\n \"--app\",\n appOutDir,\n \"--configuration\",\n JSON.stringify({\n productName: this.packager.appInfo.productName,\n productFilename: this.packager.appInfo.productFilename,\n desktopEntry,\n executableName: this.packager.executableName,\n icons,\n fileAssociations: this.packager.fileAssociations,\n ...options,\n }),\n ]\n objectToArgs(args, {\n license,\n })\n if (packager.compression === \"maximum\") {\n args.push(\"--compression\", \"xz\")\n }\n\n const updateInfo = await executeAppBuilderAsJson<BlockMapDataHolder>(args)\n return updateInfo\n }\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import { Arch } from "builder-util";
2
+ import { FileAssociation } from "../../options/FileAssociation";
3
+ import { IconInfo } from "../../platformPackager";
4
+ import { BlockMapDataHolder } from "builder-util-runtime";
5
+ interface Options {
6
+ productName: string;
7
+ productFilename: string;
8
+ executableName: string;
9
+ desktopEntry: string;
10
+ icons: IconInfo[];
11
+ license?: string | null;
12
+ fileAssociations: FileAssociation[];
13
+ compression?: "xz" | "lzo" | "zstd";
14
+ }
15
+ export interface AppImageBuilderOptions {
16
+ appDir: string;
17
+ stageDir: string;
18
+ arch: Arch;
19
+ output: string;
20
+ options: Options;
21
+ }
22
+ export declare function buildAppImage(opts: AppImageBuilderOptions): Promise<BlockMapDataHolder>;
23
+ export {};
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildAppImage = buildAppImage;
4
+ const builder_util_1 = require("builder-util");
5
+ const fs = require("fs-extra");
6
+ const path = require("path");
7
+ const linux_1 = require("../../toolsets/linux");
8
+ const appLauncher_1 = require("./appLauncher");
9
+ const differentialUpdateInfoBuilder_1 = require("../differentialUpdateInfoBuilder");
10
+ const AppImageTarget_1 = require("./AppImageTarget");
11
+ async function buildAppImage(opts) {
12
+ const { stageDir, output, appDir, options, arch } = opts;
13
+ try {
14
+ await fs.remove(output);
15
+ // Write AppRun launcher and related files
16
+ await writeAppLauncherAndRelatedFiles(opts);
17
+ const { runtimeLibraries: libraries, runtime, mksquashfs } = await (0, linux_1.getAppImageTools)(arch);
18
+ await (0, builder_util_1.copyDir)(libraries, path.join(stageDir, "usr", "lib"));
19
+ // Copy app directory to stage
20
+ // mksquashfs doesn't support merging, so we copy the entire app dir
21
+ await (0, builder_util_1.copyDir)(appDir, stageDir);
22
+ const runtimeData = await fs.readFile(runtime);
23
+ // Create squashfs with offset for runtime
24
+ const args = [stageDir, output, "-offset", runtimeData.length.toString(), "-all-root", "-noappend", "-no-progress", "-quiet", "-no-xattrs", "-no-fragments"];
25
+ if (options.compression) {
26
+ args.push("-comp", options.compression);
27
+ if (options.compression === "xz") {
28
+ args.push("-Xdict-size", "100%", "-b", "1048576");
29
+ }
30
+ }
31
+ await (0, builder_util_1.exec)(mksquashfs, args, {
32
+ cwd: stageDir,
33
+ });
34
+ // Write runtime data at the beginning of the file
35
+ await writeRuntimeData(output, runtimeData);
36
+ // Make executable
37
+ await fs.chmod(output, 0o755);
38
+ // Append blockmap inside try block to ensure cleanup on failure
39
+ const updateInfo = await (0, differentialUpdateInfoBuilder_1.appendBlockmap)(output);
40
+ return updateInfo;
41
+ }
42
+ catch (error) {
43
+ // Clean up partial build on failure
44
+ await fs.remove(output).catch(() => { });
45
+ throw error;
46
+ }
47
+ }
48
+ async function writeRuntimeData(filePath, runtimeData) {
49
+ const fd = await fs.open(filePath, "r+");
50
+ try {
51
+ await fs.write(fd, runtimeData, 0, runtimeData.length, 0);
52
+ }
53
+ finally {
54
+ try {
55
+ await fs.close(fd);
56
+ }
57
+ catch (closeError) {
58
+ // Log but don't throw - preserve original error if any
59
+ builder_util_1.log.warn({ message: closeError.message, file: filePath }, `failed to close file descriptor`);
60
+ }
61
+ }
62
+ }
63
+ /**
64
+ * Escapes a string for safe use in shell scripts by wrapping in single quotes
65
+ * and escaping any single quotes within the string.
66
+ *
67
+ * This allows strings with spaces, special characters, etc. to be safely used.
68
+ */
69
+ function escapeShellString(str) {
70
+ // Escape single quotes by replacing ' with '\''
71
+ // Then wrap the whole string in single quotes
72
+ return `'${str.replace(/'/g, "'\\''")}'`;
73
+ }
74
+ /**
75
+ * Validates that critical executable/filename fields don't contain dangerous characters
76
+ * that could break paths or cause security issues even when escaped.
77
+ */
78
+ function validateCriticalPathString(str, fieldName) {
79
+ // Only reject characters that would break filesystem paths or cause severe issues
80
+ // Allow quotes, spaces, etc. since they can be escaped
81
+ if (/[`${}|&;<>\n\r\0]/.test(str) || str.includes("/") || str.includes("\\")) {
82
+ throw new builder_util_1.InvalidConfigurationError(`${fieldName} contains characters that cannot be safely used in file paths: ${str}. ` +
83
+ `Please use only alphanumeric characters, hyphens, underscores, dots, spaces, and quotes.`);
84
+ }
85
+ }
86
+ async function writeAppLauncherAndRelatedFiles(opts) {
87
+ const { stageDir, options: { license, executableName, productFilename, productName, desktopEntry }, } = opts;
88
+ // Validate only critical path fields for severe path-breaking characters
89
+ // productName and productFilename can contain quotes, spaces, etc. - they'll be escaped
90
+ validateCriticalPathString(executableName, "executableName");
91
+ validateCriticalPathString(productFilename, "productFilename");
92
+ // Write desktop file
93
+ const desktopFileName = `${executableName}.desktop`;
94
+ await fs.writeFile(path.join(stageDir, desktopFileName), desktopEntry, { mode: 0o644 });
95
+ await (0, appLauncher_1.copyIcons)(opts);
96
+ const templateConfig = {
97
+ DesktopFileName: desktopFileName,
98
+ ExecutableName: executableName,
99
+ ProductName: productName,
100
+ ProductFilename: productFilename,
101
+ ResourceName: `appimagekit-${executableName}`,
102
+ };
103
+ const mimeTypeFile = await (0, appLauncher_1.copyMimeTypes)(opts);
104
+ if (mimeTypeFile) {
105
+ templateConfig.MimeTypeFile = mimeTypeFile;
106
+ }
107
+ let finalConfig = templateConfig;
108
+ // Copy license file if provided
109
+ if (license) {
110
+ // Validate license file exists
111
+ if (!(await (0, builder_util_1.exists)(license))) {
112
+ throw new builder_util_1.InvalidConfigurationError(`License file not found: ${license}`);
113
+ }
114
+ const licenseBaseName = path.basename(license);
115
+ const ext = path.extname(license).toLowerCase();
116
+ // Validate license filename for path safety
117
+ validateCriticalPathString(licenseBaseName, "licenseBaseName");
118
+ // Validate extension
119
+ if (![".txt", ".html"].includes(ext)) {
120
+ builder_util_1.log.warn({ license, expected: ".txt or .html" }, `license file has unexpected extension`);
121
+ }
122
+ await (0, builder_util_1.copyFile)(license, path.join(stageDir, licenseBaseName));
123
+ finalConfig = {
124
+ ...templateConfig,
125
+ EulaFile: licenseBaseName,
126
+ IsHtmlEula: ext === ".html",
127
+ };
128
+ }
129
+ const appRunContent = generateAppRunScript(finalConfig);
130
+ await fs.writeFile(path.join(stageDir, AppImageTarget_1.APP_RUN_ENTRYPOINT), appRunContent, { mode: 0o755 });
131
+ }
132
+ function hasEula(config) {
133
+ return "EulaFile" in config && typeof config.EulaFile === "string";
134
+ }
135
+ function generateAppRunScript(config) {
136
+ const eulaEnabled = hasEula(config);
137
+ return `#!/bin/bash
138
+ set -e
139
+
140
+ THIS="$0"
141
+ # http://stackoverflow.com/questions/3190818/
142
+ args=("$@")
143
+ NUMBER_OF_ARGS="$#"
144
+
145
+ if [ -z "$APPDIR" ] ; then
146
+ # Find the AppDir. It is the directory that contains AppRun.
147
+ # This assumes that this script resides inside the AppDir or a subdirectory.
148
+ # If this script is run inside an AppImage, then the AppImage runtime likely has already set $APPDIR
149
+ path="$(dirname "$(readlink -f "\${THIS}")")"
150
+ while [[ "$path" != "" && ! -e "$path/${AppImageTarget_1.APP_RUN_ENTRYPOINT}" ]]; do
151
+ path=\${path%/*}
152
+ done
153
+ APPDIR="$path"
154
+ fi
155
+
156
+ export PATH="\${APPDIR}:\${APPDIR}/usr/sbin:\${PATH}"
157
+ export XDG_DATA_DIRS="./share/:/usr/share/gnome:/usr/local/share/:/usr/share/:\${XDG_DATA_DIRS}"
158
+ export LD_LIBRARY_PATH="\${APPDIR}/usr/lib:\${LD_LIBRARY_PATH}"
159
+ export XDG_DATA_DIRS="\${APPDIR}"/usr/share/:"\${XDG_DATA_DIRS}":/usr/share/gnome/:/usr/local/share/:/usr/share/
160
+ export GSETTINGS_SCHEMA_DIR="\${APPDIR}/usr/share/glib-2.0/schemas:\${GSETTINGS_SCHEMA_DIR}"
161
+
162
+ BIN="$APPDIR/${config.ExecutableName}"
163
+
164
+ if [ -z "$APPIMAGE_EXIT_AFTER_INSTALL" ] ; then
165
+ trap atexit EXIT
166
+ fi
167
+
168
+ isEulaAccepted=1
169
+
170
+ atexit()
171
+ {
172
+ if [ $isEulaAccepted == 1 ] ; then
173
+ if [ $NUMBER_OF_ARGS -eq 0 ] ; then
174
+ exec "$BIN"
175
+ else
176
+ exec "$BIN" "\${args[@]}"
177
+ fi
178
+ fi
179
+ }
180
+
181
+ error()
182
+ {
183
+ if [ -x /usr/bin/zenity ] ; then
184
+ LD_LIBRARY_PATH="" zenity --error --text "\${1}" 2>/dev/null
185
+ elif [ -x /usr/bin/kdialog ] ; then
186
+ LD_LIBRARY_PATH="" kdialog --msgbox "\${1}" 2>/dev/null
187
+ elif [ -x /usr/bin/Xdialog ] ; then
188
+ LD_LIBRARY_PATH="" Xdialog --msgbox "\${1}" 2>/dev/null
189
+ else
190
+ echo "\${1}"
191
+ fi
192
+ exit 1
193
+ }
194
+
195
+ yesno()
196
+ {
197
+ TITLE=$1
198
+ TEXT=$2
199
+ if [ -x /usr/bin/zenity ] ; then
200
+ LD_LIBRARY_PATH="" zenity --question --title="$TITLE" --text="$TEXT" 2>/dev/null || exit 0
201
+ elif [ -x /usr/bin/kdialog ] ; then
202
+ LD_LIBRARY_PATH="" kdialog --title "$TITLE" --yesno "$TEXT" || exit 0
203
+ elif [ -x /usr/bin/Xdialog ] ; then
204
+ LD_LIBRARY_PATH="" Xdialog --title "$TITLE" --clear --yesno "$TEXT" 10 80 || exit 0
205
+ else
206
+ echo "zenity, kdialog, Xdialog missing. Skipping \${THIS}."
207
+ exit 0
208
+ fi
209
+ }
210
+
211
+ check_dep()
212
+ {
213
+ DEP=$1
214
+ if ! command -v "$DEP" &>/dev/null ; then
215
+ echo "$DEP is missing. Skipping \${THIS}."
216
+ exit 0
217
+ fi
218
+ }
219
+
220
+ if [ -z "$APPIMAGE" ] ; then
221
+ APPIMAGE="$APPDIR/${AppImageTarget_1.APP_RUN_ENTRYPOINT}"
222
+ # not running from within an AppImage; hence using the AppRun for Exec=
223
+ fi
224
+
225
+ ${eulaEnabled
226
+ ? `if [ -z "$APPIMAGE_SILENT_INSTALL" ] ; then
227
+ EULA_MARK_DIR="\${XDG_CONFIG_HOME:-$HOME/.config}/${config.ProductFilename}"
228
+ EULA_MARK_FILE="$EULA_MARK_DIR/eulaAccepted"
229
+ # show EULA only if desktop file doesn't exist
230
+ if [ ! -e "$EULA_MARK_FILE" ] ; then
231
+ if [ -x /usr/bin/zenity ] ; then
232
+ # on cancel simply exits and our trap handler launches app, so, $isEulaAccepted is set here to 0 and then to 1 if EULA accepted
233
+ isEulaAccepted=0
234
+ LD_LIBRARY_PATH="" zenity --text-info --title=${escapeShellString(config.ProductName)} --filename="$APPDIR/${config.EulaFile}" --ok-label=Agree --cancel-label=Disagree ${config.IsHtmlEula ? "--html" : ""}
235
+ elif [ -x /usr/bin/Xdialog ] ; then
236
+ isEulaAccepted=0
237
+ LD_LIBRARY_PATH="" Xdialog --title ${escapeShellString(config.ProductName)} --textbox "$APPDIR/${config.EulaFile}" 30 80 --ok-label Agree --cancel-label Disagree
238
+ elif [ -x /usr/bin/kdialog ] ; then
239
+ # cannot find any option to force Agree/Disagree buttons for kdialog. And official example exactly with OK button https://techbase.kde.org/Development/Tutorials/Shell_Scripting_with_KDE_Dialogs#Example_21._--textbox_dialog_box
240
+ # in any case we pass labels text
241
+ isEulaAccepted=0
242
+ LD_LIBRARY_PATH="" kdialog --textbox "$APPDIR/${config.EulaFile}" --yes-label Agree --cancel-label "Disagree"
243
+ fi
244
+
245
+ case $? in
246
+ 0)
247
+ isEulaAccepted=1
248
+ echo "License accepted"
249
+ mkdir -p "$EULA_MARK_DIR"
250
+ touch "$EULA_MARK_FILE"
251
+ ;;
252
+ 1)
253
+ echo "License not accepted"
254
+ exit 0
255
+ ;;
256
+ -1)
257
+ echo "An unexpected error has occurred."
258
+ isEulaAccepted=1
259
+ ;;
260
+ esac
261
+ fi
262
+ fi`
263
+ : ""}
264
+ `;
265
+ }
266
+ //# sourceMappingURL=appImageUtil.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appImageUtil.js","sourceRoot":"","sources":["../../../src/targets/appimage/appImageUtil.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arch, copyDir, copyFile, exec, exists, InvalidConfigurationError, log } from \"builder-util\"\nimport * as fs from \"fs-extra\"\nimport * as path from \"path\"\nimport { FileAssociation } from \"../../options/FileAssociation\"\nimport { IconInfo } from \"../../platformPackager\"\nimport { getAppImageTools } from \"../../toolsets/linux\"\nimport { copyIcons, copyMimeTypes } from \"./appLauncher\"\nimport { appendBlockmap } from \"../differentialUpdateInfoBuilder\"\nimport { BlockMapDataHolder } from \"builder-util-runtime\"\nimport { APP_RUN_ENTRYPOINT } from \"./AppImageTarget\"\n\ninterface Options {\n productName: string\n productFilename: string\n executableName: string\n desktopEntry: string\n icons: IconInfo[]\n license?: string | null\n fileAssociations: FileAssociation[]\n compression?: \"xz\" | \"lzo\" | \"zstd\"\n}\n\nexport interface AppImageBuilderOptions {\n appDir: string\n stageDir: string\n arch: Arch\n output: string\n options: Options\n}\n\nexport async function buildAppImage(opts: AppImageBuilderOptions): Promise<BlockMapDataHolder> {\n const { stageDir, output, appDir, options, arch } = opts\n\n try {\n await fs.remove(output)\n\n // Write AppRun launcher and related files\n await writeAppLauncherAndRelatedFiles(opts)\n\n const { runtimeLibraries: libraries, runtime, mksquashfs } = await getAppImageTools(arch)\n await copyDir(libraries, path.join(stageDir, \"usr\", \"lib\"))\n\n // Copy app directory to stage\n // mksquashfs doesn't support merging, so we copy the entire app dir\n await copyDir(appDir, stageDir)\n\n const runtimeData = await fs.readFile(runtime)\n\n // Create squashfs with offset for runtime\n const args: string[] = [stageDir, output, \"-offset\", runtimeData.length.toString(), \"-all-root\", \"-noappend\", \"-no-progress\", \"-quiet\", \"-no-xattrs\", \"-no-fragments\"]\n if (options.compression) {\n args.push(\"-comp\", options.compression)\n\n if (options.compression === \"xz\") {\n args.push(\"-Xdict-size\", \"100%\", \"-b\", \"1048576\")\n }\n }\n await exec(mksquashfs, args, {\n cwd: stageDir,\n })\n\n // Write runtime data at the beginning of the file\n await writeRuntimeData(output, runtimeData)\n\n // Make executable\n await fs.chmod(output, 0o755)\n\n // Append blockmap inside try block to ensure cleanup on failure\n const updateInfo = await appendBlockmap(output)\n return updateInfo\n } catch (error) {\n // Clean up partial build on failure\n await fs.remove(output).catch(() => {})\n throw error\n }\n}\n\nasync function writeRuntimeData(filePath: string, runtimeData: Buffer): Promise<void> {\n const fd = await fs.open(filePath, \"r+\")\n try {\n await fs.write(fd, runtimeData, 0, runtimeData.length, 0)\n } finally {\n try {\n await fs.close(fd)\n } catch (closeError: any) {\n // Log but don't throw - preserve original error if any\n log.warn({ message: closeError.message, file: filePath }, `failed to close file descriptor`)\n }\n }\n}\n\n/**\n * Escapes a string for safe use in shell scripts by wrapping in single quotes\n * and escaping any single quotes within the string.\n *\n * This allows strings with spaces, special characters, etc. to be safely used.\n */\nfunction escapeShellString(str: string): string {\n // Escape single quotes by replacing ' with '\\''\n // Then wrap the whole string in single quotes\n return `'${str.replace(/'/g, \"'\\\\''\")}'`\n}\n\n/**\n * Validates that critical executable/filename fields don't contain dangerous characters\n * that could break paths or cause security issues even when escaped.\n */\nfunction validateCriticalPathString(str: string, fieldName: string): void {\n // Only reject characters that would break filesystem paths or cause severe issues\n // Allow quotes, spaces, etc. since they can be escaped\n if (/[`${}|&;<>\\n\\r\\0]/.test(str) || str.includes(\"/\") || str.includes(\"\\\\\")) {\n throw new InvalidConfigurationError(\n `${fieldName} contains characters that cannot be safely used in file paths: ${str}. ` +\n `Please use only alphanumeric characters, hyphens, underscores, dots, spaces, and quotes.`\n )\n }\n}\n\nasync function writeAppLauncherAndRelatedFiles(opts: AppImageBuilderOptions): Promise<void> {\n const {\n stageDir,\n options: { license, executableName, productFilename, productName, desktopEntry },\n } = opts\n\n // Validate only critical path fields for severe path-breaking characters\n // productName and productFilename can contain quotes, spaces, etc. - they'll be escaped\n validateCriticalPathString(executableName, \"executableName\")\n validateCriticalPathString(productFilename, \"productFilename\")\n\n // Write desktop file\n const desktopFileName = `${executableName}.desktop`\n await fs.writeFile(path.join(stageDir, desktopFileName), desktopEntry, { mode: 0o644 })\n await copyIcons(opts)\n\n const templateConfig: AppRunScriptBase = {\n DesktopFileName: desktopFileName,\n ExecutableName: executableName,\n ProductName: productName,\n ProductFilename: productFilename,\n ResourceName: `appimagekit-${executableName}`,\n }\n\n const mimeTypeFile = await copyMimeTypes(opts)\n if (mimeTypeFile) {\n templateConfig.MimeTypeFile = mimeTypeFile\n }\n\n let finalConfig: AppRunScript = templateConfig\n\n // Copy license file if provided\n if (license) {\n // Validate license file exists\n if (!(await exists(license))) {\n throw new InvalidConfigurationError(`License file not found: ${license}`)\n }\n\n const licenseBaseName = path.basename(license)\n const ext = path.extname(license).toLowerCase()\n\n // Validate license filename for path safety\n validateCriticalPathString(licenseBaseName, \"licenseBaseName\")\n\n // Validate extension\n if (![\".txt\", \".html\"].includes(ext)) {\n log.warn({ license, expected: \".txt or .html\" }, `license file has unexpected extension`)\n }\n\n await copyFile(license, path.join(stageDir, licenseBaseName))\n\n finalConfig = {\n ...templateConfig,\n EulaFile: licenseBaseName,\n IsHtmlEula: ext === \".html\",\n }\n }\n\n const appRunContent = generateAppRunScript(finalConfig)\n await fs.writeFile(path.join(stageDir, APP_RUN_ENTRYPOINT), appRunContent, { mode: 0o755 })\n}\n\ntype AppRunScriptBase = {\n ExecutableName: string\n DesktopFileName: string\n ProductFilename: string\n ProductName: string\n ResourceName: string\n MimeTypeFile?: string\n}\n\ntype AppRunScriptWithEula = AppRunScriptBase & {\n EulaFile: string\n IsHtmlEula: boolean\n}\n\ntype AppRunScript = AppRunScriptBase | AppRunScriptWithEula\n\nfunction hasEula(config: AppRunScript): config is AppRunScriptWithEula {\n return \"EulaFile\" in config && typeof config.EulaFile === \"string\"\n}\n\nfunction generateAppRunScript(config: AppRunScript): string {\n const eulaEnabled = hasEula(config)\n\n return `#!/bin/bash\nset -e\n\nTHIS=\"$0\"\n# http://stackoverflow.com/questions/3190818/\nargs=(\"$@\")\nNUMBER_OF_ARGS=\"$#\"\n\nif [ -z \"$APPDIR\" ] ; then\n # Find the AppDir. It is the directory that contains AppRun.\n # This assumes that this script resides inside the AppDir or a subdirectory.\n # If this script is run inside an AppImage, then the AppImage runtime likely has already set $APPDIR\n path=\"$(dirname \"$(readlink -f \"\\${THIS}\")\")\"\n while [[ \"$path\" != \"\" && ! -e \"$path/${APP_RUN_ENTRYPOINT}\" ]]; do\n path=\\${path%/*}\n done\n APPDIR=\"$path\"\nfi\n\nexport PATH=\"\\${APPDIR}:\\${APPDIR}/usr/sbin:\\${PATH}\"\nexport XDG_DATA_DIRS=\"./share/:/usr/share/gnome:/usr/local/share/:/usr/share/:\\${XDG_DATA_DIRS}\"\nexport LD_LIBRARY_PATH=\"\\${APPDIR}/usr/lib:\\${LD_LIBRARY_PATH}\"\nexport XDG_DATA_DIRS=\"\\${APPDIR}\"/usr/share/:\"\\${XDG_DATA_DIRS}\":/usr/share/gnome/:/usr/local/share/:/usr/share/\nexport GSETTINGS_SCHEMA_DIR=\"\\${APPDIR}/usr/share/glib-2.0/schemas:\\${GSETTINGS_SCHEMA_DIR}\"\n\nBIN=\"$APPDIR/${config.ExecutableName}\"\n\nif [ -z \"$APPIMAGE_EXIT_AFTER_INSTALL\" ] ; then\n trap atexit EXIT\nfi\n\nisEulaAccepted=1\n\natexit()\n{\n if [ $isEulaAccepted == 1 ] ; then\n if [ $NUMBER_OF_ARGS -eq 0 ] ; then\n exec \"$BIN\"\n else\n exec \"$BIN\" \"\\${args[@]}\"\n fi\n fi\n}\n\nerror()\n{\n if [ -x /usr/bin/zenity ] ; then\n LD_LIBRARY_PATH=\"\" zenity --error --text \"\\${1}\" 2>/dev/null\n elif [ -x /usr/bin/kdialog ] ; then\n LD_LIBRARY_PATH=\"\" kdialog --msgbox \"\\${1}\" 2>/dev/null\n elif [ -x /usr/bin/Xdialog ] ; then\n LD_LIBRARY_PATH=\"\" Xdialog --msgbox \"\\${1}\" 2>/dev/null\n else\n echo \"\\${1}\"\n fi\n exit 1\n}\n\nyesno()\n{\n TITLE=$1\n TEXT=$2\n if [ -x /usr/bin/zenity ] ; then\n LD_LIBRARY_PATH=\"\" zenity --question --title=\"$TITLE\" --text=\"$TEXT\" 2>/dev/null || exit 0\n elif [ -x /usr/bin/kdialog ] ; then\n LD_LIBRARY_PATH=\"\" kdialog --title \"$TITLE\" --yesno \"$TEXT\" || exit 0\n elif [ -x /usr/bin/Xdialog ] ; then\n LD_LIBRARY_PATH=\"\" Xdialog --title \"$TITLE\" --clear --yesno \"$TEXT\" 10 80 || exit 0\n else\n echo \"zenity, kdialog, Xdialog missing. Skipping \\${THIS}.\"\n exit 0\n fi\n}\n\ncheck_dep()\n{\n DEP=$1\n if ! command -v \"$DEP\" &>/dev/null ; then\n echo \"$DEP is missing. Skipping \\${THIS}.\"\n exit 0\n fi\n}\n\nif [ -z \"$APPIMAGE\" ] ; then\n APPIMAGE=\"$APPDIR/${APP_RUN_ENTRYPOINT}\"\n # not running from within an AppImage; hence using the AppRun for Exec=\nfi\n\n${\n eulaEnabled\n ? `if [ -z \"$APPIMAGE_SILENT_INSTALL\" ] ; then\n EULA_MARK_DIR=\"\\${XDG_CONFIG_HOME:-$HOME/.config}/${config.ProductFilename}\"\n EULA_MARK_FILE=\"$EULA_MARK_DIR/eulaAccepted\"\n # show EULA only if desktop file doesn't exist\n if [ ! -e \"$EULA_MARK_FILE\" ] ; then\n if [ -x /usr/bin/zenity ] ; then\n # on cancel simply exits and our trap handler launches app, so, $isEulaAccepted is set here to 0 and then to 1 if EULA accepted\n isEulaAccepted=0\n LD_LIBRARY_PATH=\"\" zenity --text-info --title=${escapeShellString(config.ProductName)} --filename=\"$APPDIR/${config.EulaFile}\" --ok-label=Agree --cancel-label=Disagree ${config.IsHtmlEula ? \"--html\" : \"\"}\n elif [ -x /usr/bin/Xdialog ] ; then\n isEulaAccepted=0\n LD_LIBRARY_PATH=\"\" Xdialog --title ${escapeShellString(config.ProductName)} --textbox \"$APPDIR/${config.EulaFile}\" 30 80 --ok-label Agree --cancel-label Disagree\n elif [ -x /usr/bin/kdialog ] ; then\n # cannot find any option to force Agree/Disagree buttons for kdialog. And official example exactly with OK button https://techbase.kde.org/Development/Tutorials/Shell_Scripting_with_KDE_Dialogs#Example_21._--textbox_dialog_box\n # in any case we pass labels text\n isEulaAccepted=0\n LD_LIBRARY_PATH=\"\" kdialog --textbox \"$APPDIR/${config.EulaFile}\" --yes-label Agree --cancel-label \"Disagree\"\n fi\n\n case $? in\n 0)\n isEulaAccepted=1\n echo \"License accepted\"\n mkdir -p \"$EULA_MARK_DIR\"\n touch \"$EULA_MARK_FILE\"\n ;;\n 1)\n echo \"License not accepted\"\n exit 0\n ;;\n -1)\n echo \"An unexpected error has occurred.\"\n isEulaAccepted=1\n ;;\n esac\n fi\nfi`\n : \"\"\n}\n`\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import { AppImageBuilderOptions } from "./appImageUtil";
2
+ export declare function copyIcons(options: AppImageBuilderOptions): Promise<void>;
3
+ export declare function copyMimeTypes(options: AppImageBuilderOptions): Promise<string | null>;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.copyIcons = copyIcons;
4
+ exports.copyMimeTypes = copyMimeTypes;
5
+ const path = require("path");
6
+ const fs = require("fs-extra");
7
+ const builder_util_1 = require("builder-util");
8
+ const ICON_DIR_RELATIVE_PATH = "usr/share/icons/hicolor";
9
+ const MIME_TYPE_DIR_RELATIVE_PATH = "usr/share/mime";
10
+ /**
11
+ * Escapes special XML characters to prevent injection
12
+ */
13
+ function xmlEscape(str) {
14
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
15
+ }
16
+ async function copyIcons(options) {
17
+ const { stageDir, options: configuration } = options;
18
+ const iconCommonDir = path.join(stageDir, ICON_DIR_RELATIVE_PATH);
19
+ await fs.ensureDir(iconCommonDir);
20
+ const icons = configuration.icons;
21
+ if (!icons || icons.length === 0) {
22
+ throw new Error("At least one icon is required for AppImage");
23
+ }
24
+ const iconExtWithDot = path.extname(icons[0].file);
25
+ const iconFileName = `${configuration.executableName}${iconExtWithDot}`;
26
+ const maxIconIndex = icons.length - 1;
27
+ const iconInfoList = icons.map(icon => {
28
+ if (path.extname(icon.file) !== iconExtWithDot) {
29
+ throw new Error(`All icons must have the same extension: expected ${iconExtWithDot}, but got ${icon.file}`);
30
+ }
31
+ let iconSizeDir;
32
+ if (iconExtWithDot === ".svg") {
33
+ // SVG icons go in scalable/apps directory per freedesktop icon theme spec
34
+ iconSizeDir = "scalable/apps";
35
+ }
36
+ else {
37
+ iconSizeDir = `${icon.size}x${icon.size}/apps`;
38
+ }
39
+ const iconRelativeToStageFile = path.join(ICON_DIR_RELATIVE_PATH, iconSizeDir, iconFileName);
40
+ const iconDir = path.join(iconCommonDir, iconSizeDir);
41
+ const iconFile = path.join(iconDir, iconFileName);
42
+ return { icon, iconDir, iconFile, iconRelativeToStageFile };
43
+ });
44
+ await Promise.all(iconInfoList.map(async ({ icon, iconDir, iconFile }) => {
45
+ await fs.ensureDir(iconDir);
46
+ await (0, builder_util_1.copyOrLinkFile)(icon.file, iconFile);
47
+ }));
48
+ // Create symlinks for the last (largest) icon
49
+ const { iconRelativeToStageFile } = iconInfoList[maxIconIndex];
50
+ await fs.symlink(iconRelativeToStageFile, path.join(stageDir, iconFileName));
51
+ await fs.symlink(iconRelativeToStageFile, path.join(stageDir, ".DirIcon"));
52
+ }
53
+ async function copyMimeTypes(options) {
54
+ const { stageDir, options: { fileAssociations, productName, executableName }, } = options;
55
+ if (!fileAssociations || fileAssociations.length === 0) {
56
+ return null;
57
+ }
58
+ const mimeTypeParts = [];
59
+ for (const fileAssociation of fileAssociations) {
60
+ if (!fileAssociation.mimeType) {
61
+ continue;
62
+ }
63
+ // XML-escape to prevent injection
64
+ mimeTypeParts.push(`<mime-type type="${xmlEscape(fileAssociation.mimeType)}">`);
65
+ mimeTypeParts.push(` <comment>${xmlEscape(productName)} document</comment>`);
66
+ // Handle extension(s)
67
+ const extensions = Array.isArray(fileAssociation.ext) ? fileAssociation.ext : [fileAssociation.ext];
68
+ for (const ext of extensions) {
69
+ // Validate extension doesn't contain dangerous characters
70
+ if (!/^[a-zA-Z0-9_-]+$/.test(ext)) {
71
+ builder_util_1.log.warn({ extension: ext }, `file extension contains unexpected characters and may not be supported`);
72
+ }
73
+ mimeTypeParts.push(` <glob pattern="*.${xmlEscape(ext)}"/>`);
74
+ }
75
+ mimeTypeParts.push(' <generic-icon name="x-office-document"/>');
76
+ mimeTypeParts.push("</mime-type>");
77
+ }
78
+ // If no mime-types were generated, return null
79
+ if (mimeTypeParts.length === 0) {
80
+ return null;
81
+ }
82
+ const mimeTypeDir = path.join(stageDir, MIME_TYPE_DIR_RELATIVE_PATH);
83
+ const fileName = `${executableName}.xml`;
84
+ const mimeTypeFile = path.join(mimeTypeDir, fileName);
85
+ await fs.ensureDir(mimeTypeDir);
86
+ const xmlContent = ['<?xml version="1.0"?>', '<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">', ...mimeTypeParts, "</mime-info>"].join("\n");
87
+ // Use 0o644 (rw-r--r--) instead of 0o666 to avoid world-writable permissions
88
+ await fs.writeFile(mimeTypeFile, xmlContent, { mode: 0o644 });
89
+ return path.join(MIME_TYPE_DIR_RELATIVE_PATH, fileName);
90
+ }
91
+ //# sourceMappingURL=appLauncher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appLauncher.js","sourceRoot":"","sources":["../../../src/targets/appimage/appLauncher.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from \"path\"\nimport * as fs from \"fs-extra\"\nimport { copyOrLinkFile, log } from \"builder-util\"\nimport { AppImageBuilderOptions } from \"./appImageUtil\"\n\nconst ICON_DIR_RELATIVE_PATH = \"usr/share/icons/hicolor\"\nconst MIME_TYPE_DIR_RELATIVE_PATH = \"usr/share/mime\"\n\n/**\n * Escapes special XML characters to prevent injection\n */\nfunction xmlEscape(str: string): string {\n return str.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\").replace(/'/g, \"&apos;\")\n}\n\nexport async function copyIcons(options: AppImageBuilderOptions): Promise<void> {\n const { stageDir, options: configuration } = options\n const iconCommonDir = path.join(stageDir, ICON_DIR_RELATIVE_PATH)\n\n await fs.ensureDir(iconCommonDir)\n\n const icons = configuration.icons\n if (!icons || icons.length === 0) {\n throw new Error(\"At least one icon is required for AppImage\")\n }\n\n const iconExtWithDot = path.extname(icons[0].file)\n const iconFileName = `${configuration.executableName}${iconExtWithDot}`\n const maxIconIndex = icons.length - 1\n\n const iconInfoList = icons.map(icon => {\n if (path.extname(icon.file) !== iconExtWithDot) {\n throw new Error(`All icons must have the same extension: expected ${iconExtWithDot}, but got ${icon.file}`)\n }\n let iconSizeDir: string\n\n if (iconExtWithDot === \".svg\") {\n // SVG icons go in scalable/apps directory per freedesktop icon theme spec\n iconSizeDir = \"scalable/apps\"\n } else {\n iconSizeDir = `${icon.size}x${icon.size}/apps`\n }\n\n const iconRelativeToStageFile = path.join(ICON_DIR_RELATIVE_PATH, iconSizeDir, iconFileName)\n const iconDir = path.join(iconCommonDir, iconSizeDir)\n const iconFile = path.join(iconDir, iconFileName)\n return { icon, iconDir, iconFile, iconRelativeToStageFile }\n })\n await Promise.all(\n iconInfoList.map(async ({ icon, iconDir, iconFile }) => {\n await fs.ensureDir(iconDir)\n await copyOrLinkFile(icon.file, iconFile)\n })\n )\n // Create symlinks for the last (largest) icon\n const { iconRelativeToStageFile } = iconInfoList[maxIconIndex]\n await fs.symlink(iconRelativeToStageFile, path.join(stageDir, iconFileName))\n await fs.symlink(iconRelativeToStageFile, path.join(stageDir, \".DirIcon\"))\n}\n\nexport async function copyMimeTypes(options: AppImageBuilderOptions): Promise<string | null> {\n const {\n stageDir,\n options: { fileAssociations, productName, executableName },\n } = options\n\n if (!fileAssociations || fileAssociations.length === 0) {\n return null\n }\n\n const mimeTypeParts: string[] = []\n\n for (const fileAssociation of fileAssociations) {\n if (!fileAssociation.mimeType) {\n continue\n }\n\n // XML-escape to prevent injection\n mimeTypeParts.push(`<mime-type type=\"${xmlEscape(fileAssociation.mimeType)}\">`)\n mimeTypeParts.push(` <comment>${xmlEscape(productName)} document</comment>`)\n\n // Handle extension(s)\n const extensions = Array.isArray(fileAssociation.ext) ? fileAssociation.ext : [fileAssociation.ext]\n\n for (const ext of extensions) {\n // Validate extension doesn't contain dangerous characters\n if (!/^[a-zA-Z0-9_-]+$/.test(ext)) {\n log.warn({ extension: ext }, `file extension contains unexpected characters and may not be supported`)\n }\n mimeTypeParts.push(` <glob pattern=\"*.${xmlEscape(ext)}\"/>`)\n }\n\n mimeTypeParts.push(' <generic-icon name=\"x-office-document\"/>')\n mimeTypeParts.push(\"</mime-type>\")\n }\n\n // If no mime-types were generated, return null\n if (mimeTypeParts.length === 0) {\n return null\n }\n\n const mimeTypeDir = path.join(stageDir, MIME_TYPE_DIR_RELATIVE_PATH)\n const fileName = `${executableName}.xml`\n const mimeTypeFile = path.join(mimeTypeDir, fileName)\n\n await fs.ensureDir(mimeTypeDir)\n\n const xmlContent = ['<?xml version=\"1.0\"?>', '<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">', ...mimeTypeParts, \"</mime-info>\"].join(\"\\n\")\n\n // Use 0o644 (rw-r--r--) instead of 0o666 to avoid world-writable permissions\n await fs.writeFile(mimeTypeFile, xmlContent, { mode: 0o644 })\n\n return path.join(MIME_TYPE_DIR_RELATIVE_PATH, fileName)\n}\n"]}
@@ -8,7 +8,7 @@ const builder_util_1 = require("builder-util");
8
8
  const fs_extra_1 = require("fs-extra");
9
9
  const path = require("path");
10
10
  const tar_1 = require("tar");
11
- const tools_1 = require("./tools");
11
+ const linux_1 = require("../toolsets/linux");
12
12
  /** @internal */
13
13
  async function tar(compression, format, outFile, dirToArchive, isMacApp, tempDirManager) {
14
14
  const tarFile = await tempDirManager.getTempFile({ suffix: ".tar" });
@@ -33,7 +33,7 @@ async function tar(compression, format, outFile, dirToArchive, isMacApp, tempDir
33
33
  // noinspection SpellCheckingInspection
34
34
  let lzipPath = "lzip";
35
35
  if (process.platform === "darwin") {
36
- lzipPath = path.join(await (0, tools_1.getLinuxToolsPath)(), "bin", lzipPath);
36
+ lzipPath = path.join(await (0, linux_1.getLinuxToolsPath)(), "bin", lzipPath);
37
37
  }
38
38
  await (0, builder_util_1.exec)(lzipPath, [compression === "store" ? "-1" : "-9", "--keep" /* keep (don't delete) input files */, tarFile]);
39
39
  // bloody lzip creates file in the same dir where input file with postfix `.lz`, option --output doesn't work