@gtkx/cli 2.0.0-beta.7 → 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.
Files changed (122) hide show
  1. package/dist/commands/deploy.d.ts +4 -0
  2. package/dist/commands/deploy.d.ts.map +1 -1
  3. package/dist/commands/deploy.js +6 -0
  4. package/dist/commands/deploy.js.map +1 -1
  5. package/dist/deploy/download.d.ts +7 -3
  6. package/dist/deploy/download.d.ts.map +1 -1
  7. package/dist/deploy/download.js +19 -15
  8. package/dist/deploy/download.js.map +1 -1
  9. package/dist/deploy/native-addon.d.ts +5 -0
  10. package/dist/deploy/native-addon.d.ts.map +1 -0
  11. package/dist/deploy/native-addon.js +134 -0
  12. package/dist/deploy/native-addon.js.map +1 -0
  13. package/dist/deploy/node-runtime/download.js +2 -2
  14. package/dist/deploy/node-runtime/download.js.map +1 -1
  15. package/dist/deploy/node-runtime/elf.d.ts +3 -1
  16. package/dist/deploy/node-runtime/elf.d.ts.map +1 -1
  17. package/dist/deploy/node-runtime/elf.js +3 -1
  18. package/dist/deploy/node-runtime/elf.js.map +1 -1
  19. package/dist/deploy/node-runtime/index.d.ts.map +1 -1
  20. package/dist/deploy/node-runtime/index.js +14 -2
  21. package/dist/deploy/node-runtime/index.js.map +1 -1
  22. package/dist/deploy/payload/stage.d.ts +2 -1
  23. package/dist/deploy/payload/stage.d.ts.map +1 -1
  24. package/dist/deploy/payload/stage.js +22 -2
  25. package/dist/deploy/payload/stage.js.map +1 -1
  26. package/dist/deploy/run-deploy.d.ts +1 -0
  27. package/dist/deploy/run-deploy.d.ts.map +1 -1
  28. package/dist/deploy/run-deploy.js +135 -79
  29. package/dist/deploy/run-deploy.js.map +1 -1
  30. package/dist/deploy/settings/arch.d.ts +9 -3
  31. package/dist/deploy/settings/arch.d.ts.map +1 -1
  32. package/dist/deploy/settings/arch.js +27 -6
  33. package/dist/deploy/settings/arch.js.map +1 -1
  34. package/dist/deploy/settings/index.d.ts +2 -1
  35. package/dist/deploy/settings/index.d.ts.map +1 -1
  36. package/dist/deploy/settings/index.js +2 -1
  37. package/dist/deploy/settings/index.js.map +1 -1
  38. package/dist/deploy/settings/paths.d.ts +2 -1
  39. package/dist/deploy/settings/paths.d.ts.map +1 -1
  40. package/dist/deploy/settings/paths.js +6 -5
  41. package/dist/deploy/settings/paths.js.map +1 -1
  42. package/dist/deploy/targets/appimage.d.ts.map +1 -1
  43. package/dist/deploy/targets/appimage.js +1 -0
  44. package/dist/deploy/targets/appimage.js.map +1 -1
  45. package/dist/deploy/targets/flatpak-pnpm.js +4 -4
  46. package/dist/deploy/targets/flatpak-pnpm.js.map +1 -1
  47. package/dist/deploy/targets/flatpak.d.ts.map +1 -1
  48. package/dist/deploy/targets/flatpak.js +1 -0
  49. package/dist/deploy/targets/flatpak.js.map +1 -1
  50. package/dist/deploy/targets/nfpm-target.d.ts.map +1 -1
  51. package/dist/deploy/targets/nfpm-target.js +1 -0
  52. package/dist/deploy/targets/nfpm-target.js.map +1 -1
  53. package/dist/deploy/types.d.ts +4 -2
  54. package/dist/deploy/types.d.ts.map +1 -1
  55. package/dist/deploy/types.js.map +1 -1
  56. package/dist/deploy/vendored/appimagetool.js +2 -2
  57. package/dist/deploy/vendored/appimagetool.js.map +1 -1
  58. package/dist/deploy/vendored/nfpm.js +1 -1
  59. package/dist/deploy/vendored/nfpm.js.map +1 -1
  60. package/dist/dev/font-dir.d.ts +1 -1
  61. package/dist/dev/font-dir.d.ts.map +1 -1
  62. package/dist/dev/font-dir.js +0 -3
  63. package/dist/dev/font-dir.js.map +1 -1
  64. package/dist/dev/runner.d.ts.map +1 -1
  65. package/dist/dev/runner.js +19 -6
  66. package/dist/dev/runner.js.map +1 -1
  67. package/dist/internal/font-staging.d.ts +5 -2
  68. package/dist/internal/font-staging.d.ts.map +1 -1
  69. package/dist/internal/font-staging.js +76 -27
  70. package/dist/internal/font-staging.js.map +1 -1
  71. package/dist/internal/source-imports.js +1 -1
  72. package/dist/internal/source-imports.js.map +1 -1
  73. package/dist/vite-plugins/font-worker-env.d.ts.map +1 -1
  74. package/dist/vite-plugins/font-worker-env.js +2 -5
  75. package/dist/vite-plugins/font-worker-env.js.map +1 -1
  76. package/dist/vite-plugins/font.d.ts.map +1 -1
  77. package/dist/vite-plugins/font.js +16 -4
  78. package/dist/vite-plugins/font.js.map +1 -1
  79. package/dist/vite-plugins/icons.js +2 -2
  80. package/dist/vite-plugins/icons.js.map +1 -1
  81. package/dist/vite-plugins/react-compiler.js +2 -2
  82. package/dist/vite-plugins/react-compiler.js.map +1 -1
  83. package/dist/vite-plugins/resources.js +2 -2
  84. package/dist/vite-plugins/resources.js.map +1 -1
  85. package/dist/vite-plugins/settings-worker-env.js +2 -2
  86. package/dist/vite-plugins/settings-worker-env.js.map +1 -1
  87. package/dist/vite-plugins/store-links.d.ts.map +1 -1
  88. package/dist/vite-plugins/store-links.js +2 -1
  89. package/dist/vite-plugins/store-links.js.map +1 -1
  90. package/dist/vite-plugins/undeclared-library.js +2 -2
  91. package/dist/vite-plugins/undeclared-library.js.map +1 -1
  92. package/package.json +13 -13
  93. package/src/commands/deploy.ts +6 -0
  94. package/src/deploy/download.ts +33 -17
  95. package/src/deploy/native-addon.ts +183 -0
  96. package/src/deploy/node-runtime/download.ts +2 -2
  97. package/src/deploy/node-runtime/elf.ts +4 -1
  98. package/src/deploy/node-runtime/index.ts +24 -4
  99. package/src/deploy/payload/stage.ts +34 -2
  100. package/src/deploy/run-deploy.ts +203 -93
  101. package/src/deploy/settings/arch.ts +40 -8
  102. package/src/deploy/settings/index.ts +4 -1
  103. package/src/deploy/settings/paths.ts +8 -6
  104. package/src/deploy/targets/appimage.ts +1 -0
  105. package/src/deploy/targets/flatpak-pnpm.ts +4 -4
  106. package/src/deploy/targets/flatpak.ts +1 -0
  107. package/src/deploy/targets/nfpm-target.ts +1 -0
  108. package/src/deploy/types.ts +4 -1
  109. package/src/deploy/vendored/appimagetool.ts +2 -2
  110. package/src/deploy/vendored/nfpm.ts +1 -1
  111. package/src/dev/font-dir.ts +1 -6
  112. package/src/dev/runner.ts +24 -6
  113. package/src/internal/font-staging.ts +99 -27
  114. package/src/internal/source-imports.ts +1 -1
  115. package/src/vite-plugins/font-worker-env.ts +2 -6
  116. package/src/vite-plugins/font.ts +21 -3
  117. package/src/vite-plugins/icons.ts +2 -2
  118. package/src/vite-plugins/react-compiler.ts +2 -2
  119. package/src/vite-plugins/resources.ts +2 -2
  120. package/src/vite-plugins/settings-worker-env.ts +2 -2
  121. package/src/vite-plugins/store-links.ts +2 -1
  122. package/src/vite-plugins/undeclared-library.ts +2 -2
@@ -3,7 +3,9 @@ import { info, warn } from "@gtkx/utils";
3
3
  import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join, relative } from "node:path";
5
5
  import type {
6
+ DeployArchName,
6
7
  DeployArtifact,
8
+ DeployConfig,
7
9
  DeployManifest,
8
10
  DeployPayload,
9
11
  DeploySettings,
@@ -29,10 +31,12 @@ import { extractMetadataMessages, localizeMetadata } from "./freedesktop/localiz
29
31
  import { renderMetainfo } from "./freedesktop/metainfo.js";
30
32
  import { renderMimePackage } from "./freedesktop/mime-package.js";
31
33
  import { validateDesktopEntry, validateMetainfo } from "./freedesktop/validate.js";
34
+ import { resolveStagedAddon } from "./native-addon.js";
32
35
  import { resolveNodeRuntime } from "./node-runtime/index.js";
33
36
  import { collectNotices } from "./notices/collect.js";
34
37
  import { type StagedMetadata, stageOverlays, stagePayload } from "./payload/stage.js";
35
38
  import { DEFAULT_TARGETS, parseTargetList, targetsFor } from "./registry.js";
39
+ import { archesFor, hostArchName, isHostArch, parseArchList } from "./settings/arch.js";
36
40
  import { readBuildManifest } from "./settings/build-manifest.js";
37
41
  import { resolveDeploySettings } from "./settings/index.js";
38
42
  import { readPackageManifest } from "./settings/package-manifest.js";
@@ -62,31 +66,52 @@ type DeployOptions = {
62
66
  cwd: string;
63
67
  configFile?: string | undefined;
64
68
  targets?: string | undefined;
69
+ arches?: string | undefined;
65
70
  outDir?: string | undefined;
66
71
  shouldPrintManifests: boolean;
67
72
  shouldSkipBuild: boolean;
68
73
  };
69
74
 
70
- type PreflightRequest = {
75
+ type LoadedDeployConfig = {
76
+ config: Config;
77
+ configFile: string;
78
+ root: string;
79
+ deploy: DeployConfig;
80
+ };
81
+
82
+ type DeployPlan = {
83
+ loaded: LoadedDeployConfig;
71
84
  targets: DeployTarget[];
72
- settings: DeploySettings;
85
+ arches: [DeployArchName, ...DeployArchName[]];
73
86
  project: CatalogProject | null;
87
+ };
88
+
89
+ type PreflightRequest = {
90
+ plan: DeployPlan;
91
+ settings: DeploySettings;
74
92
  shouldPrintManifests: boolean;
75
93
  shouldSkipBuild: boolean;
76
94
  };
77
95
 
78
- type LoadedDeploySettings = {
79
- config: Config;
80
- configFile: string;
96
+ type BuildApplicationRequest = {
97
+ options: DeployOptions;
98
+ plan: DeployPlan;
81
99
  settings: DeploySettings;
100
+ buildOutDir: string | null;
82
101
  };
83
102
 
84
103
  type BuildPayloadRequest = {
85
104
  options: DeployOptions;
86
- loaded: LoadedDeploySettings;
87
- targets: DeployTarget[];
88
- project: CatalogProject | null;
89
- buildOutDir: string | null;
105
+ plan: DeployPlan;
106
+ settings: DeploySettings;
107
+ metadata: StagedMetadata;
108
+ };
109
+
110
+ type ArchRunRequest = {
111
+ options: DeployOptions;
112
+ plan: DeployPlan;
113
+ arch: DeployArchName;
114
+ metadata: StagedMetadata;
90
115
  };
91
116
 
92
117
  const BUILD_MODE = "production";
@@ -229,17 +254,28 @@ const sourceModeTools = (targets: DeployTarget[], settings: DeploySettings): Dep
229
254
  const isNodeRequired = (targets: DeployTarget[], settings: DeploySettings): boolean =>
230
255
  !(settings.deploy.flatpak?.mode === "source" && targets.every((target) => target.name === FLATPAK_TARGET));
231
256
 
232
- const runtimeToolsFor = (targets: DeployTarget[], settings: DeploySettings): DeployTool[] => {
233
- if (!isNodeRequired(targets, settings)) {
257
+ const archiveToolsFor = (settings: DeploySettings): DeployTool[] =>
258
+ (settings.deploy.node?.source ?? "download") === "download" ? [TAR] : [];
259
+
260
+ const stripToolsFor = (settings: DeploySettings, arches: DeployArchName[]): DeployTool[] => {
261
+ if (settings.deploy.node?.shouldStrip === false) {
234
262
  return [];
235
263
  }
236
264
 
237
- const node = settings.deploy.node ?? {};
238
- const archiveTools = (node.source ?? "download") === "download" ? [TAR] : [];
265
+ return arches.some((arch) => isHostArch(arch)) ? [STRIP] : [];
266
+ };
267
+
268
+ const runtimeToolsFor = (targets: DeployTarget[], settings: DeploySettings, arches: DeployArchName[]): DeployTool[] => {
269
+ if (!isNodeRequired(targets, settings)) {
270
+ return [];
271
+ }
239
272
 
240
- return node.shouldStrip === false ? archiveTools : [...archiveTools, STRIP];
273
+ return [...archiveToolsFor(settings), ...stripToolsFor(settings, arches)];
241
274
  };
242
275
 
276
+ const crossBuildToolsFor = (arches: DeployArchName[]): DeployTool[] =>
277
+ arches.every((arch) => isHostArch(arch)) ? [] : [TAR];
278
+
243
279
  const mutableCatalogTools = (project: CatalogProject): DeployTool[] => {
244
280
  if (project.catalogs.length === 0) {
245
281
  return [MSGGREP, XGETTEXT];
@@ -266,20 +302,17 @@ const catalogTools = (project: CatalogProject | null, shouldSkipBuild: boolean):
266
302
  return project.catalogs.length === 0 ? [] : [MSGFMT];
267
303
  };
268
304
 
269
- const preflight = ({
270
- targets,
271
- settings,
272
- project,
273
- shouldPrintManifests,
274
- shouldSkipBuild,
275
- }: PreflightRequest): void => {
305
+ const preflight = ({ plan, settings, shouldPrintManifests, shouldSkipBuild }: PreflightRequest): void => {
306
+ const { targets, arches, project } = plan;
307
+
276
308
  const packagerTools = shouldPrintManifests
277
309
  ? []
278
- : [...runtimeToolsFor(targets, settings), ...targets.flatMap((target) => target.tools)];
310
+ : [...runtimeToolsFor(targets, settings, arches), ...targets.flatMap((target) => target.tools)];
279
311
 
280
312
  const required = [
281
313
  DESKTOP_FILE_VALIDATE,
282
314
  APPSTREAMCLI,
315
+ ...crossBuildToolsFor(arches),
283
316
  ...catalogTools(project, shouldSkipBuild),
284
317
  ...sourceModeTools(targets, settings),
285
318
  ];
@@ -291,15 +324,50 @@ const preflight = ({
291
324
  warnLibraryMinimums(targets, settings);
292
325
  };
293
326
 
294
- const resolveTargetNames = (options: DeployOptions, settings: DeploySettings): string[] => {
327
+ const hostOnlyNames = (targets: DeployTarget[]): string[] =>
328
+ targets.filter((target) => target.isHostOnly).map((target) => target.name);
329
+
330
+ const assertCrossBuild = (plan: DeployPlan): void => {
331
+ const foreign = plan.arches.filter((arch) => !isHostArch(arch));
332
+
333
+ if (foreign.length === 0) {
334
+ return;
335
+ }
336
+
337
+ const hostOnly = hostOnlyNames(plan.targets);
338
+
339
+ if (hostOnly.length > 0) {
340
+ throw new Error(
341
+ `Cannot build ${hostOnly.join(" and ")} for ${foreign.join(" and ")}: those targets package with ` +
342
+ `tooling that only runs on ${hostArchName()}. Deploy them in a separate run without --arch.`,
343
+ );
344
+ }
345
+
346
+ if ((plan.loaded.deploy.node?.source ?? "download") !== "download") {
347
+ throw new Error(
348
+ 'Cannot deploy for another architecture with a `deploy.node.source` other than "download": ' +
349
+ `a host or path runtime is always ${hostArchName()}.`,
350
+ );
351
+ }
352
+ };
353
+
354
+ const resolveTargetNames = (options: DeployOptions, deploy: DeployConfig): string[] => {
295
355
  if (options.targets !== undefined) {
296
356
  return parseTargetList(options.targets);
297
357
  }
298
358
 
299
- return settings.deploy.targets ?? DEFAULT_TARGETS;
359
+ return deploy.targets ?? DEFAULT_TARGETS;
360
+ };
361
+
362
+ const resolveArchNames = (options: DeployOptions, deploy: DeployConfig): string[] => {
363
+ if (options.arches !== undefined) {
364
+ return parseArchList(options.arches);
365
+ }
366
+
367
+ return deploy.architectures ?? [hostArchName()];
300
368
  };
301
369
 
302
- const loadSettings = async (options: DeployOptions): Promise<LoadedDeploySettings> => {
370
+ const loadDeployConfig = async (options: DeployOptions): Promise<LoadedDeployConfig> => {
303
371
  const { config, configFile, root } = await loadConfig(options.cwd, {
304
372
  mode: BUILD_MODE,
305
373
  configFile: options.configFile,
@@ -309,15 +377,26 @@ const loadSettings = async (options: DeployOptions): Promise<LoadedDeploySetting
309
377
  throw missingDeployError(config.applicationId, readPackageManifest(root));
310
378
  }
311
379
 
380
+ return { config, configFile, root, deploy: config.deploy };
381
+ };
382
+
383
+ const settingsFor = (plan: DeployPlan, options: DeployOptions, arch: DeployArchName): DeploySettings =>
384
+ resolveDeploySettings({
385
+ root: plan.loaded.root,
386
+ config: plan.loaded.config,
387
+ configFile: plan.loaded.configFile,
388
+ outDirOverride: options.outDir,
389
+ arch,
390
+ });
391
+
392
+ const planDeploy = async (options: DeployOptions): Promise<DeployPlan> => {
393
+ const loaded = await loadDeployConfig(options);
394
+
312
395
  return {
313
- config,
314
- configFile,
315
- settings: resolveDeploySettings({
316
- root,
317
- config,
318
- configFile,
319
- outDirOverride: options.outDir,
320
- }),
396
+ loaded,
397
+ targets: targetsFor(resolveTargetNames(options, loaded.deploy)),
398
+ arches: archesFor(resolveArchNames(options, loaded.deploy)),
399
+ project: resolveCatalogProject(loaded.root, loaded.config.applicationId),
321
400
  };
322
401
  };
323
402
 
@@ -347,46 +426,57 @@ const synchronizeMetadataCatalogs = async (
347
426
  }
348
427
  };
349
428
 
350
- const buildPayload = async ({
429
+ const buildApplication = async ({
351
430
  options,
352
- loaded,
353
- targets,
354
- project,
431
+ plan,
432
+ settings,
355
433
  buildOutDir,
356
- }: BuildPayloadRequest): Promise<DeployPayload> => {
357
- const settings = loaded.settings;
434
+ }: BuildApplicationRequest): Promise<StagedMetadata> => {
358
435
  const templates = renderMetadata(settings);
359
436
 
360
- if (buildOutDir !== null) {
361
- info(`Building ${options.entry}`);
362
- using preparedOutput = prepareBuildOutDir(settings.paths.root, buildOutDir);
363
-
364
- await buildApp({
365
- entry: options.entry,
366
- configFile: options.configFile,
367
- vite: { root: options.cwd, build: { outDir: preparedOutput.path, emptyOutDir: false } },
368
- });
437
+ if (buildOutDir === null) {
438
+ if (plan.project !== null) {
439
+ compileCatalogs(plan.project, join(settings.paths.dist, LOCALE_DIRNAME));
440
+ }
369
441
 
370
- preparedOutput.commit();
371
- await synchronizeMetadataCatalogs(settings, templates, project);
372
- } else if (project !== null) {
373
- compileCatalogs(project, join(settings.paths.dist, LOCALE_DIRNAME));
442
+ return localizeMetadata(templates, plan.project);
374
443
  }
375
444
 
376
- const metadata = localizeMetadata(templates, project);
377
- validateMetadata(settings, metadata, isFlathubSubmission(settings, targets));
378
- const buildManifest = readBuildManifest(settings, loaded);
445
+ info(`Building ${options.entry}`);
446
+ using preparedOutput = prepareBuildOutDir(settings.paths.root, buildOutDir);
447
+
448
+ await buildApp({
449
+ entry: options.entry,
450
+ configFile: options.configFile,
451
+ vite: { root: options.cwd, build: { outDir: preparedOutput.path, emptyOutDir: false } },
452
+ });
453
+
454
+ preparedOutput.commit();
455
+ await synchronizeMetadataCatalogs(settings, templates, plan.project);
456
+
457
+ return localizeMetadata(templates, plan.project);
458
+ };
459
+
460
+ const buildArchPayload = async ({
461
+ options,
462
+ plan,
463
+ settings,
464
+ metadata,
465
+ }: BuildPayloadRequest): Promise<DeployPayload> => {
466
+ validateMetadata(settings, metadata, isFlathubSubmission(settings, plan.targets));
467
+ const buildManifest = readBuildManifest(settings, plan.loaded);
379
468
 
380
469
  const builtSettings: DeploySettings = {
381
470
  ...settings,
382
471
  paths: { ...settings.paths, schemaFiles: buildManifest.schemaFiles },
383
472
  };
384
473
 
385
- const node = options.shouldPrintManifests || !isNodeRequired(targets, builtSettings)
474
+ const node = options.shouldPrintManifests || !isNodeRequired(plan.targets, builtSettings)
386
475
  ? null
387
476
  : await resolveNodeRuntime(builtSettings);
388
477
 
389
- const stage = stagePayload({ settings: builtSettings, node, metadata });
478
+ const addon = await resolveStagedAddon(builtSettings);
479
+ const stage = stagePayload({ settings: builtSettings, node, addon, metadata });
390
480
  info(`Staged ${String(stage.length)} files into ${displayPath(builtSettings, builtSettings.paths.stage)}`);
391
481
  const notices = collectNotices({ settings: builtSettings, node, packages: buildManifest.packages });
392
482
 
@@ -438,56 +528,76 @@ const packTargets = async (
438
528
  return artifacts;
439
529
  };
440
530
 
441
- const announce = (settings: DeploySettings, targets: DeployTarget[]): void => {
442
- const names = targets.map((target) => target.name).join(", ");
531
+ const announce = (settings: DeploySettings, plan: DeployPlan): void => {
532
+ const names = plan.targets.map((target) => target.name).join(", ");
443
533
  const version = `${settings.versions.packageVersion}-${settings.versions.debRevision}`;
444
- info(`Deploying ${settings.name} ${version} as ${settings.binaryName} (${settings.arch.rpm}) to ${names}`);
534
+ const arches = plan.arches.join(", ");
535
+ info(`Deploying ${settings.name} ${version} as ${settings.binaryName} (${arches}) to ${names}`);
445
536
  };
446
537
 
447
- const runDeploy = async (options: DeployOptions): Promise<void> => {
448
- let loaded = await loadSettings(options);
449
- let settings = loaded.settings;
450
- let project = resolveCatalogProject(settings.paths.root, settings.applicationId);
451
- let targets = targetsFor(resolveTargetNames(options, settings));
452
- let buildOutDir = options.shouldSkipBuild ? null : resolveBuildOutDir(settings.paths.root, settings.paths.dist);
453
- announce(settings, targets);
538
+ const deployArch = async (request: ArchRunRequest): Promise<DeployArtifact[]> => {
539
+ const { options, plan, arch, metadata } = request;
540
+ const settings = settingsFor(plan, options, arch);
541
+ const payload = await buildArchPayload({ options, plan, settings, metadata });
542
+ const rendered = renderTargetManifests(plan.targets, payload);
454
543
 
455
- preflight({
456
- targets,
457
- settings,
458
- project,
459
- shouldPrintManifests: options.shouldPrintManifests,
460
- shouldSkipBuild: options.shouldSkipBuild,
461
- });
544
+ if (options.shouldPrintManifests) {
545
+ const count = rendered.values().toArray().flat().length;
546
+ info(`Wrote ${String(count)} manifests for ${arch}, 0 packages built`);
462
547
 
463
- if (!options.shouldSkipBuild) {
464
- await ensureGenerated(options.cwd, {
465
- shouldAnnounce: true,
466
- mode: BUILD_MODE,
467
- configFile: options.configFile,
468
- });
469
-
470
- loaded = await loadSettings(options);
471
- settings = loaded.settings;
472
- project = resolveCatalogProject(settings.paths.root, settings.applicationId);
473
- targets = targetsFor(resolveTargetNames(options, settings));
474
- buildOutDir = resolveBuildOutDir(settings.paths.root, settings.paths.dist);
548
+ return [];
475
549
  }
476
550
 
477
- prepareDeployOutDir(settings.paths.root, settings.paths.outDir);
478
- const payload = await buildPayload({ options, loaded, targets, project, buildOutDir });
479
- const rendered = renderTargetManifests(targets, payload);
551
+ return await packTargets(rendered, payload);
552
+ };
480
553
 
481
- if (options.shouldPrintManifests) {
482
- const count = rendered.values().toArray().flat().length;
483
- info(`Wrote ${String(count)} manifests, 0 packages built`);
554
+ const regenerate = async (options: DeployOptions, plan: DeployPlan): Promise<DeployPlan> => {
555
+ if (options.shouldSkipBuild) {
556
+ return plan;
557
+ }
484
558
 
559
+ await ensureGenerated(options.cwd, {
560
+ shouldAnnounce: true,
561
+ mode: BUILD_MODE,
562
+ configFile: options.configFile,
563
+ });
564
+
565
+ return await planDeploy(options);
566
+ };
567
+
568
+ const finish = (options: DeployOptions, settings: DeploySettings, artifacts: DeployArtifact[]): void => {
569
+ if (options.shouldPrintManifests) {
485
570
  return;
486
571
  }
487
572
 
488
- const artifacts = await packTargets(rendered, payload);
489
573
  const output = displayPath(settings, settings.paths.output);
490
574
  info(`Deploy complete: ${String(artifacts.length)} artifacts in ${output}`);
491
575
  };
492
576
 
577
+ const runDeploy = async (options: DeployOptions): Promise<void> => {
578
+ const planned = await planDeploy(options);
579
+ assertCrossBuild(planned);
580
+ announce(settingsFor(planned, options, planned.arches[0]), planned);
581
+
582
+ preflight({
583
+ plan: planned,
584
+ settings: settingsFor(planned, options, planned.arches[0]),
585
+ shouldPrintManifests: options.shouldPrintManifests,
586
+ shouldSkipBuild: options.shouldSkipBuild,
587
+ });
588
+
589
+ const plan = await regenerate(options, planned);
590
+ const settings = settingsFor(plan, options, plan.arches[0]);
591
+ const buildOutDir = options.shouldSkipBuild ? null : resolveBuildOutDir(settings.paths.root, settings.paths.dist);
592
+ prepareDeployOutDir(settings.paths.root, settings.paths.outDir);
593
+ const metadata = await buildApplication({ options, plan, settings, buildOutDir });
594
+ const artifacts: DeployArtifact[] = [];
595
+
596
+ for (const arch of plan.arches) {
597
+ artifacts.push(...await deployArch({ options, plan, arch, metadata }));
598
+ }
599
+
600
+ finish(options, settings, artifacts);
601
+ };
602
+
493
603
  export { runDeploy };
@@ -1,18 +1,50 @@
1
- import type { DeployArch } from "../types.js";
1
+ import { sortStrings } from "@gtkx/utils";
2
+ import type { DeployArch, DeployArchName } from "../types.js";
2
3
 
3
- const ARCH_TABLE: Record<string, DeployArch> = {
4
+ const EM_X86_64 = 62;
5
+ const EM_AARCH64 = 183;
6
+
7
+ const ARCH_TABLE: Record<DeployArchName, DeployArch> = {
4
8
  arm64: { deb: "arm64", rpm: "aarch64", flatpak: "aarch64", appimage: "aarch64", node: "arm64" },
5
9
  x64: { deb: "amd64", rpm: "x86_64", flatpak: "x86_64", appimage: "x86_64", node: "x64" },
6
10
  };
7
11
 
8
- const resolveArch = (arch: string = process.arch): DeployArch => {
9
- const resolved = ARCH_TABLE[arch];
12
+ const ELF_MACHINE_TABLE: Record<DeployArchName, number> = {
13
+ arm64: EM_AARCH64,
14
+ x64: EM_X86_64,
15
+ };
16
+
17
+ const KNOWN_ARCH_NAMES = sortStrings(Object.keys(ARCH_TABLE)).join(", ");
18
+
19
+ const isArchName = (name: string): name is DeployArchName => Object.hasOwn(ARCH_TABLE, name);
20
+
21
+ const assertKnownArch = (name: string): DeployArchName => {
22
+ if (!isArchName(name)) {
23
+ throw new Error(`Cannot deploy for ${name}: only ${KNOWN_ARCH_NAMES} are supported`);
24
+ }
25
+
26
+ return name;
27
+ };
28
+
29
+ const resolveArch = (name: DeployArchName): DeployArch => ARCH_TABLE[name];
30
+
31
+ const hostArchName = (): DeployArchName => assertKnownArch(process.arch);
32
+
33
+ const isHostArch = (name: DeployArchName): boolean => name === hostArchName();
34
+
35
+ const elfMachineFor = (name: DeployArchName): number => ELF_MACHINE_TABLE[name];
36
+
37
+ const parseArchList = (value: string): string[] => value.split(",").map((name) => name.trim()).filter(Boolean);
38
+
39
+ const archesFor = (names: string[]): [DeployArchName, ...DeployArchName[]] => {
40
+ const requested = sortStrings(new Set(names.map((name) => assertKnownArch(name.trim()))));
41
+ const [first, ...rest] = requested.map((name) => assertKnownArch(name));
10
42
 
11
- if (resolved === undefined) {
12
- throw new Error(`Cannot deploy for ${arch}: only ${Object.keys(ARCH_TABLE).join(" and ")} are supported`);
43
+ if (first === undefined) {
44
+ throw new Error(`Cannot deploy without an architecture; choose from ${KNOWN_ARCH_NAMES}`);
13
45
  }
14
46
 
15
- return resolved;
47
+ return [first, ...rest];
16
48
  };
17
49
 
18
- export { resolveArch };
50
+ export { archesFor, elfMachineFor, hostArchName, isHostArch, KNOWN_ARCH_NAMES, parseArchList, resolveArch };
@@ -1,5 +1,6 @@
1
1
  import type { Config } from "@gtkx/config";
2
2
  import type {
3
+ DeployArchName,
3
4
  DeployConfig,
4
5
  DeployDesktopAction,
5
6
  DeployDeveloper,
@@ -32,6 +33,7 @@ type SettingsRequest = {
32
33
  root: string;
33
34
  config: Config;
34
35
  configFile: string;
36
+ arch: DeployArchName;
35
37
  outDirOverride?: string | undefined;
36
38
  now?: Date | undefined;
37
39
  };
@@ -181,13 +183,14 @@ const resolveDeploySettings = (request: SettingsRequest): DeploySettings => {
181
183
  ...desktopSlice(deploy),
182
184
  extraFiles: resolveExtraFiles(request.root, deploy),
183
185
  versions: core.versions,
184
- arch: resolveArch(),
186
+ arch: resolveArch(request.arch),
185
187
  paths: resolvePaths({
186
188
  root: request.root,
187
189
  deploy,
188
190
  applicationIcon: request.config.applicationIcon,
189
191
  applicationId: request.config.applicationId,
190
192
  outDirOverride: request.outDirOverride,
193
+ arch: request.arch,
191
194
  }),
192
195
  ...resolveLibraries(request.root, request.config),
193
196
  deploy,
@@ -2,7 +2,7 @@ import type { Config } from "@gtkx/config";
2
2
  import { isPathInside } from "@gtkx/utils";
3
3
  import { lstatSync, readdirSync, statSync, writeFileSync } from "node:fs";
4
4
  import { join, relative, resolve, sep } from "node:path";
5
- import type { DeployConfig, DeployPaths } from "../types.js";
5
+ import type { DeployArchName, DeployConfig, DeployPaths } from "../types.js";
6
6
  import { resolveApplicationIcon } from "../../internal/icon-path.js";
7
7
  import { prepareOutputDirectory, readRegularFile } from "../../internal/output-directory.js";
8
8
 
@@ -12,6 +12,7 @@ type PathsRequest = {
12
12
  applicationIcon: Config["applicationIcon"];
13
13
  applicationId: Config["applicationId"];
14
14
  outDirOverride: string | undefined;
15
+ arch: DeployArchName;
15
16
  };
16
17
 
17
18
  const DEFAULT_OUT_DIR = "build";
@@ -113,16 +114,17 @@ const resolveLicenseFile = (root: string, configured: string | undefined): strin
113
114
  const resolvePaths = (request: PathsRequest): DeployPaths => {
114
115
  const { root, deploy } = request;
115
116
  const outDir = resolveOutDir(request);
117
+ const archDir = join(outDir, request.arch);
116
118
 
117
119
  return {
118
120
  root,
119
121
  dist: join(root, DIST_DIR),
120
122
  outDir,
121
- metadata: join(outDir, "metadata"),
122
- runtime: join(outDir, "runtime"),
123
- stage: join(outDir, "stage"),
124
- overlay: join(outDir, "overlay"),
125
- targets: join(outDir, "targets"),
123
+ metadata: join(archDir, "metadata"),
124
+ runtime: join(archDir, "runtime"),
125
+ stage: join(archDir, "stage"),
126
+ overlay: join(archDir, "overlay"),
127
+ targets: join(archDir, "targets"),
126
128
  output: join(outDir, "out"),
127
129
  applicationIcon: resolveApplicationIcon(root, request.applicationId, request.applicationIcon),
128
130
  licenseFile: resolveLicenseFile(root, deploy.licenseFile),
@@ -29,6 +29,7 @@ const EXTRACT_AND_RUN = "APPIMAGE_EXTRACT_AND_RUN";
29
29
  const appimageTarget: DeployTarget = {
30
30
  name: "appimage",
31
31
  prefix: PREFIX,
32
+ isHostOnly: true,
32
33
  tools: [FILE_TOOL],
33
34
  render: (payload) => renderManifests(payload),
34
35
  pack: (payload) => packAppImage(payload),
@@ -9,12 +9,12 @@ type PnpmPin = {
9
9
  sha512: string;
10
10
  };
11
11
 
12
- const PNPM_VERSION = "11.25.0";
12
+ const PNPM_VERSION = "11.26.0";
13
13
  const PNPM_MAJOR = 11;
14
- const PNPM_MINOR = 25;
14
+ const PNPM_MINOR = 26;
15
15
 
16
- const PNPM_SHA512 = "5cde925b4f075f725eb71fbae18a42ffe784524789f19b61c731cb8721ec28aaee160e01a" +
17
- "8d5af4fedb2a42cdbf300efe23db356b0d4a17b4d63e11f8ab7c956";
16
+ const PNPM_SHA512 = "fc0e2bf890b9f983611f1ab68c0637bce914390653699f83c1a78b005ed25f2c81e77920c8" +
17
+ "fd8eee2ecf0b58b28cdcb00a84d97f69bcf7c56b2f344710238664";
18
18
 
19
19
  const PNPM_DIR = "flatpak-pnpm";
20
20
  const PNPM_COMMAND = "pnpm";
@@ -21,6 +21,7 @@ const BUILDER_REF = "org.flatpak.Builder";
21
21
  const flatpakTarget: DeployTarget = {
22
22
  name: "flatpak",
23
23
  prefix: PREFIX,
24
+ isHostOnly: true,
24
25
  tools: [FLATPAK, FLATPAK_BUILDER, DESKTOP_FILE_VALIDATE, APPSTREAMCLI],
25
26
  render: (payload) => renderManifests(payload),
26
27
  pack: (payload) => Promise.try(() => packFlatpak(payload)),
@@ -32,6 +32,7 @@ const packPackage = async (payload: DeployPayload, packager: NfpmPackager): Prom
32
32
  const nfpmTarget = (packager: NfpmPackager): DeployTarget => ({
33
33
  name: packager,
34
34
  prefix: PREFIX,
35
+ isHostOnly: false,
35
36
  tools: [TAR],
36
37
  render: (payload) => renderManifests(payload, packager),
37
38
  pack: (payload) => packPackage(payload, packager),
@@ -2,6 +2,7 @@ import type { Config } from "@gtkx/config";
2
2
  import type { ResolvedApplicationIcon } from "../internal/icon-path.js";
3
3
 
4
4
  type DeployConfig = NonNullable<Config["deploy"]>;
5
+ type DeployArchName = NonNullable<DeployConfig["architectures"]>[number];
5
6
  type DeployTargetName = NonNullable<DeployConfig["targets"]>[number];
6
7
  type PackageFamily = "arch" | "debian" | "fedora" | "suse" | "unknown";
7
8
 
@@ -94,7 +95,7 @@ type DeployArch = {
94
95
  rpm: string;
95
96
  flatpak: string;
96
97
  appimage: string;
97
- node: string;
98
+ node: DeployArchName;
98
99
  };
99
100
 
100
101
  type DeployPaths = {
@@ -170,6 +171,7 @@ type DeployArtifact = {
170
171
  type DeployTarget = {
171
172
  name: DeployTargetName;
172
173
  prefix: string;
174
+ isHostOnly: boolean;
173
175
  tools: DeployTool[];
174
176
  render: (payload: DeployPayload) => DeployManifest[];
175
177
  pack: (payload: DeployPayload, manifests: DeployManifest[]) => Promise<DeployArtifact[]>;
@@ -177,6 +179,7 @@ type DeployTarget = {
177
179
 
178
180
  export type {
179
181
  DeployArch,
182
+ DeployArchName,
180
183
  DeployArtifact,
181
184
  DeployConfig,
182
185
  DeployDesktopAction,
@@ -39,7 +39,7 @@ const resolveAppimageTooling = async (arch: string): Promise<AppimageTooling> =>
39
39
  url: `${APPIMAGETOOL_URL}/appimagetool-${arch}.AppImage`,
40
40
  dest: join(dir, `appimagetool-${arch}`),
41
41
  label: `appimagetool ${APPIMAGETOOL_VERSION}`,
42
- sha256: digestFor(APPIMAGETOOL_DIGESTS, arch, "appimagetool"),
42
+ digest: digestFor(APPIMAGETOOL_DIGESTS, arch, "appimagetool"),
43
43
  mode: EXECUTABLE_MODE,
44
44
  });
45
45
 
@@ -47,7 +47,7 @@ const resolveAppimageTooling = async (arch: string): Promise<AppimageTooling> =>
47
47
  url: `${RUNTIME_URL}/runtime-${arch}`,
48
48
  dest: join(dir, `runtime-${arch}`),
49
49
  label: `the AppImage runtime ${RUNTIME_VERSION}`,
50
- sha256: digestFor(RUNTIME_DIGESTS, arch, "the AppImage runtime"),
50
+ digest: digestFor(RUNTIME_DIGESTS, arch, "the AppImage runtime"),
51
51
  mode: EXECUTABLE_MODE,
52
52
  });
53
53
 
@@ -56,7 +56,7 @@ const downloadNfpm = async (dir: string, binary: string): Promise<string> => {
56
56
  url: `${NFPM_BASE_URL}/${assetName}`,
57
57
  dest: join(dir, assetName),
58
58
  label: `nfpm ${NFPM_VERSION}`,
59
- sha256: digestFor(process.arch),
59
+ digest: digestFor(process.arch),
60
60
  });
61
61
 
62
62
  extractNfpm(archive, binary);