@gtkx/cli 2.0.0-beta.6 → 2.0.0-beta.8
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/README.md +43 -40
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/codegen/freshness.d.ts.map +1 -1
- package/dist/codegen/freshness.js +5 -1
- package/dist/codegen/freshness.js.map +1 -1
- package/dist/commands/deploy.d.ts +4 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +6 -0
- package/dist/commands/deploy.js.map +1 -1
- package/dist/deploy/download.d.ts +7 -3
- package/dist/deploy/download.d.ts.map +1 -1
- package/dist/deploy/download.js +19 -15
- package/dist/deploy/download.js.map +1 -1
- package/dist/deploy/native-addon.d.ts +5 -0
- package/dist/deploy/native-addon.d.ts.map +1 -0
- package/dist/deploy/native-addon.js +134 -0
- package/dist/deploy/native-addon.js.map +1 -0
- package/dist/deploy/node-runtime/download.js +2 -2
- package/dist/deploy/node-runtime/download.js.map +1 -1
- package/dist/deploy/node-runtime/elf.d.ts +3 -1
- package/dist/deploy/node-runtime/elf.d.ts.map +1 -1
- package/dist/deploy/node-runtime/elf.js +3 -1
- package/dist/deploy/node-runtime/elf.js.map +1 -1
- package/dist/deploy/node-runtime/index.d.ts.map +1 -1
- package/dist/deploy/node-runtime/index.js +14 -2
- package/dist/deploy/node-runtime/index.js.map +1 -1
- package/dist/deploy/payload/stage.d.ts +2 -1
- package/dist/deploy/payload/stage.d.ts.map +1 -1
- package/dist/deploy/payload/stage.js +22 -2
- package/dist/deploy/payload/stage.js.map +1 -1
- package/dist/deploy/run-deploy.d.ts +1 -0
- package/dist/deploy/run-deploy.d.ts.map +1 -1
- package/dist/deploy/run-deploy.js +135 -79
- package/dist/deploy/run-deploy.js.map +1 -1
- package/dist/deploy/settings/arch.d.ts +9 -3
- package/dist/deploy/settings/arch.d.ts.map +1 -1
- package/dist/deploy/settings/arch.js +27 -6
- package/dist/deploy/settings/arch.js.map +1 -1
- package/dist/deploy/settings/index.d.ts +2 -1
- 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/libraries.d.ts.map +1 -1
- package/dist/deploy/settings/libraries.js +2 -1
- package/dist/deploy/settings/libraries.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 +6 -5
- package/dist/deploy/settings/paths.js.map +1 -1
- package/dist/deploy/targets/appimage.d.ts.map +1 -1
- package/dist/deploy/targets/appimage.js +1 -0
- package/dist/deploy/targets/appimage.js.map +1 -1
- package/dist/deploy/targets/flatpak-pnpm.js +4 -4
- package/dist/deploy/targets/flatpak-pnpm.js.map +1 -1
- package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak-source.js +2 -0
- package/dist/deploy/targets/flatpak-source.js.map +1 -1
- package/dist/deploy/targets/flatpak.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak.js +1 -0
- package/dist/deploy/targets/flatpak.js.map +1 -1
- package/dist/deploy/targets/nfpm-target.d.ts.map +1 -1
- package/dist/deploy/targets/nfpm-target.js +1 -0
- package/dist/deploy/targets/nfpm-target.js.map +1 -1
- package/dist/deploy/types.d.ts +4 -2
- package/dist/deploy/types.d.ts.map +1 -1
- package/dist/deploy/types.js.map +1 -1
- package/dist/deploy/vendored/appimagetool.js +2 -2
- package/dist/deploy/vendored/appimagetool.js.map +1 -1
- package/dist/deploy/vendored/nfpm.js +1 -1
- package/dist/deploy/vendored/nfpm.js.map +1 -1
- package/dist/dev/font-dir.d.ts +3 -0
- package/dist/dev/font-dir.d.ts.map +1 -0
- package/dist/dev/font-dir.js +9 -0
- package/dist/dev/font-dir.js.map +1 -0
- package/dist/dev/runner-main.d.ts.map +1 -1
- package/dist/dev/runner-main.js +2 -0
- package/dist/dev/runner-main.js.map +1 -1
- package/dist/dev/runner.d.ts.map +1 -1
- package/dist/dev/runner.js +19 -6
- package/dist/dev/runner.js.map +1 -1
- package/dist/internal/agent-rules.d.ts.map +1 -1
- package/dist/internal/agent-rules.js +6 -2
- package/dist/internal/agent-rules.js.map +1 -1
- package/dist/internal/banner.d.ts.map +1 -1
- package/dist/internal/banner.js +3 -0
- package/dist/internal/banner.js.map +1 -1
- package/dist/internal/font-path.d.ts +4 -0
- package/dist/internal/font-path.d.ts.map +1 -0
- package/dist/internal/font-path.js +12 -0
- package/dist/internal/font-path.js.map +1 -0
- package/dist/internal/font-staging.d.ts +6 -0
- package/dist/internal/font-staging.d.ts.map +1 -0
- package/dist/internal/font-staging.js +92 -0
- package/dist/internal/font-staging.js.map +1 -0
- package/dist/internal/source-imports.d.ts +2 -1
- package/dist/internal/source-imports.d.ts.map +1 -1
- package/dist/internal/source-imports.js +8 -3
- package/dist/internal/source-imports.js.map +1 -1
- package/dist/internal/xdg-banner.d.ts +4 -0
- package/dist/internal/xdg-banner.d.ts.map +1 -0
- package/dist/internal/xdg-banner.js +11 -0
- package/dist/internal/xdg-banner.js.map +1 -0
- package/dist/internal/xdg-data-dirs.d.ts +2 -1
- package/dist/internal/xdg-data-dirs.d.ts.map +1 -1
- package/dist/internal/xdg-data-dirs.js +1 -1
- package/dist/internal/xdg-data-dirs.js.map +1 -1
- package/dist/settings/schema.d.ts.map +1 -1
- package/dist/settings/schema.js +1 -6
- package/dist/settings/schema.js.map +1 -1
- package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
- package/dist/vite-plugins/asset-extensions.js +2 -0
- package/dist/vite-plugins/asset-extensions.js.map +1 -1
- package/dist/vite-plugins/asset-imports.d.ts.map +1 -1
- package/dist/vite-plugins/asset-imports.js +7 -2
- package/dist/vite-plugins/asset-imports.js.map +1 -1
- package/dist/vite-plugins/asset-specifier.d.ts +5 -1
- package/dist/vite-plugins/asset-specifier.d.ts.map +1 -1
- package/dist/vite-plugins/asset-specifier.js +10 -1
- package/dist/vite-plugins/asset-specifier.js.map +1 -1
- package/dist/vite-plugins/css.d.ts.map +1 -1
- package/dist/vite-plugins/css.js +3 -2
- package/dist/vite-plugins/css.js.map +1 -1
- package/dist/vite-plugins/font-name.d.ts +3 -0
- package/dist/vite-plugins/font-name.d.ts.map +1 -0
- package/dist/vite-plugins/font-name.js +406 -0
- package/dist/vite-plugins/font-name.js.map +1 -0
- package/dist/vite-plugins/font-worker-env.d.ts +4 -0
- package/dist/vite-plugins/font-worker-env.d.ts.map +1 -0
- package/dist/vite-plugins/font-worker-env.js +19 -0
- package/dist/vite-plugins/font-worker-env.js.map +1 -0
- package/dist/vite-plugins/font.d.ts +4 -0
- package/dist/vite-plugins/font.d.ts.map +1 -0
- package/dist/vite-plugins/font.js +99 -0
- package/dist/vite-plugins/font.js.map +1 -0
- package/dist/vite-plugins/icons.d.ts.map +1 -1
- package/dist/vite-plugins/icons.js +5 -10
- package/dist/vite-plugins/icons.js.map +1 -1
- package/dist/vite-plugins/index.d.ts.map +1 -1
- package/dist/vite-plugins/index.js +2 -0
- package/dist/vite-plugins/index.js.map +1 -1
- package/dist/vite-plugins/react-compiler.js +2 -2
- package/dist/vite-plugins/react-compiler.js.map +1 -1
- package/dist/vite-plugins/resources.js +3 -3
- package/dist/vite-plugins/resources.js.map +1 -1
- package/dist/vite-plugins/settings-worker-env.js +2 -2
- package/dist/vite-plugins/settings-worker-env.js.map +1 -1
- package/dist/vite-plugins/store-links.d.ts.map +1 -1
- package/dist/vite-plugins/store-links.js +2 -1
- package/dist/vite-plugins/store-links.js.map +1 -1
- package/dist/vite-plugins/undeclared-library.js +5 -5
- package/dist/vite-plugins/undeclared-library.js.map +1 -1
- package/dist/vitest-plugin.d.ts.map +1 -1
- package/dist/vitest-plugin.js +2 -0
- package/dist/vitest-plugin.js.map +1 -1
- package/env.d.ts +9 -0
- package/package.json +17 -15
- package/src/cli.ts +1 -1
- package/src/codegen/freshness.ts +5 -1
- package/src/commands/deploy.ts +6 -0
- package/src/deploy/download.ts +33 -17
- package/src/deploy/native-addon.ts +183 -0
- package/src/deploy/node-runtime/download.ts +2 -2
- package/src/deploy/node-runtime/elf.ts +4 -1
- package/src/deploy/node-runtime/index.ts +24 -4
- package/src/deploy/payload/stage.ts +34 -2
- package/src/deploy/run-deploy.ts +203 -93
- package/src/deploy/settings/arch.ts +40 -8
- package/src/deploy/settings/index.ts +4 -1
- package/src/deploy/settings/libraries.ts +2 -1
- package/src/deploy/settings/paths.ts +8 -6
- package/src/deploy/targets/appimage.ts +1 -0
- package/src/deploy/targets/flatpak-pnpm.ts +4 -4
- package/src/deploy/targets/flatpak-source.ts +2 -0
- package/src/deploy/targets/flatpak.ts +1 -0
- package/src/deploy/targets/nfpm-target.ts +1 -0
- package/src/deploy/types.ts +4 -1
- package/src/deploy/vendored/appimagetool.ts +2 -2
- package/src/deploy/vendored/nfpm.ts +1 -1
- package/src/dev/font-dir.ts +11 -0
- package/src/dev/runner-main.ts +2 -0
- package/src/dev/runner.ts +24 -6
- package/src/internal/agent-rules.ts +6 -2
- package/src/internal/banner.ts +4 -0
- package/src/internal/font-path.ts +15 -0
- package/src/internal/font-staging.ts +129 -0
- package/src/internal/source-imports.ts +18 -3
- package/src/internal/xdg-banner.ts +13 -0
- package/src/internal/xdg-data-dirs.ts +1 -1
- package/src/settings/schema.ts +1 -8
- package/src/vite-plugins/asset-extensions.ts +2 -0
- package/src/vite-plugins/asset-imports.ts +9 -1
- package/src/vite-plugins/asset-specifier.ts +17 -0
- package/src/vite-plugins/css.ts +4 -2
- package/src/vite-plugins/font-name.ts +582 -0
- package/src/vite-plugins/font-worker-env.ts +25 -0
- package/src/vite-plugins/font.ts +165 -0
- package/src/vite-plugins/icons.ts +6 -12
- package/src/vite-plugins/index.ts +2 -0
- package/src/vite-plugins/react-compiler.ts +2 -2
- package/src/vite-plugins/resources.ts +3 -3
- package/src/vite-plugins/settings-worker-env.ts +2 -2
- package/src/vite-plugins/store-links.ts +2 -1
- package/src/vite-plugins/undeclared-library.ts +5 -5
- package/src/vitest-plugin.ts +2 -0
package/dist/vitest-plugin.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import gtkxVitest, {} from "@gtkx/vitest";
|
|
2
2
|
import { gtkxEnsureStore } from "./vite-plugins/ensure-store.js";
|
|
3
|
+
import { gtkxFontWorkerEnv } from "./vite-plugins/font-worker-env.js";
|
|
3
4
|
import { gtkxVitePlugins } from "./vite-plugins/index.js";
|
|
4
5
|
import { gtkxSettingsWorkerEnv } from "./vite-plugins/settings-worker-env.js";
|
|
5
6
|
/**
|
|
@@ -13,6 +14,7 @@ const gtkx = (options = {}) => [
|
|
|
13
14
|
gtkxEnsureStore(options.configFile),
|
|
14
15
|
...gtkxVitePlugins({ configFile: options.configFile }),
|
|
15
16
|
gtkxSettingsWorkerEnv(options.configFile),
|
|
17
|
+
gtkxFontWorkerEnv(options.configFile),
|
|
16
18
|
gtkxVitest(options),
|
|
17
19
|
];
|
|
18
20
|
export default gtkx;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitest-plugin.js","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,EAAE,EAAsB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,IAAI,GAAG,CAAC,UAAyB,EAAE,EAAY,EAAE,CAAC;IACpD,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC;IACnC,GAAG,eAAe,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;IACtD,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC;IACzC,UAAU,CAAC,OAAO,CAAC;CACtB,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import type { Plugin } from \"vite\";\nimport gtkxVitest, { type PluginOptions } from \"@gtkx/vitest\";\nimport { gtkxEnsureStore } from \"./vite-plugins/ensure-store.js\";\nimport { gtkxVitePlugins } from \"./vite-plugins/index.js\";\nimport { gtkxSettingsWorkerEnv } from \"./vite-plugins/settings-worker-env.js\";\n\n/**\n * Vite plugin stack for testing a GTKX application: the generated bindings the tests import,\n * the plugins the app is built with, the project's compiled GSettings schemas exposed to every\n * test worker, and a headless display per worker.\n *\n * @returns The plugins to spread into a Vitest config.\n */\nconst gtkx = (options: PluginOptions = {}): Plugin[] => [\n gtkxEnsureStore(options.configFile),\n ...gtkxVitePlugins({ configFile: options.configFile }),\n gtkxSettingsWorkerEnv(options.configFile),\n gtkxVitest(options),\n];\n\nexport default gtkx;\n"]}
|
|
1
|
+
{"version":3,"file":"vitest-plugin.js","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,EAAE,EAAsB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,IAAI,GAAG,CAAC,UAAyB,EAAE,EAAY,EAAE,CAAC;IACpD,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC;IACnC,GAAG,eAAe,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;IACtD,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC;IACzC,iBAAiB,CAAC,OAAO,CAAC,UAAU,CAAC;IACrC,UAAU,CAAC,OAAO,CAAC;CACtB,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import type { Plugin } from \"vite\";\nimport gtkxVitest, { type PluginOptions } from \"@gtkx/vitest\";\nimport { gtkxEnsureStore } from \"./vite-plugins/ensure-store.js\";\nimport { gtkxFontWorkerEnv } from \"./vite-plugins/font-worker-env.js\";\nimport { gtkxVitePlugins } from \"./vite-plugins/index.js\";\nimport { gtkxSettingsWorkerEnv } from \"./vite-plugins/settings-worker-env.js\";\n\n/**\n * Vite plugin stack for testing a GTKX application: the generated bindings the tests import,\n * the plugins the app is built with, the project's compiled GSettings schemas exposed to every\n * test worker, and a headless display per worker.\n *\n * @returns The plugins to spread into a Vitest config.\n */\nconst gtkx = (options: PluginOptions = {}): Plugin[] => [\n gtkxEnsureStore(options.configFile),\n ...gtkxVitePlugins({ configFile: options.configFile }),\n gtkxSettingsWorkerEnv(options.configFile),\n gtkxFontWorkerEnv(options.configFile),\n gtkxVitest(options),\n];\n\nexport default gtkx;\n"]}
|
package/env.d.ts
CHANGED
|
@@ -14,6 +14,15 @@ declare module "*?icon" {
|
|
|
14
14
|
export default iconName;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
declare module "*?font" {
|
|
18
|
+
/**
|
|
19
|
+
* Font family name of the bundled font. Importing the module bundles the font file with the application and
|
|
20
|
+
* puts it where fontconfig finds it, so the family is available to Pango without any runtime registration.
|
|
21
|
+
*/
|
|
22
|
+
const fontFamily: string;
|
|
23
|
+
export default fontFamily;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
declare module "*.css?url" {
|
|
18
27
|
/**
|
|
19
28
|
* Absolute filesystem path of the source stylesheet under `gtkx dev` or the emitted stylesheet once built,
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/cli",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0-beta.8",
|
|
4
|
+
"description": "Adwaita-first GNOME app toolchain for React and TypeScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtkx",
|
|
7
7
|
"gtk",
|
|
8
8
|
"gtk4",
|
|
9
|
+
"adwaita",
|
|
10
|
+
"gnome",
|
|
9
11
|
"react",
|
|
10
12
|
"typescript",
|
|
11
13
|
"native",
|
|
@@ -70,33 +72,33 @@
|
|
|
70
72
|
"dependencies": {
|
|
71
73
|
"@babel/core": "^8.0.1",
|
|
72
74
|
"@babel/preset-typescript": "^8.0.1",
|
|
73
|
-
"@gtkx/codegen": "2.0.0-beta.
|
|
74
|
-
"@gtkx/config": "2.0.0-beta.
|
|
75
|
-
"@gtkx/mcp": "2.0.0-beta.
|
|
76
|
-
"@gtkx/native": "2.0.0-beta.
|
|
77
|
-
"@gtkx/runtime": "2.0.0-beta.
|
|
78
|
-
"@gtkx/utils": "2.0.0-beta.
|
|
79
|
-
"@gtkx/vitest": "2.0.0-beta.
|
|
80
|
-
"@swc/core": "^1.16.
|
|
75
|
+
"@gtkx/codegen": "2.0.0-beta.8",
|
|
76
|
+
"@gtkx/config": "2.0.0-beta.8",
|
|
77
|
+
"@gtkx/mcp": "2.0.0-beta.8",
|
|
78
|
+
"@gtkx/native": "2.0.0-beta.8",
|
|
79
|
+
"@gtkx/runtime": "2.0.0-beta.8",
|
|
80
|
+
"@gtkx/utils": "2.0.0-beta.8",
|
|
81
|
+
"@gtkx/vitest": "2.0.0-beta.8",
|
|
82
|
+
"@swc/core": "^1.16.2",
|
|
81
83
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
82
84
|
"citty": "^0.2.2",
|
|
83
|
-
"confbox": "^0.
|
|
84
|
-
"create-gtkx": "2.0.0-beta.
|
|
85
|
+
"confbox": "^0.3.1",
|
|
86
|
+
"create-gtkx": "2.0.0-beta.8",
|
|
85
87
|
"fast-xml-builder": "^1.3.1",
|
|
86
88
|
"fast-xml-parser": "^5.11.1",
|
|
87
89
|
"hosted-git-info": "^10.1.1",
|
|
88
|
-
"i18next-cli": "1.
|
|
90
|
+
"i18next-cli": "1.73.1",
|
|
89
91
|
"jiti": "^2.7.0",
|
|
90
92
|
"react-refresh": "^0.18.0",
|
|
91
93
|
"vite": "^8.2.2",
|
|
92
94
|
"yaml": "^2.9.0"
|
|
93
95
|
},
|
|
94
96
|
"devDependencies": {
|
|
95
|
-
"@gtkx/testing": "2.0.0-beta.
|
|
97
|
+
"@gtkx/testing": "2.0.0-beta.8",
|
|
96
98
|
"@types/babel__core": "^7.20.5",
|
|
97
99
|
"@types/hosted-git-info": "^3.0.5",
|
|
98
100
|
"@types/react-refresh": "^0.14.7",
|
|
99
|
-
"vitest": "^
|
|
101
|
+
"vitest": "^5.0.0"
|
|
100
102
|
},
|
|
101
103
|
"peerDependencies": {
|
|
102
104
|
"react": "^19"
|
package/src/cli.ts
CHANGED
|
@@ -18,7 +18,7 @@ const main = defineCommand({
|
|
|
18
18
|
meta: {
|
|
19
19
|
name: "gtkx",
|
|
20
20
|
version,
|
|
21
|
-
description: "CLI for GTKX: create and develop
|
|
21
|
+
description: "CLI for GTKX: create and develop Adwaita-first GNOME applications",
|
|
22
22
|
},
|
|
23
23
|
subCommands: {
|
|
24
24
|
dev: async () => {
|
package/src/codegen/freshness.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Config } from "@gtkx/config";
|
|
2
2
|
import { resolveGirPath, resolveLibraries } from "@gtkx/codegen";
|
|
3
|
+
import { resolveBoundLibraries } from "@gtkx/codegen/internal";
|
|
3
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
5
|
import { basename, dirname, join } from "node:path";
|
|
5
6
|
import { type CodegenStore, resolveCodegenStore } from "./store-resolver.js";
|
|
@@ -86,7 +87,10 @@ const hasStoreExports = (storeDir: string, required: string[], forbidden: string
|
|
|
86
87
|
};
|
|
87
88
|
|
|
88
89
|
const isGiStoreStale = (store: CodegenStore, libraries: string[]): boolean => {
|
|
89
|
-
const required = [
|
|
90
|
+
const required = [
|
|
91
|
+
PACKAGE_EXPORT,
|
|
92
|
+
...resolveBoundLibraries(libraries).map((library) => namespaceExport(library)),
|
|
93
|
+
];
|
|
90
94
|
|
|
91
95
|
return !hasStoreExports(store.giStoreDir, required);
|
|
92
96
|
};
|
package/src/commands/deploy.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineCommand } from "citty";
|
|
2
2
|
import { KNOWN_NAMES } from "../deploy/registry.js";
|
|
3
3
|
import { runDeploy } from "../deploy/run-deploy.js";
|
|
4
|
+
import { KNOWN_ARCH_NAMES } from "../deploy/settings/arch.js";
|
|
4
5
|
import { configArg, entryArg } from "../internal/entry-arg.js";
|
|
5
6
|
import { resolveProject } from "../internal/prepare-project.js";
|
|
6
7
|
|
|
@@ -18,6 +19,10 @@ const deploy = defineCommand({
|
|
|
18
19
|
type: "string",
|
|
19
20
|
description: `Comma-separated package formats to build (${KNOWN_NAMES})`,
|
|
20
21
|
},
|
|
22
|
+
arch: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: `Comma-separated architectures to build (${KNOWN_ARCH_NAMES}; default: the host)`,
|
|
25
|
+
},
|
|
21
26
|
out: {
|
|
22
27
|
type: "string",
|
|
23
28
|
description: "Output directory relative to the project root (default: build)",
|
|
@@ -39,6 +44,7 @@ const deploy = defineCommand({
|
|
|
39
44
|
cwd,
|
|
40
45
|
configFile,
|
|
41
46
|
targets: args.target,
|
|
47
|
+
arches: args.arch,
|
|
42
48
|
outDir: args.out,
|
|
43
49
|
shouldPrintManifests: args["print-manifests"] === true,
|
|
44
50
|
shouldSkipBuild: args["skip-build"] === true,
|
package/src/deploy/download.ts
CHANGED
|
@@ -11,17 +11,23 @@ type DigestRequest = {
|
|
|
11
11
|
subject: string;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
type DigestAlgorithm = "sha256" | "sha512";
|
|
15
|
+
|
|
14
16
|
type DownloadRequest = {
|
|
15
17
|
url: string;
|
|
16
18
|
dest: string;
|
|
17
19
|
label: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
digest: string;
|
|
21
|
+
algorithm?: DigestAlgorithm | undefined;
|
|
22
|
+
freshDigest?: (() => Promise<string>) | undefined;
|
|
20
23
|
mode?: number | undefined;
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
const CACHE_NAMESPACE = "gtkx";
|
|
24
|
-
const DIGEST_PATTERN =
|
|
27
|
+
const DIGEST_PATTERN: Record<DigestAlgorithm, RegExp> = {
|
|
28
|
+
sha256: /^[\da-f]{64}$/,
|
|
29
|
+
sha512: /^[\da-f]{128}$/,
|
|
30
|
+
};
|
|
25
31
|
|
|
26
32
|
const cacheRoot = (): string => {
|
|
27
33
|
const base = process.env.XDG_CACHE_HOME;
|
|
@@ -36,10 +42,11 @@ const cacheDir = (segments: string[]): string => {
|
|
|
36
42
|
return dir;
|
|
37
43
|
};
|
|
38
44
|
|
|
39
|
-
const getDigest = (contents: Buffer): string =>
|
|
45
|
+
const getDigest = (contents: Buffer, algorithm: DigestAlgorithm): string =>
|
|
46
|
+
createHash(algorithm).update(contents).digest("hex");
|
|
40
47
|
|
|
41
|
-
const assertDigest = (url: string, contents: Buffer, expected: string): void => {
|
|
42
|
-
const actual = getDigest(contents);
|
|
48
|
+
const assertDigest = (url: string, contents: Buffer, expected: string, algorithm: DigestAlgorithm): void => {
|
|
49
|
+
const actual = getDigest(contents, algorithm);
|
|
43
50
|
|
|
44
51
|
if (actual !== expected) {
|
|
45
52
|
throw new Error(`Checksum mismatch for ${url}\n expected ${expected}\n received ${actual}`);
|
|
@@ -62,11 +69,11 @@ const fetchText = async (url: string): Promise<string> => {
|
|
|
62
69
|
return bytes.toString("utf8");
|
|
63
70
|
};
|
|
64
71
|
|
|
65
|
-
const readCachedDigest = (dest: string): string | undefined => {
|
|
72
|
+
const readCachedDigest = (dest: string, algorithm: DigestAlgorithm): string | undefined => {
|
|
66
73
|
try {
|
|
67
74
|
const cached = readFileSync(dest, "utf8").trim();
|
|
68
75
|
|
|
69
|
-
return DIGEST_PATTERN.test(cached) ? cached : undefined;
|
|
76
|
+
return DIGEST_PATTERN[algorithm].test(cached) ? cached : undefined;
|
|
70
77
|
} catch {
|
|
71
78
|
return undefined;
|
|
72
79
|
}
|
|
@@ -82,7 +89,7 @@ const publishedDigest = async (request: DigestRequest): Promise<string> => {
|
|
|
82
89
|
const checksums = await fetchText(request.url);
|
|
83
90
|
const digest = digestFromChecksums(checksums, request.assetName, request.subject);
|
|
84
91
|
|
|
85
|
-
if (!DIGEST_PATTERN.test(digest)) {
|
|
92
|
+
if (!DIGEST_PATTERN.sha256.test(digest)) {
|
|
86
93
|
throw new Error(`${request.subject} published a malformed checksum for ${request.assetName}: "${digest}"`);
|
|
87
94
|
}
|
|
88
95
|
|
|
@@ -92,17 +99,17 @@ const publishedDigest = async (request: DigestRequest): Promise<string> => {
|
|
|
92
99
|
};
|
|
93
100
|
|
|
94
101
|
const cachedDigest = async (request: DigestRequest): Promise<string> => {
|
|
95
|
-
const cached = readCachedDigest(request.dest);
|
|
102
|
+
const cached = readCachedDigest(request.dest, "sha256");
|
|
96
103
|
|
|
97
104
|
return cached ?? publishedDigest(request);
|
|
98
105
|
};
|
|
99
106
|
|
|
100
|
-
const isCacheUsable = (dest: string,
|
|
107
|
+
const isCacheUsable = (dest: string, digest: string, algorithm: DigestAlgorithm): boolean => {
|
|
101
108
|
if (!existsSync(dest)) {
|
|
102
109
|
return false;
|
|
103
110
|
}
|
|
104
111
|
|
|
105
|
-
if (getDigest(readFileSync(dest)) ===
|
|
112
|
+
if (getDigest(readFileSync(dest), algorithm) === digest) {
|
|
106
113
|
return true;
|
|
107
114
|
}
|
|
108
115
|
|
|
@@ -112,16 +119,17 @@ const isCacheUsable = (dest: string, sha256: string): boolean => {
|
|
|
112
119
|
};
|
|
113
120
|
|
|
114
121
|
const downloadFile = async (request: DownloadRequest): Promise<string> => {
|
|
115
|
-
const { url, dest, label,
|
|
122
|
+
const { url, dest, label, digest, mode } = request;
|
|
123
|
+
const algorithm = request.algorithm ?? "sha256";
|
|
116
124
|
|
|
117
|
-
if (isCacheUsable(dest,
|
|
125
|
+
if (isCacheUsable(dest, digest, algorithm)) {
|
|
118
126
|
return dest;
|
|
119
127
|
}
|
|
120
128
|
|
|
121
129
|
info(`Downloading ${label}`);
|
|
122
|
-
const expected = request.
|
|
130
|
+
const expected = request.freshDigest === undefined ? digest : await request.freshDigest();
|
|
123
131
|
const contents = await fetchBytes(url);
|
|
124
|
-
assertDigest(url, contents, expected);
|
|
132
|
+
assertDigest(url, contents, expected, algorithm);
|
|
125
133
|
const staging = `${dest}.partial`;
|
|
126
134
|
writeFileSync(staging, contents);
|
|
127
135
|
|
|
@@ -145,4 +153,12 @@ const digestFromChecksums = (checksums: string, assetName: string, subject: stri
|
|
|
145
153
|
return digest;
|
|
146
154
|
};
|
|
147
155
|
|
|
148
|
-
export {
|
|
156
|
+
export {
|
|
157
|
+
cacheDir,
|
|
158
|
+
cachedDigest,
|
|
159
|
+
type DigestRequest,
|
|
160
|
+
downloadFile,
|
|
161
|
+
publishedDigest,
|
|
162
|
+
readCachedDigest,
|
|
163
|
+
writeAtomically,
|
|
164
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import type { DeployArchName, DeploySettings } from "./types.js";
|
|
5
|
+
import { runCliTool } from "../internal/run-cli-tool.js";
|
|
6
|
+
import { cacheDir, downloadFile, readCachedDigest, writeAtomically } from "./download.js";
|
|
7
|
+
import { isHostArch } from "./settings/arch.js";
|
|
8
|
+
|
|
9
|
+
type ProjectRequire = ReturnType<typeof createRequire>;
|
|
10
|
+
type Tarball = { url: string; digest: string };
|
|
11
|
+
|
|
12
|
+
const BINDING_FILENAME = "gtkx.node";
|
|
13
|
+
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
14
|
+
const NATIVE_MANIFEST = "@gtkx/native/package.json";
|
|
15
|
+
const PACKAGE_ROOT = "package";
|
|
16
|
+
const REQUEST_TIMEOUT = 30_000;
|
|
17
|
+
const SRI_PREFIX = "sha512-";
|
|
18
|
+
const STRIP_COMPONENTS = "1";
|
|
19
|
+
const VERSION_PATTERN = /^[\w.+-]+$/;
|
|
20
|
+
|
|
21
|
+
const binaryFilename = (arch: DeployArchName): string => `native.linux-${arch}-gnu.node`;
|
|
22
|
+
const platformStem = (arch: DeployArchName): string => `native-linux-${arch}-gnu`;
|
|
23
|
+
const platformPackage = (arch: DeployArchName): string => `@gtkx/${platformStem(arch)}`;
|
|
24
|
+
|
|
25
|
+
const withoutTrailingSlash = (value: string): string => {
|
|
26
|
+
let end = value.length;
|
|
27
|
+
|
|
28
|
+
while (end > 0 && value[end - 1] === "/") {
|
|
29
|
+
end -= 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return value.slice(0, end);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const registryUrl = (): string => {
|
|
36
|
+
const configured = process.env.npm_config_registry;
|
|
37
|
+
|
|
38
|
+
return withoutTrailingSlash(configured !== undefined && configured.length > 0 ? configured : DEFAULT_REGISTRY);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const projectRequireFor = (root: string): ProjectRequire => createRequire(join(root, "package.json"));
|
|
42
|
+
|
|
43
|
+
const localBinary = (projectRequire: ProjectRequire, arch: DeployArchName): string | null => {
|
|
44
|
+
const nativeRoot = dirname(projectRequire.resolve(NATIVE_MANIFEST));
|
|
45
|
+
const candidate = join(nativeRoot, binaryFilename(arch));
|
|
46
|
+
|
|
47
|
+
return existsSync(candidate) ? candidate : null;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const installedBinary = (projectRequire: ProjectRequire, arch: DeployArchName): string | null => {
|
|
51
|
+
try {
|
|
52
|
+
return projectRequire.resolve(platformPackage(arch));
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const manifestVersion = (manifest: unknown): string | null => {
|
|
59
|
+
if (typeof manifest !== "object" || manifest === null || !("version" in manifest)) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const version = manifest.version;
|
|
64
|
+
|
|
65
|
+
return typeof version === "string" && VERSION_PATTERN.test(version) ? version : null;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const nativeVersion = (projectRequire: ProjectRequire): string => {
|
|
69
|
+
const parsed: unknown = JSON.parse(readFileSync(projectRequire.resolve(NATIVE_MANIFEST), "utf8"));
|
|
70
|
+
const version = manifestVersion(parsed);
|
|
71
|
+
|
|
72
|
+
if (version === null) {
|
|
73
|
+
throw new Error("Cannot read a usable version from @gtkx/native; is it installed?");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return version;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const hexFromIntegrity = (integrity: unknown, subject: string): string => {
|
|
80
|
+
if (typeof integrity !== "string" || !integrity.startsWith(SRI_PREFIX)) {
|
|
81
|
+
throw new Error(`The npm registry publishes no sha512 integrity for ${subject}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return Buffer.from(integrity.slice(SRI_PREFIX.length), "base64").toString("hex");
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const fetchVersionDocument = async (url: string, subject: string): Promise<Record<string, unknown>> => {
|
|
88
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(REQUEST_TIMEOUT) });
|
|
89
|
+
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Cannot fetch ${subject}: HTTP ${String(response.status)} ${response.statusText}. ` +
|
|
93
|
+
`Install ${subject} directly, or drop that architecture from --arch.`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const document: unknown = await response.json();
|
|
98
|
+
|
|
99
|
+
if (typeof document !== "object" || document === null) {
|
|
100
|
+
throw new Error(`The npm registry returned no metadata for ${subject}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return document as Record<string, unknown>;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const publishedTarball = async (arch: DeployArchName, version: string): Promise<Tarball> => {
|
|
107
|
+
const registry = registryUrl();
|
|
108
|
+
const subject = `${platformPackage(arch)}@${version}`;
|
|
109
|
+
const url = `${registry}/${encodeURIComponent(platformPackage(arch))}/${version}`;
|
|
110
|
+
const document = await fetchVersionDocument(url, subject);
|
|
111
|
+
const dist: unknown = document.dist;
|
|
112
|
+
|
|
113
|
+
if (typeof dist !== "object" || dist === null) {
|
|
114
|
+
throw new Error(`The npm registry returned no tarball for ${subject}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const entry = dist as Record<string, unknown>;
|
|
118
|
+
const tarball = entry.tarball;
|
|
119
|
+
|
|
120
|
+
if (typeof tarball !== "string" || new URL(tarball).origin !== new URL(registry).origin) {
|
|
121
|
+
throw new Error(`The npm registry returned an unusable tarball URL for ${subject}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { url: tarball, digest: hexFromIntegrity(entry.integrity, subject) };
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const extractAddon = (archive: string, dir: string, arch: DeployArchName): void => {
|
|
128
|
+
runCliTool({
|
|
129
|
+
tool: "tar",
|
|
130
|
+
args: ["-xzf", archive, "-C", dir, "--strip-components", STRIP_COMPONENTS,
|
|
131
|
+
`${PACKAGE_ROOT}/${binaryFilename(arch)}`],
|
|
132
|
+
target: platformPackage(arch),
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const cachedTarballUrl = (arch: DeployArchName, version: string): string =>
|
|
137
|
+
`${registryUrl()}/${platformPackage(arch)}/-/${platformStem(arch)}-${version}.tgz`;
|
|
138
|
+
|
|
139
|
+
const tarballFor = async (arch: DeployArchName, version: string, sidecar: string): Promise<Tarball> => {
|
|
140
|
+
const cached = readCachedDigest(sidecar, "sha512");
|
|
141
|
+
|
|
142
|
+
if (cached !== undefined) {
|
|
143
|
+
return { url: cachedTarballUrl(arch, version), digest: cached };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const published = await publishedTarball(arch, version);
|
|
147
|
+
writeAtomically(sidecar, published.digest);
|
|
148
|
+
|
|
149
|
+
return published;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const downloadedBinary = async (arch: DeployArchName, version: string): Promise<string> => {
|
|
153
|
+
const dir = cacheDir(["native", `${version}-linux-${arch}`]);
|
|
154
|
+
const archive = join(dir, `${platformStem(arch)}-${version}.tgz`);
|
|
155
|
+
const tarball = await tarballFor(arch, version, `${archive}.sha512`);
|
|
156
|
+
|
|
157
|
+
await downloadFile({
|
|
158
|
+
url: tarball.url,
|
|
159
|
+
dest: archive,
|
|
160
|
+
label: `${platformPackage(arch)} ${version}`,
|
|
161
|
+
digest: tarball.digest,
|
|
162
|
+
algorithm: "sha512",
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
extractAddon(archive, dir, arch);
|
|
166
|
+
|
|
167
|
+
return join(dir, binaryFilename(arch));
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const resolveStagedAddon = async (settings: DeploySettings): Promise<string | null> => {
|
|
171
|
+
const arch = settings.arch.node;
|
|
172
|
+
|
|
173
|
+
if (isHostArch(arch)) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const projectRequire = projectRequireFor(settings.paths.root);
|
|
178
|
+
const resolved = localBinary(projectRequire, arch) ?? installedBinary(projectRequire, arch);
|
|
179
|
+
|
|
180
|
+
return resolved ?? await downloadedBinary(arch, nativeVersion(projectRequire));
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export { BINDING_FILENAME, resolveStagedAddon };
|
|
@@ -44,8 +44,8 @@ const downloadNode = async (version: string, arch: string, destDir: string): Pro
|
|
|
44
44
|
url: `${DIST_BASE_URL}/v${version}/${assetName}`,
|
|
45
45
|
dest: join(dir, assetName),
|
|
46
46
|
label: `Node.js ${version} for linux-${arch}`,
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
digest: await cachedDigest(request),
|
|
48
|
+
freshDigest: () => publishedDigest(request),
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
mkdirSync(destDir, { recursive: true });
|
|
@@ -7,6 +7,7 @@ type ElfSection = {
|
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
type ElfInfo = {
|
|
10
|
+
machine: number;
|
|
10
11
|
needed: string[];
|
|
11
12
|
glibcMinimum: string | null;
|
|
12
13
|
};
|
|
@@ -22,6 +23,7 @@ const CLASS_64 = 2;
|
|
|
22
23
|
const LITTLE_ENDIAN = 1;
|
|
23
24
|
const OFFSET_CLASS = 4;
|
|
24
25
|
const OFFSET_DATA = 5;
|
|
26
|
+
const OFFSET_MACHINE = 0x12;
|
|
25
27
|
const OFFSET_SECTION_HEADERS = 0x28;
|
|
26
28
|
const OFFSET_SECTION_ENTRY_SIZE = 0x3A;
|
|
27
29
|
const OFFSET_SECTION_COUNT = 0x3C;
|
|
@@ -140,6 +142,7 @@ const readElfBuffer = (buffer: Buffer, path: string): ElfInfo => {
|
|
|
140
142
|
const strings = stringTable(buffer, sectionFor(sections, ".dynstr"));
|
|
141
143
|
|
|
142
144
|
return {
|
|
145
|
+
machine: buffer.readUInt16LE(OFFSET_MACHINE),
|
|
143
146
|
needed: readNeeded(buffer, sectionFor(sections, ".dynamic"), strings),
|
|
144
147
|
glibcMinimum: readGlibcMinimum(strings),
|
|
145
148
|
};
|
|
@@ -180,4 +183,4 @@ const glibcMinimumForFiles = (paths: Iterable<string>): string | null => {
|
|
|
180
183
|
return formatVersion(highestVersion(versions));
|
|
181
184
|
};
|
|
182
185
|
|
|
183
|
-
export { type ElfInfo, glibcMinimumForFiles, readElfInfo };
|
|
186
|
+
export { type ElfInfo, glibcMinimumForFiles, readElfInfo, readOptionalElfInfo };
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { MINIMUM_NODE_VERSION } from "@gtkx/config/internal";
|
|
2
|
-
import { info, tryResolveExecutable } from "@gtkx/utils";
|
|
2
|
+
import { info, tryResolveExecutable, warn } from "@gtkx/utils";
|
|
3
3
|
import { execFileSync } from "node:child_process";
|
|
4
4
|
import { chmodSync, copyFileSync, mkdirSync, statSync } from "node:fs";
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
6
|
-
import type { DeploySettings, NodeRuntime } from "../types.js";
|
|
6
|
+
import type { DeployConfig, DeploySettings, NodeRuntime } from "../types.js";
|
|
7
7
|
import { runCliTool } from "../../internal/run-cli-tool.js";
|
|
8
|
+
import { elfMachineFor, hostArchName } from "../settings/arch.js";
|
|
8
9
|
import { downloadNode } from "./download.js";
|
|
9
|
-
import { readElfInfo } from "./elf.js";
|
|
10
|
+
import { type ElfInfo, readElfInfo } from "./elf.js";
|
|
10
11
|
import { assertPortableNode } from "./guard.js";
|
|
11
12
|
import { licenseBesideNode } from "./license.js";
|
|
12
13
|
|
|
@@ -171,13 +172,32 @@ const stageFromSource = async (
|
|
|
171
172
|
return { path: stageNode(settings, sourcePath), licenseFile: licenseBesideNode(sourcePath) };
|
|
172
173
|
};
|
|
173
174
|
|
|
175
|
+
type DeployNodeConfig = NonNullable<DeployConfig["node"]>;
|
|
176
|
+
|
|
177
|
+
const shouldStripRuntime = (settings: DeploySettings, node: DeployNodeConfig, elf: ElfInfo): boolean => {
|
|
178
|
+
if (node.shouldStrip === false) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (elf.machine === elfMachineFor(hostArchName())) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
warn(
|
|
187
|
+
`Skipping \`strip\` on the bundled Node.js: it was built for ${settings.arch.node} and \`strip\` reads ` +
|
|
188
|
+
`only ${hostArchName()}. The ${settings.arch.node} packages carry an unstripped runtime.`,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
return false;
|
|
192
|
+
};
|
|
193
|
+
|
|
174
194
|
const resolveNodeRuntime = async (settings: DeploySettings): Promise<NodeRuntime> => {
|
|
175
195
|
const node = settings.deploy.node ?? {};
|
|
176
196
|
const source = nodeSourceFor(settings);
|
|
177
197
|
const version = resolveNodeVersion(settings);
|
|
178
198
|
const staged = await stageFromSource(settings, version, source);
|
|
179
|
-
const isStripped = node.shouldStrip === false ? false : didStripBinary(staged.path);
|
|
180
199
|
const elf = readElfInfo(staged.path);
|
|
200
|
+
const isStripped = shouldStripRuntime(settings, node, elf) && didStripBinary(staged.path);
|
|
181
201
|
const glibcMinimum = elf.glibcMinimum ?? "unknown";
|
|
182
202
|
info(`Bundled Node.js v${version} (${megabytes(staged.path)} MiB, runtime glibc >= ${glibcMinimum})`);
|
|
183
203
|
|
|
@@ -8,7 +8,10 @@ import { listFilesRecursive } from "../../internal/list-files.js";
|
|
|
8
8
|
import { BUNDLE_FILENAME } from "../../vite-plugins/esm-extension.js";
|
|
9
9
|
import { renderCopyright } from "../freedesktop/copyright.js";
|
|
10
10
|
import { renderDbusService } from "../freedesktop/dbus-service.js";
|
|
11
|
+
import { BINDING_FILENAME } from "../native-addon.js";
|
|
12
|
+
import { readOptionalElfInfo } from "../node-runtime/elf.js";
|
|
11
13
|
import { renderNotices } from "../notices/render.js";
|
|
14
|
+
import { elfMachineFor } from "../settings/arch.js";
|
|
12
15
|
import { copyInto, EXECUTABLE_MODE, executableModeFor, writeInto } from "./copy-tree.js";
|
|
13
16
|
import { stageIcons } from "./icons.js";
|
|
14
17
|
import { NODE_FILENAME, renderLauncher } from "./launcher.js";
|
|
@@ -17,6 +20,7 @@ import { stageSchemas } from "./schemas.js";
|
|
|
17
20
|
type StageRequest = {
|
|
18
21
|
settings: DeploySettings;
|
|
19
22
|
node: NodeRuntime | null;
|
|
23
|
+
addon: string | null;
|
|
20
24
|
metadata: StagedMetadata;
|
|
21
25
|
};
|
|
22
26
|
|
|
@@ -67,6 +71,29 @@ const stageRuntimeFiles = (settings: DeploySettings, root: string): StagedFile[]
|
|
|
67
71
|
.map((file) => copyInto(root, join(libDirFor(settings), file.rel), file.absPath));
|
|
68
72
|
};
|
|
69
73
|
|
|
74
|
+
const stageAddon = (settings: DeploySettings, root: string, addon: string | null): StagedFile[] =>
|
|
75
|
+
addon === null ? [] : [copyInto(root, join(libDirFor(settings), BINDING_FILENAME), addon)];
|
|
76
|
+
|
|
77
|
+
const architectureMismatch = (settings: DeploySettings, file: StagedFile): Error =>
|
|
78
|
+
new Error(
|
|
79
|
+
`Cannot deploy ${file.rel} for ${settings.arch.node}: it was built for another architecture. ` +
|
|
80
|
+
"Run `gtkx build` again, or drop --skip-build.",
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const assertStagedArchitecture = (settings: DeploySettings, staged: StagedFile[]): void => {
|
|
84
|
+
const expected = elfMachineFor(settings.arch.node);
|
|
85
|
+
const lib = libDirFor(settings);
|
|
86
|
+
const checked = new Set([join(lib, BINDING_FILENAME), join(lib, NODE_FILENAME)]);
|
|
87
|
+
|
|
88
|
+
const binaries = staged.filter((entry) => checked.has(entry.rel));
|
|
89
|
+
|
|
90
|
+
for (const file of binaries) {
|
|
91
|
+
if (readOptionalElfInfo(file.abs)?.machine !== expected) {
|
|
92
|
+
throw architectureMismatch(settings, file);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
70
97
|
const stageCatalogs = (settings: DeploySettings, root: string): StagedFile[] =>
|
|
71
98
|
listFilesRecursive(join(settings.paths.dist, LOCALE_DIRNAME))
|
|
72
99
|
.map((file) => copyInto(root, join(SHARE_LOCALE, file.rel), file.absPath));
|
|
@@ -138,20 +165,25 @@ const byRelativePath = (files: StagedFile[]): StagedFile[] => {
|
|
|
138
165
|
return sortStringsBy(latest.values(), (file) => file.rel);
|
|
139
166
|
};
|
|
140
167
|
|
|
141
|
-
const stagePayload = ({ settings, node, metadata }: StageRequest): StagedFile[] => {
|
|
168
|
+
const stagePayload = ({ settings, node, addon, metadata }: StageRequest): StagedFile[] => {
|
|
142
169
|
const root = settings.paths.stage;
|
|
143
170
|
rmSync(root, { recursive: true, force: true, maxRetries: 5 });
|
|
144
171
|
|
|
145
|
-
|
|
172
|
+
const staged = byRelativePath([
|
|
146
173
|
writeInto(root, join("bin", settings.binaryName), renderLauncher(settings), EXECUTABLE_MODE),
|
|
147
174
|
...stageNodeBinary(settings, root, node),
|
|
148
175
|
...stageRuntimeFiles(settings, root),
|
|
176
|
+
...stageAddon(settings, root, addon),
|
|
149
177
|
...stageCatalogs(settings, root),
|
|
150
178
|
...stageExtraFiles(settings, root, metadata),
|
|
151
179
|
...stageMetadata(settings, root, metadata),
|
|
152
180
|
...stageIcons(settings, root),
|
|
153
181
|
...stageSchemas(settings, root),
|
|
154
182
|
]);
|
|
183
|
+
|
|
184
|
+
assertStagedArchitecture(settings, staged);
|
|
185
|
+
|
|
186
|
+
return staged;
|
|
155
187
|
};
|
|
156
188
|
|
|
157
189
|
const withoutDbusActivation = (desktopEntry: string): string =>
|