@gtkx/cli 1.1.0 → 1.2.1
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/builder.d.ts.map +1 -1
- package/dist/builder.js +2 -0
- package/dist/builder.js.map +1 -1
- package/dist/codegen/report.d.ts.map +1 -1
- package/dist/codegen/report.js +3 -0
- package/dist/codegen/report.js.map +1 -1
- package/dist/codegen/run-codegen.d.ts +1 -0
- package/dist/codegen/run-codegen.d.ts.map +1 -1
- package/dist/codegen/run-codegen.js +7 -1
- package/dist/codegen/run-codegen.js.map +1 -1
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +1 -0
- package/dist/commands/docs.js.map +1 -1
- package/dist/deploy/freedesktop/copyright.d.ts +2 -2
- package/dist/deploy/freedesktop/copyright.d.ts.map +1 -1
- package/dist/deploy/freedesktop/copyright.js +105 -14
- package/dist/deploy/freedesktop/copyright.js.map +1 -1
- package/dist/deploy/install-hints.d.ts.map +1 -1
- package/dist/deploy/install-hints.js +7 -4
- package/dist/deploy/install-hints.js.map +1 -1
- package/dist/deploy/node-runtime/download.d.ts +5 -1
- package/dist/deploy/node-runtime/download.d.ts.map +1 -1
- package/dist/deploy/node-runtime/download.js +6 -3
- package/dist/deploy/node-runtime/download.js.map +1 -1
- package/dist/deploy/node-runtime/index.d.ts +2 -1
- package/dist/deploy/node-runtime/index.d.ts.map +1 -1
- package/dist/deploy/node-runtime/index.js +10 -9
- package/dist/deploy/node-runtime/index.js.map +1 -1
- package/dist/deploy/node-runtime/license.d.ts +4 -0
- package/dist/deploy/node-runtime/license.d.ts.map +1 -0
- package/dist/deploy/node-runtime/license.js +19 -0
- package/dist/deploy/node-runtime/license.js.map +1 -0
- package/dist/deploy/notices/collect.d.ts +8 -0
- package/dist/deploy/notices/collect.d.ts.map +1 -0
- package/dist/deploy/notices/collect.js +17 -0
- package/dist/deploy/notices/collect.js.map +1 -0
- package/dist/deploy/notices/dependencies.d.ts +5 -0
- package/dist/deploy/notices/dependencies.d.ts.map +1 -0
- package/dist/deploy/notices/dependencies.js +39 -0
- package/dist/deploy/notices/dependencies.js.map +1 -0
- package/dist/deploy/notices/gtkx.d.ts +6 -0
- package/dist/deploy/notices/gtkx.d.ts.map +1 -0
- package/dist/deploy/notices/gtkx.js +68 -0
- package/dist/deploy/notices/gtkx.js.map +1 -0
- package/dist/deploy/notices/libraries.d.ts +4 -0
- package/dist/deploy/notices/libraries.d.ts.map +1 -0
- package/dist/deploy/notices/libraries.js +60 -0
- package/dist/deploy/notices/libraries.js.map +1 -0
- package/dist/deploy/notices/node-runtime.d.ts +4 -0
- package/dist/deploy/notices/node-runtime.d.ts.map +1 -0
- package/dist/deploy/notices/node-runtime.js +78 -0
- package/dist/deploy/notices/node-runtime.js.map +1 -0
- package/dist/deploy/notices/packages.d.ts +13 -0
- package/dist/deploy/notices/packages.d.ts.map +1 -0
- package/dist/deploy/notices/packages.js +115 -0
- package/dist/deploy/notices/packages.js.map +1 -0
- package/dist/deploy/notices/render.d.ts +4 -0
- package/dist/deploy/notices/render.d.ts.map +1 -0
- package/dist/deploy/notices/render.js +41 -0
- package/dist/deploy/notices/render.js.map +1 -0
- package/dist/deploy/notices/text.d.ts +5 -0
- package/dist/deploy/notices/text.d.ts.map +1 -0
- package/dist/deploy/notices/text.js +34 -0
- package/dist/deploy/notices/text.js.map +1 -0
- package/dist/deploy/payload/copy-tree.d.ts +2 -1
- package/dist/deploy/payload/copy-tree.d.ts.map +1 -1
- package/dist/deploy/payload/copy-tree.js +3 -1
- package/dist/deploy/payload/copy-tree.js.map +1 -1
- package/dist/deploy/payload/stage.d.ts +7 -3
- package/dist/deploy/payload/stage.d.ts.map +1 -1
- package/dist/deploy/payload/stage.js +25 -14
- package/dist/deploy/payload/stage.js.map +1 -1
- package/dist/deploy/run-deploy.d.ts.map +1 -1
- package/dist/deploy/run-deploy.js +28 -7
- package/dist/deploy/run-deploy.js.map +1 -1
- package/dist/deploy/settings/extra-files.d.ts +4 -0
- package/dist/deploy/settings/extra-files.d.ts.map +1 -0
- package/dist/deploy/settings/extra-files.js +11 -0
- package/dist/deploy/settings/extra-files.js.map +1 -0
- package/dist/deploy/settings/index.d.ts.map +1 -1
- package/dist/deploy/settings/index.js +2 -1
- package/dist/deploy/settings/index.js.map +1 -1
- package/dist/deploy/settings/package-manifest.d.ts +2 -0
- package/dist/deploy/settings/package-manifest.d.ts.map +1 -1
- package/dist/deploy/settings/package-manifest.js +8 -0
- package/dist/deploy/settings/package-manifest.js.map +1 -1
- package/dist/deploy/settings/paths.d.ts +2 -1
- package/dist/deploy/settings/paths.d.ts.map +1 -1
- package/dist/deploy/settings/paths.js +1 -1
- package/dist/deploy/settings/paths.js.map +1 -1
- package/dist/deploy/targets/flatpak-manifest.d.ts +3 -1
- package/dist/deploy/targets/flatpak-manifest.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak-manifest.js +50 -4
- package/dist/deploy/targets/flatpak-manifest.js.map +1 -1
- package/dist/deploy/targets/flatpak-pnpm.d.ts +15 -0
- package/dist/deploy/targets/flatpak-pnpm.d.ts.map +1 -0
- package/dist/deploy/targets/flatpak-pnpm.js +69 -0
- package/dist/deploy/targets/flatpak-pnpm.js.map +1 -0
- package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak-source.js +70 -7
- package/dist/deploy/targets/flatpak-source.js.map +1 -1
- package/dist/deploy/targets/flatpak-sources.d.ts +5 -3
- package/dist/deploy/targets/flatpak-sources.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak-sources.js +23 -9
- package/dist/deploy/targets/flatpak-sources.js.map +1 -1
- package/dist/deploy/targets/flatpak.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak.js +3 -2
- package/dist/deploy/targets/flatpak.js.map +1 -1
- package/dist/deploy/tools.d.ts +3 -1
- package/dist/deploy/tools.d.ts.map +1 -1
- package/dist/deploy/tools.js +17 -3
- package/dist/deploy/tools.js.map +1 -1
- package/dist/deploy/types.d.ts +22 -2
- package/dist/deploy/types.d.ts.map +1 -1
- package/dist/deploy/types.js.map +1 -1
- package/dist/vite-plugins/bundled-packages.d.ts +5 -0
- package/dist/vite-plugins/bundled-packages.d.ts.map +1 -0
- package/dist/vite-plugins/bundled-packages.js +63 -0
- package/dist/vite-plugins/bundled-packages.js.map +1 -0
- package/package.json +9 -9
- package/src/builder.ts +2 -0
- package/src/codegen/report.ts +4 -0
- package/src/codegen/run-codegen.ts +11 -1
- package/src/commands/docs.ts +1 -0
- package/src/deploy/freedesktop/copyright.ts +153 -18
- package/src/deploy/install-hints.ts +9 -6
- package/src/deploy/node-runtime/download.ts +13 -4
- package/src/deploy/node-runtime/index.ts +17 -11
- package/src/deploy/node-runtime/license.ts +24 -0
- package/src/deploy/notices/collect.ts +26 -0
- package/src/deploy/notices/dependencies.ts +53 -0
- package/src/deploy/notices/gtkx.ts +92 -0
- package/src/deploy/notices/libraries.ts +81 -0
- package/src/deploy/notices/node-runtime.ts +101 -0
- package/src/deploy/notices/packages.ts +168 -0
- package/src/deploy/notices/render.ts +52 -0
- package/src/deploy/notices/text.ts +42 -0
- package/src/deploy/payload/copy-tree.ts +5 -1
- package/src/deploy/payload/stage.ts +40 -16
- package/src/deploy/run-deploy.ts +34 -6
- package/src/deploy/settings/extra-files.ts +21 -0
- package/src/deploy/settings/index.ts +2 -1
- package/src/deploy/settings/package-manifest.ts +12 -0
- package/src/deploy/settings/paths.ts +1 -1
- package/src/deploy/targets/flatpak-manifest.ts +69 -4
- package/src/deploy/targets/flatpak-pnpm.ts +115 -0
- package/src/deploy/targets/flatpak-source.ts +105 -6
- package/src/deploy/targets/flatpak-sources.ts +36 -9
- package/src/deploy/targets/flatpak.ts +3 -2
- package/src/deploy/tools.ts +23 -2
- package/src/deploy/types.ts +27 -1
- package/src/vite-plugins/bundled-packages.ts +95 -0
- package/LICENSE +0 -373
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const LICENSE_FILE = /^(?:licen[cs]e|copying|notice)(?:[-._].*)?$/i;
|
|
5
|
+
const COPYRIGHT_LINE = /^(?:Copyright\b|©)/;
|
|
6
|
+
const MAX_COPYRIGHT_LINES = 20;
|
|
7
|
+
const MORE_COPYRIGHT = "and the other holders named in the license text";
|
|
8
|
+
|
|
9
|
+
const readLicenseText = (path: string): string | null => {
|
|
10
|
+
try {
|
|
11
|
+
return readFileSync(path, "utf8").trimEnd();
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const licenseFilesIn = (dir: string): string[] =>
|
|
18
|
+
readdirSync(dir, { withFileTypes: true })
|
|
19
|
+
.filter((entry) => entry.isFile() && LICENSE_FILE.test(entry.name))
|
|
20
|
+
.map((entry) => entry.name)
|
|
21
|
+
.toSorted((left, right) => left.localeCompare(right));
|
|
22
|
+
|
|
23
|
+
const licenseTextIn = (dir: string): string | null => {
|
|
24
|
+
const texts = licenseFilesIn(dir)
|
|
25
|
+
.map((name) => readLicenseText(join(dir, name)))
|
|
26
|
+
.filter((text) => text !== null);
|
|
27
|
+
|
|
28
|
+
return texts.length === 0 ? null : texts.join("\n\n");
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const copyrightLines = (text: string | null): string[] => {
|
|
32
|
+
const found = (text ?? "")
|
|
33
|
+
.split("\n")
|
|
34
|
+
.map((line) => line.trim())
|
|
35
|
+
.filter((line) => COPYRIGHT_LINE.test(line));
|
|
36
|
+
|
|
37
|
+
const unique = [...new Set(found)];
|
|
38
|
+
|
|
39
|
+
return unique.length > MAX_COPYRIGHT_LINES ? [...unique.slice(0, MAX_COPYRIGHT_LINES), MORE_COPYRIGHT] : unique;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export { copyrightLines, licenseTextIn, readLicenseText };
|
|
@@ -7,6 +7,7 @@ const EXECUTABLE_MODE = 0o755;
|
|
|
7
7
|
const READABLE_MODE = 0o644;
|
|
8
8
|
const EXECUTABLE_SUFFIXES = [".node", ".so"];
|
|
9
9
|
const MODE_MASK = 0o7777;
|
|
10
|
+
const EXECUTE_MASK = 0o111;
|
|
10
11
|
|
|
11
12
|
const modeFor = (rel: string): number => {
|
|
12
13
|
const name = rel.split("/").at(-1) ?? rel;
|
|
@@ -44,8 +45,11 @@ const writeInto = (root: string, rel: string, contents: string, mode = READABLE_
|
|
|
44
45
|
|
|
45
46
|
const sourceMode = (path: string): number => statSync(path).mode & MODE_MASK;
|
|
46
47
|
|
|
48
|
+
const executableModeFor = (path: string): number =>
|
|
49
|
+
(sourceMode(path) & EXECUTE_MASK) === 0 ? READABLE_MODE : EXECUTABLE_MODE;
|
|
50
|
+
|
|
47
51
|
const copyTree = (root: string, relBase: string, sourceDir: string): StagedFile[] =>
|
|
48
52
|
listFilesRecursive(sourceDir)
|
|
49
53
|
.map((file) => copyInto(root, join(relBase, file.rel), file.absPath, sourceMode(file.absPath)));
|
|
50
54
|
|
|
51
|
-
export { copyInto, copyTree, EXECUTABLE_MODE, writeInto };
|
|
55
|
+
export { copyInto, copyTree, EXECUTABLE_MODE, executableModeFor, writeInto };
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { sortStringsBy } from "@gtkx/utils";
|
|
2
2
|
import { existsSync, rmSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
-
import type { DeploySettings, DeployTargetName, NodeRuntime, StagedFile } from "../types.js";
|
|
4
|
+
import type { DeploySettings, DeployTargetName, NodeRuntime, NoticeSection, StagedFile } from "../types.js";
|
|
5
5
|
import { listFilesRecursive } from "../../internal/list-files.js";
|
|
6
|
+
import { BUNDLED_PACKAGES_FILENAME } from "../../vite-plugins/bundled-packages.js";
|
|
6
7
|
import { BUNDLE_FILENAME } from "../../vite-plugins/esm-extension.js";
|
|
7
8
|
import { renderCopyright } from "../freedesktop/copyright.js";
|
|
8
9
|
import { renderDbusService } from "../freedesktop/dbus-service.js";
|
|
9
10
|
import { renderDesktopEntry } from "../freedesktop/desktop-entry.js";
|
|
10
|
-
import {
|
|
11
|
+
import { renderNotices } from "../notices/render.js";
|
|
12
|
+
import { copyInto, EXECUTABLE_MODE, executableModeFor, writeInto } from "./copy-tree.js";
|
|
11
13
|
import { stageIcons } from "./icons.js";
|
|
12
14
|
import { NODE_FILENAME, renderLauncher } from "./launcher.js";
|
|
13
15
|
import { stageSchemas } from "./schemas.js";
|
|
@@ -25,6 +27,8 @@ type StagedMetadata = {
|
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
const DIST_ICONS_DIR = "icons";
|
|
30
|
+
const NOTICES_FILENAME = "THIRD-PARTY-NOTICES";
|
|
31
|
+
const NODE_LICENSE_FILENAME = "LICENSE";
|
|
28
32
|
const SHARE_APPLICATIONS = "share/applications";
|
|
29
33
|
const SHARE_METAINFO = "share/metainfo";
|
|
30
34
|
const SHARE_MIME_PACKAGES = "share/mime/packages";
|
|
@@ -40,7 +44,14 @@ const PREFIX_FOR: Record<DeployTargetName, string> = {
|
|
|
40
44
|
};
|
|
41
45
|
|
|
42
46
|
const libDirFor = (settings: DeploySettings): string => `lib/${settings.binaryName}`;
|
|
47
|
+
const licenseDirFor = (settings: DeploySettings): string => `${SHARE_LICENSES}/${settings.binaryName}`;
|
|
48
|
+
const licenseDestination = (settings: DeploySettings): string => `${licenseDirFor(settings)}/LICENSE`;
|
|
49
|
+
const noticesDestination = (settings: DeploySettings): string => `${licenseDirFor(settings)}/${NOTICES_FILENAME}`;
|
|
43
50
|
const isIconAsset = (rel: string): boolean => rel === DIST_ICONS_DIR || rel.startsWith(`${DIST_ICONS_DIR}/`);
|
|
51
|
+
const isBuildMetadata = (rel: string): boolean => rel === BUNDLED_PACKAGES_FILENAME;
|
|
52
|
+
|
|
53
|
+
const nodeLicenseDestination = (settings: DeploySettings): string =>
|
|
54
|
+
`${licenseDirFor(settings)}/${NODE_FILENAME}/${NODE_LICENSE_FILENAME}`;
|
|
44
55
|
|
|
45
56
|
const stageRuntimeFiles = (settings: DeploySettings, root: string): StagedFile[] => {
|
|
46
57
|
const dist = settings.paths.dist;
|
|
@@ -50,7 +61,7 @@ const stageRuntimeFiles = (settings: DeploySettings, root: string): StagedFile[]
|
|
|
50
61
|
}
|
|
51
62
|
|
|
52
63
|
return listFilesRecursive(dist)
|
|
53
|
-
.filter((file) => !isIconAsset(file.rel))
|
|
64
|
+
.filter((file) => !isIconAsset(file.rel) && !isBuildMetadata(file.rel))
|
|
54
65
|
.map((file) => copyInto(root, join(libDirFor(settings), file.rel), file.absPath));
|
|
55
66
|
};
|
|
56
67
|
|
|
@@ -66,8 +77,11 @@ const stageMetadata = (settings: DeploySettings, root: string, metadata: StagedM
|
|
|
66
77
|
];
|
|
67
78
|
|
|
68
79
|
const stageExtraFiles = (settings: DeploySettings, root: string): StagedFile[] =>
|
|
69
|
-
|
|
70
|
-
|
|
80
|
+
settings.extraFiles.map((file) => {
|
|
81
|
+
const source = resolve(settings.paths.root, file.source);
|
|
82
|
+
|
|
83
|
+
return copyInto(root, file.destination, source, file.mode ?? executableModeFor(source));
|
|
84
|
+
});
|
|
71
85
|
|
|
72
86
|
const byRelativePath = (files: StagedFile[]): StagedFile[] => {
|
|
73
87
|
const latest: Map<string, StagedFile> = new Map();
|
|
@@ -114,7 +128,7 @@ const stageActivation = (settings: DeploySettings, root: string, target: DeployT
|
|
|
114
128
|
)];
|
|
115
129
|
};
|
|
116
130
|
|
|
117
|
-
const stageOverlay = (settings: DeploySettings, target: DeployTargetName): StagedFile[] => {
|
|
131
|
+
const stageOverlay = (settings: DeploySettings, target: DeployTargetName, notices: NoticeSection[]): StagedFile[] => {
|
|
118
132
|
const root = join(settings.paths.overlay, target);
|
|
119
133
|
rmSync(root, { recursive: true, force: true, maxRetries: 5 });
|
|
120
134
|
const licenseFile = settings.paths.licenseFile;
|
|
@@ -123,23 +137,33 @@ const stageOverlay = (settings: DeploySettings, target: DeployTargetName): Stage
|
|
|
123
137
|
if (target === "deb") {
|
|
124
138
|
return [
|
|
125
139
|
...activation,
|
|
126
|
-
writeInto(root, join(SHARE_DOC, settings.binaryName, "copyright"), renderCopyright(settings)),
|
|
140
|
+
writeInto(root, join(SHARE_DOC, settings.binaryName, "copyright"), renderCopyright(settings, notices)),
|
|
127
141
|
];
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
return [
|
|
131
145
|
...activation,
|
|
132
|
-
...(licenseFile === null
|
|
133
|
-
|
|
134
|
-
: [copyInto(root, join(SHARE_LICENSES, settings.binaryName, "LICENSE"), licenseFile)]),
|
|
146
|
+
...(licenseFile === null ? [] : [copyInto(root, licenseDestination(settings), licenseFile)]),
|
|
147
|
+
writeInto(root, noticesDestination(settings), renderNotices(settings, notices)),
|
|
135
148
|
];
|
|
136
149
|
};
|
|
137
150
|
|
|
138
|
-
const stageOverlays = (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
151
|
+
const stageOverlays = (
|
|
152
|
+
settings: DeploySettings,
|
|
153
|
+
notices: NoticeSection[],
|
|
154
|
+
): Record<DeployTargetName, StagedFile[]> => ({
|
|
155
|
+
appimage: stageOverlay(settings, "appimage", notices),
|
|
156
|
+
deb: stageOverlay(settings, "deb", notices),
|
|
157
|
+
flatpak: stageOverlay(settings, "flatpak", notices),
|
|
158
|
+
rpm: stageOverlay(settings, "rpm", notices),
|
|
143
159
|
});
|
|
144
160
|
|
|
145
|
-
export {
|
|
161
|
+
export {
|
|
162
|
+
licenseDestination,
|
|
163
|
+
nodeLicenseDestination,
|
|
164
|
+
NOTICES_FILENAME,
|
|
165
|
+
noticesDestination,
|
|
166
|
+
type StagedMetadata,
|
|
167
|
+
stageOverlays,
|
|
168
|
+
stagePayload,
|
|
169
|
+
};
|
package/src/deploy/run-deploy.ts
CHANGED
|
@@ -17,16 +17,20 @@ import { renderMetainfo } from "./freedesktop/metainfo.js";
|
|
|
17
17
|
import { renderMimePackage } from "./freedesktop/mime-package.js";
|
|
18
18
|
import { validateDesktopEntry, validateMetainfo } from "./freedesktop/validate.js";
|
|
19
19
|
import { resolveNodeRuntime } from "./node-runtime/index.js";
|
|
20
|
+
import { collectNotices } from "./notices/collect.js";
|
|
20
21
|
import { type StagedMetadata, stageOverlays, stagePayload } from "./payload/stage.js";
|
|
21
22
|
import { DEFAULT_TARGETS, parseTargetList, targetsFor } from "./registry.js";
|
|
22
23
|
import { resolveDeploySettings } from "./settings/index.js";
|
|
23
24
|
import { readPackageManifest } from "./settings/package-manifest.js";
|
|
24
25
|
import { missingDeployError } from "./settings/starter.js";
|
|
26
|
+
import { finishArgsFor, hasDisplaySocket } from "./targets/flatpak-manifest.js";
|
|
27
|
+
import { detectPackageManager } from "./targets/flatpak-sources.js";
|
|
25
28
|
import {
|
|
26
29
|
APPSTREAMCLI,
|
|
27
30
|
assertTools,
|
|
28
31
|
DESKTOP_FILE_VALIDATE,
|
|
29
32
|
FLATPAK_NODE_GENERATOR,
|
|
33
|
+
FLATPAK_NODE_GENERATOR_PNPM,
|
|
30
34
|
probeTools,
|
|
31
35
|
STRIP,
|
|
32
36
|
TAR,
|
|
@@ -76,10 +80,19 @@ const validateMetadata = (settings: DeploySettings, metadata: StagedMetadata, ar
|
|
|
76
80
|
info("Validated the desktop entry and the metainfo");
|
|
77
81
|
};
|
|
78
82
|
|
|
79
|
-
const
|
|
80
|
-
|
|
83
|
+
const warnMissingDisplay = (settings: DeploySettings, finishArgs: string[]): void => {
|
|
84
|
+
if (hasDisplaySocket(finishArgs)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
warn(
|
|
89
|
+
`The flatpak permissions grant no display socket, so ${settings.name} will start without a window. ` +
|
|
90
|
+
"Grant one through `deploy.flatpak.finishArgs`, such as `--socket=wayland`.",
|
|
91
|
+
);
|
|
92
|
+
};
|
|
81
93
|
|
|
82
|
-
|
|
94
|
+
const warnMissingNetwork = (settings: DeploySettings, finishArgs: string[]): void => {
|
|
95
|
+
if (settings.deploy.flatpak?.finishArgs === undefined || finishArgs.includes(NETWORK_ARG)) {
|
|
83
96
|
return;
|
|
84
97
|
}
|
|
85
98
|
|
|
@@ -90,10 +103,24 @@ const warnMissingNetwork = (settings: DeploySettings): void => {
|
|
|
90
103
|
warn(`This app declares ${WEBKIT_LIBRARY} but its flatpak permissions omit ${NETWORK_ARG}, so pages will not load`);
|
|
91
104
|
};
|
|
92
105
|
|
|
106
|
+
const warnFlatpakPermissions = (targets: DeployTarget[], settings: DeploySettings): void => {
|
|
107
|
+
if (targets.every((target) => target.name !== "flatpak")) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const finishArgs = finishArgsFor(settings);
|
|
112
|
+
warnMissingDisplay(settings, finishArgs);
|
|
113
|
+
warnMissingNetwork(settings, finishArgs);
|
|
114
|
+
};
|
|
115
|
+
|
|
93
116
|
const sourceModeTools = (targets: DeployTarget[], settings: DeploySettings): DeployTool[] => {
|
|
94
117
|
const isFlatpakSource = settings.deploy.flatpak?.mode === "source";
|
|
95
118
|
|
|
96
|
-
|
|
119
|
+
if (!isFlatpakSource || targets.every((target) => target.name !== "flatpak")) {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return detectPackageManager(settings) === "pnpm" ? [FLATPAK_NODE_GENERATOR_PNPM] : [FLATPAK_NODE_GENERATOR];
|
|
97
124
|
};
|
|
98
125
|
|
|
99
126
|
const isNodeRequired = (targets: DeployTarget[], settings: DeploySettings): boolean =>
|
|
@@ -119,7 +146,7 @@ const preflight = (targets: DeployTarget[], settings: DeploySettings, shouldPrin
|
|
|
119
146
|
const report = probeTools([...required, ...packagerTools]);
|
|
120
147
|
assertTools(report);
|
|
121
148
|
warnMissingOptional(report);
|
|
122
|
-
|
|
149
|
+
warnFlatpakPermissions(targets, settings);
|
|
123
150
|
};
|
|
124
151
|
|
|
125
152
|
const resolveTargetNames = (options: DeployOptions, settings: DeploySettings): string[] => {
|
|
@@ -159,8 +186,9 @@ const buildPayload = async (
|
|
|
159
186
|
|
|
160
187
|
const stage = stagePayload({ settings, node, metadata });
|
|
161
188
|
info(`Staged ${String(stage.length)} files into ${displayPath(settings, settings.paths.stage)}`);
|
|
189
|
+
const notices = collectNotices({ settings, node });
|
|
162
190
|
|
|
163
|
-
return { settings, node, stage, overlays: stageOverlays(settings) };
|
|
191
|
+
return { settings, node, stage, notices, overlays: stageOverlays(settings, notices) };
|
|
164
192
|
};
|
|
165
193
|
|
|
166
194
|
const renderTargetManifests = (
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DeployConfig, DeployExtraFile } from "../types.js";
|
|
2
|
+
|
|
3
|
+
type ExtraFiles = NonNullable<DeployConfig["extraFiles"]>;
|
|
4
|
+
type ExtraFileEntry = NonNullable<ExtraFiles[string]>;
|
|
5
|
+
|
|
6
|
+
const OCTAL_RADIX = 8;
|
|
7
|
+
|
|
8
|
+
const resolveExtraFile = (destination: string, entry: ExtraFileEntry): DeployExtraFile => {
|
|
9
|
+
if (typeof entry === "string") {
|
|
10
|
+
return { destination, source: entry, mode: null };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const mode = entry.mode === undefined ? null : Number.parseInt(entry.mode, OCTAL_RADIX);
|
|
14
|
+
|
|
15
|
+
return { destination, source: entry.source, mode };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const resolveExtraFiles = (deploy: DeployConfig): DeployExtraFile[] =>
|
|
19
|
+
Object.entries(deploy.extraFiles ?? {}).map(([destination, entry]) => resolveExtraFile(destination, entry));
|
|
20
|
+
|
|
21
|
+
export { resolveExtraFiles };
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
} from "../types.js";
|
|
10
10
|
import { resolveArch } from "./arch.js";
|
|
11
11
|
import { resolveExecToken, resolveFileAssociations, resolveMimeTypes } from "./exec.js";
|
|
12
|
+
import { resolveExtraFiles } from "./extra-files.js";
|
|
12
13
|
import {
|
|
13
14
|
type IdentityRequest,
|
|
14
15
|
resolveBinaryName,
|
|
@@ -177,7 +178,7 @@ const resolveDeploySettings = (request: SettingsRequest): DeploySettings => {
|
|
|
177
178
|
...identitySlice(core),
|
|
178
179
|
...metadataSlice(request.root, core),
|
|
179
180
|
...desktopSlice(deploy),
|
|
180
|
-
extraFiles: deploy
|
|
181
|
+
extraFiles: resolveExtraFiles(deploy),
|
|
181
182
|
versions: core.versions,
|
|
182
183
|
arch: resolveArch(),
|
|
183
184
|
paths: resolvePaths({ root: request.root, deploy, outDirOverride: request.outDirOverride }),
|
|
@@ -13,6 +13,8 @@ type PackageManifest = {
|
|
|
13
13
|
description: string | null;
|
|
14
14
|
license: string | null;
|
|
15
15
|
homepage: string | null;
|
|
16
|
+
repository: string | null;
|
|
17
|
+
packageManager: string | null;
|
|
16
18
|
author: ManifestAuthor;
|
|
17
19
|
};
|
|
18
20
|
|
|
@@ -28,6 +30,14 @@ const parseAuthorString = (author: string): ManifestAuthor => {
|
|
|
28
30
|
return { name: optionalString(groups?.name), email: optionalString(groups?.email) };
|
|
29
31
|
};
|
|
30
32
|
|
|
33
|
+
const parseRepository = (repository: unknown): string | null => {
|
|
34
|
+
if (isRecord(repository)) {
|
|
35
|
+
return optionalString(repository.url);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return optionalString(repository);
|
|
39
|
+
};
|
|
40
|
+
|
|
31
41
|
const parseAuthor = (author: unknown): ManifestAuthor => {
|
|
32
42
|
if (typeof author === "string") {
|
|
33
43
|
return parseAuthorString(author);
|
|
@@ -59,6 +69,8 @@ const readPackageManifest = (root: string): PackageManifest => {
|
|
|
59
69
|
description: optionalString(manifest.description),
|
|
60
70
|
license: optionalString(manifest.license),
|
|
61
71
|
homepage: optionalString(manifest.homepage),
|
|
72
|
+
repository: parseRepository(manifest.repository),
|
|
73
|
+
packageManager: optionalString(manifest.packageManager),
|
|
62
74
|
author: parseAuthor(manifest.author),
|
|
63
75
|
};
|
|
64
76
|
};
|
|
@@ -12,10 +12,76 @@ const DEFAULT_RUNTIME_VERSION = "50";
|
|
|
12
12
|
const DEFAULT_SDK = "org.gnome.Sdk";
|
|
13
13
|
const DEFAULT_FINISH_ARGS = ["--share=ipc", "--socket=wayland", "--socket=fallback-x11", "--device=dri"];
|
|
14
14
|
const DEFAULT_CLEANUP = ["/include", "/share/pkgconfig", "*.la", "*.a"];
|
|
15
|
+
const DISPLAY_SOCKETS = ["fallback-x11", "wayland", "x11"];
|
|
16
|
+
const SOCKET_ARG = "--socket=";
|
|
17
|
+
const NO_SOCKET_ARG = "--nosocket=";
|
|
18
|
+
|
|
19
|
+
const NEGATED_ARGS: [string, string][] = [
|
|
20
|
+
["--device=", "--nodevice="],
|
|
21
|
+
["--share=", "--unshare="],
|
|
22
|
+
[SOCKET_ARG, NO_SOCKET_ARG],
|
|
23
|
+
];
|
|
15
24
|
|
|
16
25
|
const branchFor = (settings: DeploySettings): string => settings.deploy.flatpak?.branch ?? DEFAULT_BRANCH;
|
|
17
26
|
const isSourceMode = (settings: DeploySettings): boolean => settings.deploy.flatpak?.mode === "source";
|
|
18
27
|
|
|
28
|
+
const negationFor = (arg: string): string | null => {
|
|
29
|
+
const negated = NEGATED_ARGS.find(([prefix]) => arg.startsWith(prefix));
|
|
30
|
+
|
|
31
|
+
if (negated === undefined) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const [prefix, negation] = negated;
|
|
36
|
+
|
|
37
|
+
return `${negation}${arg.slice(prefix.length)}`;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const mergeArgs = (defaults: string[], overrides: string[]): string[] => {
|
|
41
|
+
const kept = defaults.filter((arg) => {
|
|
42
|
+
const negation = negationFor(arg);
|
|
43
|
+
|
|
44
|
+
return negation === null || !overrides.includes(negation);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return [...new Set([...kept, ...overrides])];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const finishArgsFor = (settings: DeploySettings): string[] =>
|
|
51
|
+
mergeArgs(DEFAULT_FINISH_ARGS, settings.deploy.flatpak?.finishArgs ?? []);
|
|
52
|
+
|
|
53
|
+
const cleanupFor = (settings: DeploySettings): string[] => {
|
|
54
|
+
const cleanup = settings.deploy.flatpak?.cleanup;
|
|
55
|
+
|
|
56
|
+
if (cleanup === undefined) {
|
|
57
|
+
return DEFAULT_CLEANUP;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return cleanup.length === 0 ? [] : mergeArgs(DEFAULT_CLEANUP, cleanup);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const grantedSockets = (args: string[]): Set<string> => {
|
|
64
|
+
const granted: Set<string> = new Set();
|
|
65
|
+
|
|
66
|
+
for (const arg of args) {
|
|
67
|
+
if (arg.startsWith(SOCKET_ARG)) {
|
|
68
|
+
granted.add(arg.slice(SOCKET_ARG.length));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (arg.startsWith(NO_SOCKET_ARG)) {
|
|
72
|
+
granted.delete(arg.slice(NO_SOCKET_ARG.length));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return granted;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const hasDisplaySocket = (args: string[]): boolean => {
|
|
80
|
+
const granted = grantedSockets(args);
|
|
81
|
+
|
|
82
|
+
return DISPLAY_SOCKETS.some((socket) => granted.has(socket));
|
|
83
|
+
};
|
|
84
|
+
|
|
19
85
|
const sdkExtensionsFor = (settings: DeploySettings): string[] => {
|
|
20
86
|
const flatpak = settings.deploy.flatpak ?? {};
|
|
21
87
|
const nodeExtension = isSourceMode(settings) ? [nodeExtensionFor(settings)] : [];
|
|
@@ -46,16 +112,15 @@ const runtimeKeys = (settings: DeploySettings): FlatpakManifest => {
|
|
|
46
112
|
|
|
47
113
|
const renderFlatpakManifest = (payload: DeployPayload): FlatpakManifest => {
|
|
48
114
|
const settings = payload.settings;
|
|
49
|
-
const flatpak = settings.deploy.flatpak ?? {};
|
|
50
115
|
|
|
51
116
|
return {
|
|
52
117
|
id: settings.applicationId,
|
|
53
118
|
...runtimeKeys(settings),
|
|
54
119
|
command: settings.binaryName,
|
|
55
|
-
"finish-args":
|
|
56
|
-
cleanup:
|
|
120
|
+
"finish-args": finishArgsFor(settings),
|
|
121
|
+
cleanup: cleanupFor(settings),
|
|
57
122
|
modules: modulesFor(payload),
|
|
58
123
|
};
|
|
59
124
|
};
|
|
60
125
|
|
|
61
|
-
export { branchFor, renderFlatpakManifest };
|
|
126
|
+
export { branchFor, finishArgsFor, hasDisplaySocket, renderFlatpakManifest };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { DeploySettings } from "../types.js";
|
|
2
|
+
import type { FlatpakModule } from "./flatpak-prebuilt.js";
|
|
3
|
+
import { readPackageManifest } from "../settings/package-manifest.js";
|
|
4
|
+
|
|
5
|
+
type PnpmPin = {
|
|
6
|
+
version: string;
|
|
7
|
+
major: number;
|
|
8
|
+
minor: number;
|
|
9
|
+
sha512: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const PNPM_VERSION = "11.21.0";
|
|
13
|
+
const PNPM_MAJOR = 11;
|
|
14
|
+
const PNPM_MINOR = 21;
|
|
15
|
+
|
|
16
|
+
const PNPM_SHA512 = "521705bce689924eac72f5a3587122f362689ef6571e55ba80076fd637c11132ecffada26" +
|
|
17
|
+
"fad4ea79c485bfddbfd3d5a2a5b05805a77e893de71ec8a6cca3bb1";
|
|
18
|
+
|
|
19
|
+
const PNPM_DIR = "flatpak-pnpm";
|
|
20
|
+
const PNPM_COMMAND = "pnpm";
|
|
21
|
+
const PNPM_PREFIX = "pnpm@";
|
|
22
|
+
const PNPM_TARBALL_URL = "https://registry.npmjs.org/pnpm/-/pnpm-";
|
|
23
|
+
const PNPM_INSTALL = "pnpm install --offline --frozen-lockfile";
|
|
24
|
+
const TRUST_LOCKFILE = "--trust-lockfile";
|
|
25
|
+
const STORE_V10 = "v10";
|
|
26
|
+
const STORE_V11 = "v11";
|
|
27
|
+
const STORE_V10_MAJOR = 10;
|
|
28
|
+
const STORE_V11_MAJOR = 11;
|
|
29
|
+
const TRUST_LOCKFILE_MINOR = 3;
|
|
30
|
+
|
|
31
|
+
const DEFAULT_PIN: PnpmPin = {
|
|
32
|
+
version: PNPM_VERSION,
|
|
33
|
+
major: PNPM_MAJOR,
|
|
34
|
+
minor: PNPM_MINOR,
|
|
35
|
+
sha512: PNPM_SHA512,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const PNPM_PIN_FIELD =
|
|
39
|
+
/^pnpm@(?<version>(?<major>\d+)\.(?<minor>\d+)\.\d+)\+sha512\.(?<sha512>[\da-f]{128})$/;
|
|
40
|
+
|
|
41
|
+
const pinFromGroups = (groups: Record<string, string | undefined>): PnpmPin | null => {
|
|
42
|
+
const { major, minor, sha512, version } = groups;
|
|
43
|
+
|
|
44
|
+
if (major === undefined || minor === undefined || sha512 === undefined || version === undefined) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { version, major: Number(major), minor: Number(minor), sha512 };
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const isSupportedPin = (pin: PnpmPin): boolean =>
|
|
52
|
+
pin.major === STORE_V10_MAJOR || (pin.major === STORE_V11_MAJOR && pin.minor >= TRUST_LOCKFILE_MINOR);
|
|
53
|
+
|
|
54
|
+
const resolvePnpmPin = (settings: DeploySettings): PnpmPin => {
|
|
55
|
+
const field = readPackageManifest(settings.paths.root).packageManager;
|
|
56
|
+
|
|
57
|
+
if (field === null) {
|
|
58
|
+
return DEFAULT_PIN;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!field.startsWith(PNPM_PREFIX)) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Cannot vendor pnpm for the Flathub sandbox: "packageManager" is "${field}" but the offline install ` +
|
|
64
|
+
"resolves from a pnpm lockfile. Run `corepack use pnpm@<version>` to pin it to pnpm, or point " +
|
|
65
|
+
"`deploy.flatpak.packageManager` at the manager whose lockfile the build should install from.",
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const pin = pinFromGroups(PNPM_PIN_FIELD.exec(field)?.groups ?? {});
|
|
70
|
+
|
|
71
|
+
if (pin === null) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`Cannot vendor pnpm for the Flathub sandbox: "packageManager" is "${field}", which carries no sha512 ` +
|
|
74
|
+
"integrity digest, and every source in a Flathub build has to be hash-pinned. Run " +
|
|
75
|
+
"`corepack use pnpm@<version>` to rewrite it with its digest.",
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!isSupportedPin(pin)) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Cannot vendor pnpm ${pin.version} for the Flathub sandbox: the offline install runs on pnpm 10, or on ` +
|
|
82
|
+
"pnpm 11 from 11.3.0 on, where `--trust-lockfile` exists. Earlier pnpm 11 releases reach the registry " +
|
|
83
|
+
'during the supply-chain check and the sandbox has no network. Pin "packageManager" to one of those.',
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return pin;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const pnpmPathFor = (moduleDir: string): string => `${moduleDir}/${PNPM_DIR}`;
|
|
91
|
+
|
|
92
|
+
const pnpmSources = (pin: PnpmPin, moduleDir: string): FlatpakModule[] => [
|
|
93
|
+
{ type: "archive", url: `${PNPM_TARBALL_URL}${pin.version}.tgz`, sha512: pin.sha512, dest: PNPM_DIR },
|
|
94
|
+
{
|
|
95
|
+
type: "script",
|
|
96
|
+
dest: PNPM_DIR,
|
|
97
|
+
"dest-filename": PNPM_COMMAND,
|
|
98
|
+
commands: [`exec node ${pnpmPathFor(moduleDir)}/bin/pnpm.cjs "$@"`],
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const pnpmInstallCommand = (pin: PnpmPin): string =>
|
|
103
|
+
pin.major === STORE_V11_MAJOR ? `${PNPM_INSTALL} ${TRUST_LOCKFILE}` : PNPM_INSTALL;
|
|
104
|
+
|
|
105
|
+
const pnpmStoreVersionFor = (pin: PnpmPin): string =>
|
|
106
|
+
pin.major === STORE_V10_MAJOR ? STORE_V10 : STORE_V11;
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
pnpmInstallCommand,
|
|
110
|
+
pnpmPathFor,
|
|
111
|
+
type PnpmPin,
|
|
112
|
+
pnpmSources,
|
|
113
|
+
pnpmStoreVersionFor,
|
|
114
|
+
resolvePnpmPin,
|
|
115
|
+
};
|