@gjsify/cli 0.43.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.gjs.mjs +426 -425
- package/lib/actions/build.js +2 -1
- package/lib/commands/affected-classify.js +7 -5
- package/lib/commands/build.js +6 -0
- package/lib/commands/flatpak/ci.js +10 -2
- package/lib/commands/flatpak/init.js +15 -16
- package/lib/commands/flatpak/utils.d.ts +0 -26
- package/lib/commands/flatpak/utils.js +4 -29
- package/lib/commands/ship.js +90 -28
- package/lib/config.js +25 -0
- package/lib/types/cli-build-options.d.ts +11 -0
- package/lib/types/config-data.d.ts +74 -2
- package/lib/utils/flatpak-runtime.d.ts +41 -0
- package/lib/utils/flatpak-runtime.js +54 -0
- package/lib/utils/ship/depends.d.ts +4 -7
- package/lib/utils/ship/depends.js +3 -3
- package/lib/utils/ship/flatpak-config.d.ts +66 -0
- package/lib/utils/ship/flatpak-config.js +132 -0
- package/lib/utils/ship/flatpak.d.ts +45 -0
- package/lib/utils/ship/flatpak.js +148 -0
- package/lib/utils/ship/formats.d.ts +35 -0
- package/lib/utils/ship/formats.js +145 -6
- package/lib/utils/ship/settings.js +7 -0
- package/lib/utils/ship/stage-manifest.d.ts +11 -2
- package/lib/utils/ship/stage-manifest.js +39 -2
- package/lib/utils/ship/types.d.ts +116 -13
- package/package.json +19 -19
package/lib/actions/build.js
CHANGED
|
@@ -630,7 +630,7 @@ export class BuildAction {
|
|
|
630
630
|
throw new Error('gjsify build: an .html entry needs an explicit --outfile ' +
|
|
631
631
|
'(e.g. --outfile dist-app/app.js); the emitted index.html is written beside it.');
|
|
632
632
|
}
|
|
633
|
-
const { consoleShim, globals } = this.configData;
|
|
633
|
+
const { consoleShim, globals, dialect } = this.configData;
|
|
634
634
|
const userExternal = Array.isArray(userBundler.external) ? userBundler.external : undefined;
|
|
635
635
|
const userBanner = typeof userBundler.output?.banner === 'string' ? userBundler.output.banner : undefined;
|
|
636
636
|
const pluginOpts = {
|
|
@@ -640,6 +640,7 @@ export class BuildAction {
|
|
|
640
640
|
exclude,
|
|
641
641
|
reflection: typescript?.reflection,
|
|
642
642
|
consoleShim,
|
|
643
|
+
...(dialect !== undefined ? { dialect } : {}),
|
|
643
644
|
...(aliases ? { aliases } : {}),
|
|
644
645
|
...(opts.preserveDefaultExport ? { preserveDefaultExport: true } : {}),
|
|
645
646
|
...(opts.toolchainAnchor !== undefined ? { toolchainAnchor: opts.toolchainAnchor } : {}),
|
|
@@ -85,11 +85,13 @@ const IGNORE = [
|
|
|
85
85
|
/^docs\//,
|
|
86
86
|
...OTHER_WORKFLOW_INPUTS,
|
|
87
87
|
// Neither `@gjsify/node-gi` (the Node-native GI engine) nor `@gjsify/napi` (the
|
|
88
|
-
// N-API host over SpiderMonkey) is a gjsify workspace member
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
88
|
+
// N-API host over SpiderMonkey) is a gjsify workspace member — WHY is ADR 0031,
|
|
89
|
+
// which this comment used to carry alone. The short of it: node-gi's `build` is
|
|
90
|
+
// `node-gyp rebuild`, so a member would enter `foreach build` in a container with
|
|
91
|
+
// no GTK/GI toolchain. `main.yml` therefore neither builds nor tests them and
|
|
92
|
+
// their own workflows are the source of truth. Without the carve-outs their files
|
|
93
|
+
// map to no workspace, land in `unmatched`, and force a full run on every
|
|
94
|
+
// node-gi/napi PR.
|
|
93
95
|
/^packages\/node-gi\//,
|
|
94
96
|
/^packages\/napi\//,
|
|
95
97
|
// Flatpak build/distribution tooling (SDK-extension manifest + metainfo). No
|
package/lib/commands/build.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Config } from '../config.js';
|
|
2
2
|
import { BuildAction } from '../actions/build.js';
|
|
3
|
+
import { SOURCE_DIALECTS } from '@gjsify/rolldown-plugin-gjsify';
|
|
3
4
|
// NO `normalize: true` ON THE GLOB-BEARING OPTIONS — `entry-points` and
|
|
4
5
|
// `exclude` are PATTERNS, not paths.
|
|
5
6
|
//
|
|
@@ -106,6 +107,11 @@ export const buildCommand = {
|
|
|
106
107
|
type: 'string',
|
|
107
108
|
choices: ['silent', 'error', 'warning', 'info', 'debug', 'verbose'],
|
|
108
109
|
default: 'warning',
|
|
110
|
+
})
|
|
111
|
+
.option('dialect', {
|
|
112
|
+
description: "The dialect the SOURCE is written in. 'react-native' aliases 'react-native' onto '@gjsify/react-native' and fails the build on an import whose ADR 0032 support-table status is not supported/partial. GJS and Node app builds. This is NOT gjsify.runtimes['react-native'], which declares the runtime a package RUNS on — the two answer opposite questions.",
|
|
113
|
+
type: 'string',
|
|
114
|
+
choices: [...SOURCE_DIALECTS],
|
|
109
115
|
})
|
|
110
116
|
.option('console-shim', {
|
|
111
117
|
description: 'Inject a console shim into GJS builds for clean output without the GLib prefix and with working ANSI colors. Use --no-console-shim to disable. Only applies to GJS app builds.',
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import { dirname, resolve } from 'node:path';
|
|
11
11
|
import { Config } from '../../config.js';
|
|
12
|
-
import { defaultCiContainer, looksLikeAppId, readPackageJson
|
|
12
|
+
import { defaultCiContainer, looksLikeAppId, readPackageJson } from './utils.js';
|
|
13
|
+
import { resolveRuntime } from '../../utils/flatpak-runtime.js';
|
|
14
|
+
import { pickFlatpakBuildKeys } from '../../utils/ship/flatpak-config.js';
|
|
13
15
|
export const flatpakCiCommand = {
|
|
14
16
|
command: 'ci',
|
|
15
17
|
description: 'Scaffold .github/workflows/flatpak.yml using the flathub-infra container + flatpak-builder@v6 action.',
|
|
@@ -68,7 +70,13 @@ export const flatpakCiCommand = {
|
|
|
68
70
|
}
|
|
69
71
|
const manifest = args.manifest ?? `${appId}.json`;
|
|
70
72
|
const bundle = args.bundle ?? `${appId}.flatpak`;
|
|
71
|
-
|
|
73
|
+
// Through the deprecation window, not straight off `gjsify.flatpak`: a
|
|
74
|
+
// project told to move these keys to `gjsify.ship.flatpak` must not lose
|
|
75
|
+
// them here — the CI container tag is derived from the runtime, so
|
|
76
|
+
// reading the old block alone scaffolds a workflow against the default
|
|
77
|
+
// GNOME release rather than the pinned one.
|
|
78
|
+
const buildKeys = pickFlatpakBuildKeys(configData.ship?.flatpak, flatpak).values;
|
|
79
|
+
const { runtime, runtimeVersion } = resolveRuntime(buildKeys, {});
|
|
72
80
|
const runtimeImage = args.runtimeImage ??
|
|
73
81
|
flatpak.ciContainer ??
|
|
74
82
|
defaultCiContainer(runtime, runtimeVersion);
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
// with `skip-icons-check`.
|
|
14
14
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
15
15
|
import { dirname, resolve } from 'node:path';
|
|
16
|
-
import {
|
|
16
|
+
import { looksLikeAppId, readPackageJson } from './utils.js';
|
|
17
|
+
import { DEFAULT_CLI_FINISH_ARGS, DEFAULT_GUI_FINISH_ARGS, deriveAppendPath, resolveRuntime, } from '../../utils/flatpak-runtime.js';
|
|
18
|
+
import { pickFlatpakBuildKeys } from '../../utils/ship/flatpak-config.js';
|
|
17
19
|
import { renderDesktop, renderFlathubJson, renderMetainfoApp, renderMetainfoCli, validateScaffoldInputs, } from './scaffold.js';
|
|
18
20
|
import { Config } from '../../config.js';
|
|
19
21
|
import { OxcNotFoundError, hasOxcDevDep, runOxfmt } from '../../utils/oxc-resolve.js';
|
|
@@ -109,17 +111,24 @@ export const flatpakInitCommand = {
|
|
|
109
111
|
'or rename the package to a reverse-DNS id like org.example.MyApp.');
|
|
110
112
|
}
|
|
111
113
|
const kind = args.kind ?? flatpak.kind ?? (args.cliOnly ? 'cli' : 'app');
|
|
112
|
-
|
|
114
|
+
// The six BUILD keys come through the deprecation window
|
|
115
|
+
// (`utils/ship/flatpak-config.ts`), not straight off `gjsify.flatpak`.
|
|
116
|
+
// These commands did NOT move, but the keys did: a project that followed
|
|
117
|
+
// `ship`'s own advice and moved them would otherwise lose them here and
|
|
118
|
+
// get a committed manifest against a different runtime version with
|
|
119
|
+
// different finish-args, silently, at exit 0.
|
|
120
|
+
const buildKeys = pickFlatpakBuildKeys(configData.ship?.flatpak, flatpak).values;
|
|
121
|
+
const { runtime, runtimeId, sdk, runtimeVersion } = resolveRuntime(buildKeys, {
|
|
113
122
|
runtime: args.runtime,
|
|
114
123
|
runtimeVersion: args.runtimeVersion,
|
|
115
124
|
});
|
|
116
|
-
const sdkExtensions = mergeArrays(
|
|
117
|
-
const appendPath =
|
|
125
|
+
const sdkExtensions = mergeArrays(buildKeys.sdkExtensions, args.sdkExtension);
|
|
126
|
+
const appendPath = buildKeys.appendPath ?? (sdkExtensions?.length ? deriveAppendPath(sdkExtensions) : undefined);
|
|
118
127
|
const command = args.command ?? flatpak.command ?? appId;
|
|
119
128
|
const explicitFinishArgs = args.finishArg;
|
|
120
129
|
const finishArgs = explicitFinishArgs !== undefined
|
|
121
130
|
? explicitFinishArgs
|
|
122
|
-
: (
|
|
131
|
+
: (buildKeys.finishArgs ?? (kind === 'cli' ? DEFAULT_CLI_FINISH_ARGS : DEFAULT_GUI_FINISH_ARGS));
|
|
123
132
|
const manifest = {
|
|
124
133
|
id: appId,
|
|
125
134
|
runtime: runtimeId,
|
|
@@ -133,7 +142,7 @@ export const flatpakInitCommand = {
|
|
|
133
142
|
}
|
|
134
143
|
manifest.command = command;
|
|
135
144
|
manifest['finish-args'] = finishArgs;
|
|
136
|
-
const cleanup =
|
|
145
|
+
const cleanup = buildKeys.cleanup;
|
|
137
146
|
if (cleanup?.length)
|
|
138
147
|
manifest.cleanup = cleanup;
|
|
139
148
|
// Module assembly. Two precedence rules:
|
|
@@ -253,16 +262,6 @@ function mergeArrays(a, b) {
|
|
|
253
262
|
return undefined;
|
|
254
263
|
return [...(a ?? []), ...(b ?? [])];
|
|
255
264
|
}
|
|
256
|
-
function deriveAppendPath(sdkExtensions) {
|
|
257
|
-
const out = [];
|
|
258
|
-
for (const ext of sdkExtensions) {
|
|
259
|
-
const m = /^org\.freedesktop\.Sdk\.Extension\.([A-Za-z0-9-]+)$/.exec(ext);
|
|
260
|
-
if (m)
|
|
261
|
-
out.push(`/usr/lib/sdk/${m[1]}/bin`);
|
|
262
|
-
}
|
|
263
|
-
out.push('/app/bin');
|
|
264
|
-
return out;
|
|
265
|
-
}
|
|
266
265
|
function deriveModuleName(appId) {
|
|
267
266
|
const parts = appId.split('.');
|
|
268
267
|
return parts[parts.length - 1] || appId;
|
|
@@ -1,32 +1,6 @@
|
|
|
1
|
-
import type { ConfigDataFlatpak } from '../../types/config-data.js';
|
|
2
|
-
/**
|
|
3
|
-
* Default GNOME-Platform runtime version. Bumped per release window.
|
|
4
|
-
* GNOME 50 = April 2026 stable; tracked in
|
|
5
|
-
* https://docs.flathub.org/docs/for-app-authors/requirements.
|
|
6
|
-
*/
|
|
7
|
-
export declare const DEFAULT_GNOME_RUNTIME_VERSION = "50";
|
|
8
|
-
/** Default Freedesktop-Platform runtime version (LTS-ish). */
|
|
9
|
-
export declare const DEFAULT_FREEDESKTOP_RUNTIME_VERSION = "24.08";
|
|
10
|
-
/** Permissive GUI defaults for GTK4 + Adwaita apps. */
|
|
11
|
-
export declare const DEFAULT_GUI_FINISH_ARGS: string[];
|
|
12
|
-
/** Lean defaults for headless CLI tools — no display, no GPU. */
|
|
13
|
-
export declare const DEFAULT_CLI_FINISH_ARGS: string[];
|
|
14
1
|
/** Read package.json from a directory. Throws a helpful error if missing/invalid. */
|
|
15
2
|
export declare function readPackageJson(dir: string): Record<string, unknown>;
|
|
16
3
|
/** True if a name string looks like a reverse-DNS Flatpak app id. */
|
|
17
4
|
export declare function looksLikeAppId(value: unknown): value is string;
|
|
18
|
-
/**
|
|
19
|
-
* Pick the runtime + sdk + version triple from config + CLI overrides.
|
|
20
|
-
* `--runtime` and `--runtime-version` flags win over config values.
|
|
21
|
-
*/
|
|
22
|
-
export declare function resolveRuntime(flatpak: ConfigDataFlatpak | undefined, overrides: {
|
|
23
|
-
runtime?: string;
|
|
24
|
-
runtimeVersion?: string;
|
|
25
|
-
}): {
|
|
26
|
-
runtime: 'gnome' | 'freedesktop';
|
|
27
|
-
runtimeId: string;
|
|
28
|
-
sdk: string;
|
|
29
|
-
runtimeVersion: string;
|
|
30
|
-
};
|
|
31
5
|
/** Default container image for the GitHub Actions workflow. */
|
|
32
6
|
export declare function defaultCiContainer(runtime: 'gnome' | 'freedesktop', runtimeVersion: string): string;
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
// Shared helpers for the `gjsify flatpak <sub>` subcommand group.
|
|
2
|
+
//
|
|
3
|
+
// The runtime/SDK/finish-args half moved to `utils/flatpak-runtime.ts` when
|
|
4
|
+
// `gjsify ship --target flatpak` became its second caller — see that file's
|
|
5
|
+
// header. What is left here is what only these subcommands ask.
|
|
2
6
|
import { readFileSync } from 'node:fs';
|
|
3
7
|
import { resolve } from 'node:path';
|
|
4
|
-
/**
|
|
5
|
-
* Default GNOME-Platform runtime version. Bumped per release window.
|
|
6
|
-
* GNOME 50 = April 2026 stable; tracked in
|
|
7
|
-
* https://docs.flathub.org/docs/for-app-authors/requirements.
|
|
8
|
-
*/
|
|
9
|
-
export const DEFAULT_GNOME_RUNTIME_VERSION = '50';
|
|
10
|
-
/** Default Freedesktop-Platform runtime version (LTS-ish). */
|
|
11
|
-
export const DEFAULT_FREEDESKTOP_RUNTIME_VERSION = '24.08';
|
|
12
|
-
/** Permissive GUI defaults for GTK4 + Adwaita apps. */
|
|
13
|
-
export const DEFAULT_GUI_FINISH_ARGS = ['--device=dri', '--share=ipc', '--socket=fallback-x11', '--socket=wayland'];
|
|
14
|
-
/** Lean defaults for headless CLI tools — no display, no GPU. */
|
|
15
|
-
export const DEFAULT_CLI_FINISH_ARGS = [];
|
|
16
8
|
/** Read package.json from a directory. Throws a helpful error if missing/invalid. */
|
|
17
9
|
export function readPackageJson(dir) {
|
|
18
10
|
const path = resolve(dir, 'package.json');
|
|
@@ -34,23 +26,6 @@ export function readPackageJson(dir) {
|
|
|
34
26
|
export function looksLikeAppId(value) {
|
|
35
27
|
return typeof value === 'string' && /^[A-Za-z][A-Za-z0-9_-]*(\.[A-Za-z][A-Za-z0-9_-]*){2,}$/.test(value);
|
|
36
28
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Pick the runtime + sdk + version triple from config + CLI overrides.
|
|
39
|
-
* `--runtime` and `--runtime-version` flags win over config values.
|
|
40
|
-
*/
|
|
41
|
-
export function resolveRuntime(flatpak, overrides) {
|
|
42
|
-
const runtime = (overrides.runtime ?? flatpak?.runtime ?? 'gnome');
|
|
43
|
-
if (runtime !== 'gnome' && runtime !== 'freedesktop') {
|
|
44
|
-
throw new Error(`gjsify flatpak: unknown runtime "${runtime}" (expected "gnome" or "freedesktop")`);
|
|
45
|
-
}
|
|
46
|
-
const runtimeVersion = overrides.runtimeVersion ??
|
|
47
|
-
flatpak?.runtimeVersion ??
|
|
48
|
-
(runtime === 'gnome' ? DEFAULT_GNOME_RUNTIME_VERSION : DEFAULT_FREEDESKTOP_RUNTIME_VERSION);
|
|
49
|
-
if (runtime === 'gnome') {
|
|
50
|
-
return { runtime, runtimeId: 'org.gnome.Platform', sdk: 'org.gnome.Sdk', runtimeVersion };
|
|
51
|
-
}
|
|
52
|
-
return { runtime, runtimeId: 'org.freedesktop.Platform', sdk: 'org.freedesktop.Sdk', runtimeVersion };
|
|
53
|
-
}
|
|
54
29
|
/** Default container image for the GitHub Actions workflow. */
|
|
55
30
|
export function defaultCiContainer(runtime, runtimeVersion) {
|
|
56
31
|
const tag = `${runtime}-${runtimeVersion}`;
|
package/lib/commands/ship.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// this command, are the later stages of the same ADR — and they are the reason
|
|
21
21
|
// the split exists at all: a `.dmg` is a UDIF image over a real HFS+/APFS
|
|
22
22
|
// volume and no Linux host in this tree can write one (§ A1).
|
|
23
|
-
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
24
24
|
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
25
25
|
import { Config } from '../config.js';
|
|
26
26
|
import { renderDesktopEntry, renderMetainfoApp, renderMetainfoCli, validateAppMetadata, } from '../utils/app-metadata.js';
|
|
@@ -29,24 +29,31 @@ import { describeExit, spawnToCompletion } from '../utils/spawn.js';
|
|
|
29
29
|
import { buildDeb } from '../utils/ship/deb.js';
|
|
30
30
|
import { deriveDepends, warnAboutGjsFloor } from '../utils/ship/depends.js';
|
|
31
31
|
import { discoverPayload } from '../utils/ship/discover.js';
|
|
32
|
-
import {
|
|
32
|
+
import { buildFlatpakBundle } from '../utils/ship/flatpak.js';
|
|
33
|
+
import { assertHostCanFinish, assertToolsInstalled, resolveFormats, DEFAULT_FORMAT_IDS, FORMAT_IDS, } from '../utils/ship/formats.js';
|
|
33
34
|
import { scanGiNamespaces } from '../utils/ship/gi-namespaces.js';
|
|
34
35
|
import { assertPayloadMatchesArch, buildTimestamp, isArchIndependent, readPayloadFacts, } from '../utils/ship/payload.js';
|
|
35
36
|
import { assertOverlayIsLicensed, planOverlay, planStage } from '../utils/ship/plan.js';
|
|
36
37
|
import { renderLauncher } from '../utils/ship/launcher.js';
|
|
37
38
|
import { buildRpm } from '../utils/ship/rpm.js';
|
|
38
39
|
import { declaredBundlePath, resolveShipSettings } from '../utils/ship/settings.js';
|
|
39
|
-
import { assertExpectedTarget, assertFormatsStaged, formatTarget, overlayFiles, readStageManifest, resolveStageMtime, warnOnToolSkew, writeStageManifest, } from '../utils/ship/stage-manifest.js';
|
|
40
|
+
import { assertExpectedTarget, assertFormatsStaged, formatTarget, overlayFiles, readStageManifest, resolveStageMtime, warnOnToolSkew, writeStageManifest, STAGE_MANIFEST_FILE, } from '../utils/ship/stage-manifest.js';
|
|
40
41
|
import { readStage, writeStage } from '../utils/ship/stage-writer.js';
|
|
41
42
|
const LOG = '[gjsify ship]';
|
|
42
43
|
export const shipCommand = {
|
|
43
44
|
command: 'ship',
|
|
44
45
|
description: `Build installable artifacts (${FORMAT_IDS.join(', ')}) from the current project.`,
|
|
46
|
+
// `DEFAULT_FORMAT_IDS` in the flag text, `FORMAT_IDS` in the description:
|
|
47
|
+
// one of them answers "what can this build", the other "what will it build
|
|
48
|
+
// if I say nothing", and they stopped being the same list when a host-bound
|
|
49
|
+
// format arrived.
|
|
45
50
|
builder: (yargs) => yargs
|
|
46
51
|
.option('target', {
|
|
47
52
|
type: 'string',
|
|
48
53
|
array: true,
|
|
49
|
-
description: `Formats to build, comma-separated or repeated. Default: ${
|
|
54
|
+
description: `Formats to build, comma-separated or repeated. Default: ${DEFAULT_FORMAT_IDS.join(',')}. ` +
|
|
55
|
+
`Also available, and opt-in because it needs tooling this CLI does not carry: ` +
|
|
56
|
+
`${FORMAT_IDS.filter((id) => !DEFAULT_FORMAT_IDS.includes(id)).join(', ')}.`,
|
|
50
57
|
})
|
|
51
58
|
.option('out', {
|
|
52
59
|
type: 'string',
|
|
@@ -81,7 +88,8 @@ export const shipCommand = {
|
|
|
81
88
|
.option('verbose', {
|
|
82
89
|
type: 'boolean',
|
|
83
90
|
default: false,
|
|
84
|
-
description: 'Print each staged file
|
|
91
|
+
description: 'Print each staged file, the GI namespaces the bundle imports, and every tool a ' +
|
|
92
|
+
'host-bound packer invokes.',
|
|
85
93
|
}),
|
|
86
94
|
handler: async (args) => {
|
|
87
95
|
// The two phases share nothing but `packOne`, and the branch is first
|
|
@@ -111,7 +119,15 @@ async function assemble(args) {
|
|
|
111
119
|
const pkg = (readPackageJson(join(projectDir, 'package.json')) ?? {});
|
|
112
120
|
// Resolved before anything is built or written: a typo'd `--target`
|
|
113
121
|
// should not cost a build and leave a half-written `ship/stage/`.
|
|
114
|
-
const formats = resolveFormats(args.target ?? ship.targets ??
|
|
122
|
+
const formats = resolveFormats(args.target ?? ship.targets ?? DEFAULT_FORMAT_IDS);
|
|
123
|
+
// BEFORE the build, not before the pack. `gjsify ship` runs the project's
|
|
124
|
+
// own `build` script first, so discovering a missing `flatpak-builder`
|
|
125
|
+
// afterwards costs the whole build for a refusal that was knowable up
|
|
126
|
+
// front. Skipped under `--stage`, which is precisely the phase that does
|
|
127
|
+
// NOT need the format's tooling — that asymmetry is the point of the split.
|
|
128
|
+
if (!args.stage)
|
|
129
|
+
for (const format of formats)
|
|
130
|
+
assertCanPack(format);
|
|
115
131
|
if (!args['skip-build'])
|
|
116
132
|
await runProjectBuild(projectDir);
|
|
117
133
|
const discovered = discoverPayload({
|
|
@@ -189,17 +205,26 @@ async function assemble(args) {
|
|
|
189
205
|
// paths the payload will have.
|
|
190
206
|
const facts = readPayloadFacts(staged);
|
|
191
207
|
for (const format of formats) {
|
|
192
|
-
|
|
208
|
+
// `format.depends`, not `format.id`: a Flatpak has no package
|
|
209
|
+
// dependency list at all, and asking for one would either invent a
|
|
210
|
+
// third column in the typelib table or silently reuse rpm's — which is
|
|
211
|
+
// the exact defect `check-ship-format-vocabulary.mjs`'s header records
|
|
212
|
+
// from the other direction (a third format taking rpm's package name
|
|
213
|
+
// into a Debian `Depends:`, at exit 0).
|
|
214
|
+
if (format.depends === null)
|
|
215
|
+
continue;
|
|
216
|
+
deriveDepends(format.depends, {
|
|
193
217
|
namespaces,
|
|
194
218
|
hasIcons: facts.hasIcons,
|
|
195
219
|
hasSchemas: facts.hasSchemas,
|
|
196
|
-
extra: settings.extraDepends[format.
|
|
220
|
+
extra: settings.extraDepends[format.depends],
|
|
197
221
|
typelibPackages: settings.typelibPackages,
|
|
198
222
|
bundledTypelibs: facts.bundledTypelibs,
|
|
199
223
|
minGjsVersion: settings.minGjsVersion,
|
|
200
224
|
});
|
|
201
|
-
for (const warning of warnAboutGjsFloor(format.
|
|
225
|
+
for (const warning of warnAboutGjsFloor(format.depends, settings.minGjsVersion)) {
|
|
202
226
|
console.warn(`${LOG} ${warning}`);
|
|
227
|
+
}
|
|
203
228
|
}
|
|
204
229
|
// Written unconditionally, not only under `--stage`: a `ship/stage/` that
|
|
205
230
|
// is sometimes packable elsewhere and sometimes not is a worse contract
|
|
@@ -222,6 +247,7 @@ async function assemble(args) {
|
|
|
222
247
|
outRoot,
|
|
223
248
|
namespaces,
|
|
224
249
|
mtime,
|
|
250
|
+
verbose: args.verbose,
|
|
225
251
|
}));
|
|
226
252
|
}
|
|
227
253
|
printArtifacts(projectDir, artifacts);
|
|
@@ -256,6 +282,8 @@ async function finishFromStage(args, fromStage) {
|
|
|
256
282
|
assertExpectedTarget(manifest, args['expect-target']);
|
|
257
283
|
const formats = resolveFormats(args.target ?? manifest.formats);
|
|
258
284
|
assertFormatsStaged(manifest, formats);
|
|
285
|
+
for (const format of formats)
|
|
286
|
+
assertCanPack(format);
|
|
259
287
|
for (const warning of warnOnToolSkew(manifest))
|
|
260
288
|
console.warn(`${LOG} ${warning}`);
|
|
261
289
|
const { mtime, warnings } = resolveStageMtime(manifest);
|
|
@@ -275,8 +303,10 @@ async function finishFromStage(args, fromStage) {
|
|
|
275
303
|
const outRoot = args.out === undefined ? dirname(stageDir) : resolve(cwd, args.out);
|
|
276
304
|
const artifacts = [];
|
|
277
305
|
for (const format of formats) {
|
|
278
|
-
|
|
279
|
-
|
|
306
|
+
if (format.depends !== null) {
|
|
307
|
+
for (const warning of warnAboutGjsFloor(format.depends, manifest.settings.minGjsVersion)) {
|
|
308
|
+
console.warn(`${LOG} ${warning}`);
|
|
309
|
+
}
|
|
280
310
|
}
|
|
281
311
|
artifacts.push(await packOne({
|
|
282
312
|
format,
|
|
@@ -288,6 +318,7 @@ async function finishFromStage(args, fromStage) {
|
|
|
288
318
|
outRoot,
|
|
289
319
|
namespaces: manifest.namespaces,
|
|
290
320
|
mtime,
|
|
321
|
+
verbose: args.verbose,
|
|
291
322
|
}));
|
|
292
323
|
}
|
|
293
324
|
printArtifacts(cwd, artifacts);
|
|
@@ -302,15 +333,17 @@ async function packOne(input) {
|
|
|
302
333
|
// this answers used to be answered from the project's file lists, which are
|
|
303
334
|
// absolute paths on the build host and therefore unavailable here.
|
|
304
335
|
const facts = readPayloadFacts(payload);
|
|
305
|
-
const depends =
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
336
|
+
const depends = format.depends === null
|
|
337
|
+
? []
|
|
338
|
+
: deriveDepends(format.depends, {
|
|
339
|
+
namespaces: input.namespaces,
|
|
340
|
+
hasIcons: facts.hasIcons,
|
|
341
|
+
hasSchemas: facts.hasSchemas,
|
|
342
|
+
extra: settings.extraDepends[format.depends],
|
|
343
|
+
typelibPackages: settings.typelibPackages,
|
|
344
|
+
bundledTypelibs: facts.bundledTypelibs,
|
|
345
|
+
minGjsVersion: settings.minGjsVersion,
|
|
346
|
+
});
|
|
314
347
|
// `--arch` is a CLAIM about the payload and nothing else compared it to one:
|
|
315
348
|
// an x86-64 `.so` staged with `--arch arm64` packed at exit 0 and `rpm -qp
|
|
316
349
|
// --qf %{ARCH}` confirmed `aarch64`, because rpm reads the header and the
|
|
@@ -327,36 +360,65 @@ async function packOne(input) {
|
|
|
327
360
|
assertPayloadMatchesArch(payload, input.arch);
|
|
328
361
|
const archLabel = format.archName(input.arch, isArchIndependent(payload));
|
|
329
362
|
const common = { settings, payload, prefix: format.prefix, depends, archLabel, mtime };
|
|
363
|
+
const outDir = join(outRoot, 'out');
|
|
364
|
+
mkdirSync(outDir, { recursive: true });
|
|
365
|
+
const target = join(outDir, format.fileName(settings, archLabel));
|
|
330
366
|
// A SWITCH with a `never` guard, not a ternary. `format.id === 'deb' ? deb : rpm`
|
|
331
367
|
// read as a dispatch and behaved as one only while there were exactly two
|
|
332
368
|
// formats: a third would have taken the else-branch and written an RPM under a
|
|
333
369
|
// `.dmg` name, at exit 0. This is the same closed-vocabulary hazard the format
|
|
334
370
|
// list has, one level worse — a wrong ARTIFACT rather than a rejected
|
|
335
371
|
// declaration — so the compiler owns it now.
|
|
336
|
-
|
|
372
|
+
//
|
|
373
|
+
// Each branch WRITES `target` rather than returning bytes. It used to return
|
|
374
|
+
// them, which is the right shape only while every packer is a byte writer: a
|
|
375
|
+
// Flatpak is produced by `flatpak build-bundle`, so its bytes exist as a file
|
|
376
|
+
// before this process could hold them, and reading a bundle back into memory
|
|
377
|
+
// to hand it to one `writeFileSync` would be a copy for the sake of a
|
|
378
|
+
// signature. The size is stat'ed off what landed, for every format.
|
|
337
379
|
switch (format.id) {
|
|
338
380
|
case 'deb':
|
|
339
|
-
|
|
381
|
+
writeFileSync(target, await buildDeb(common));
|
|
340
382
|
break;
|
|
341
383
|
case 'rpm':
|
|
342
|
-
|
|
384
|
+
writeFileSync(target, await buildRpm(common));
|
|
385
|
+
break;
|
|
386
|
+
case 'flatpak':
|
|
387
|
+
await buildFlatpakBundle({
|
|
388
|
+
settings,
|
|
389
|
+
stageDir,
|
|
390
|
+
overlayDir,
|
|
391
|
+
stageManifestFile: STAGE_MANIFEST_FILE,
|
|
392
|
+
workDir: join(outRoot, 'flatpak'),
|
|
393
|
+
target,
|
|
394
|
+
archLabel,
|
|
395
|
+
verbose: input.verbose,
|
|
396
|
+
});
|
|
343
397
|
break;
|
|
344
398
|
default: {
|
|
345
399
|
const unhandled = format.id;
|
|
346
400
|
throw new Error(`gjsify ship: no packer is wired for format "${String(unhandled)}".`);
|
|
347
401
|
}
|
|
348
402
|
}
|
|
349
|
-
|
|
350
|
-
mkdirSync(outDir, { recursive: true });
|
|
351
|
-
const target = join(outDir, format.fileName(settings, archLabel));
|
|
352
|
-
writeFileSync(target, bytes);
|
|
353
|
-
return { format: format.id, path: target, size: bytes.byteLength };
|
|
403
|
+
return { format: format.id, path: target, size: statSync(target).size };
|
|
354
404
|
}
|
|
355
405
|
function printArtifacts(base, artifacts) {
|
|
356
406
|
for (const artifact of artifacts) {
|
|
357
407
|
console.log(`${LOG} ${artifact.format}: ${relative(base, artifact.path)} (${artifact.size} bytes)`);
|
|
358
408
|
}
|
|
359
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Refuse a format this host cannot finish, and say which half is missing.
|
|
412
|
+
*
|
|
413
|
+
* Two checks and not one, because the fixes differ: the wrong OS needs another
|
|
414
|
+
* machine, an absent tool needs a package. Both run on the pack path only —
|
|
415
|
+
* `--stage` deliberately needs neither, which is what makes a Flatpak
|
|
416
|
+
* assemblable on a host that cannot build one (ADR 0024 § A1).
|
|
417
|
+
*/
|
|
418
|
+
function assertCanPack(format) {
|
|
419
|
+
assertHostCanFinish(format);
|
|
420
|
+
assertToolsInstalled(format);
|
|
421
|
+
}
|
|
360
422
|
/**
|
|
361
423
|
* Refuse a build target this command cannot package correctly.
|
|
362
424
|
*
|
package/lib/config.js
CHANGED
|
@@ -4,6 +4,7 @@ import { basename, dirname } from 'node:path';
|
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
5
|
import { isGjs, hostRuntime } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
6
6
|
import { buildAppForRuntime } from './utils/runtimes.js';
|
|
7
|
+
import { DIALECT_APPS, isSourceDialect, SOURCE_DIALECTS } from '@gjsify/rolldown-plugin-gjsify';
|
|
7
8
|
/**
|
|
8
9
|
* Does the failed `import()` of a config look like a MODULE-RESOLUTION failure
|
|
9
10
|
* (the config imports something GJS's ESM loader can't resolve for an external
|
|
@@ -268,6 +269,18 @@ export class Config {
|
|
|
268
269
|
configData.exclude = cliArgs.exclude || [];
|
|
269
270
|
if (cliArgs.consoleShim !== undefined)
|
|
270
271
|
configData.consoleShim = cliArgs.consoleShim;
|
|
272
|
+
// `--dialect` narrows here rather than at the call site. yargs `choices`
|
|
273
|
+
// already rejects an unknown value typed on the command line, but a
|
|
274
|
+
// `gjsify.dialect` in package.json or `.gjsifyrc.*` never passes through
|
|
275
|
+
// yargs — so the one place both spellings meet is the one place that
|
|
276
|
+
// checks, and it names the alternatives instead of silently ignoring.
|
|
277
|
+
if (cliArgs.dialect !== undefined) {
|
|
278
|
+
if (!isSourceDialect(cliArgs.dialect)) {
|
|
279
|
+
throw new Error(`gjsify build: "${cliArgs.dialect}" is not a source dialect. Known: ${SOURCE_DIALECTS.join(', ')}. ` +
|
|
280
|
+
'This is the dialect the SOURCE is written in — not gjsify.runtimes, which declares the runtime a package RUNS on.');
|
|
281
|
+
}
|
|
282
|
+
configData.dialect = cliArgs.dialect;
|
|
283
|
+
}
|
|
271
284
|
// Default `--app` FOLLOWS the host runtime the CLI executes in: gjs when
|
|
272
285
|
// run under gjs, node when run under node/bun/deno (both consume the
|
|
273
286
|
// `--app node` bundle). Applied post-merge (not as a yargs `default:`)
|
|
@@ -278,6 +291,18 @@ export class Config {
|
|
|
278
291
|
if (cliArgs.app !== undefined)
|
|
279
292
|
configData.app = cliArgs.app;
|
|
280
293
|
configData.app ??= buildAppForRuntime(hostRuntime());
|
|
294
|
+
// A dialect only composes on the targets that have a plugin for it, and
|
|
295
|
+
// saying so is the whole point: `--dialect react-native --app browser`
|
|
296
|
+
// parses, resolves and then composes NOTHING, so the alias never happens,
|
|
297
|
+
// the support gate never runs, and the build succeeds while doing none of
|
|
298
|
+
// what was asked. That is the shape this repo keeps paying for — a flag
|
|
299
|
+
// accepted and ignored. Checked here rather than in the plugin because the
|
|
300
|
+
// plugin only ever sees the targets it was composed on.
|
|
301
|
+
if (configData.dialect !== undefined && !DIALECT_APPS.has(configData.app)) {
|
|
302
|
+
throw new Error(`gjsify build: --dialect ${configData.dialect} has no effect on --app ${configData.app}. ` +
|
|
303
|
+
`It composes on ${[...DIALECT_APPS].join(' and ')} only. ` +
|
|
304
|
+
'Drop the dialect, or build one of those targets.');
|
|
305
|
+
}
|
|
281
306
|
if (cliArgs.globals !== undefined)
|
|
282
307
|
configData.globals = cliArgs.globals;
|
|
283
308
|
// Fallback applied post-merge (not as a yargs `default:`) so a
|
|
@@ -25,6 +25,17 @@ export interface CliBuildOptions {
|
|
|
25
25
|
* working ANSI colors). Disable with `--no-console-shim`. Default: true.
|
|
26
26
|
*/
|
|
27
27
|
consoleShim?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* The dialect the SOURCE is written in — `--dialect react-native` (ADR 0032):
|
|
30
|
+
* alias `react-native` onto `@gjsify/react-native` and fail the build on an
|
|
31
|
+
* import whose support-table status is not `supported` or `partial`. `gjs` and
|
|
32
|
+
* `node` targets. Default: unset — a tree with a phone leg has the real
|
|
33
|
+
* `react-native` on purpose.
|
|
34
|
+
*
|
|
35
|
+
* A `string` here and a `SourceDialect` in `ConfigData`: yargs hands back
|
|
36
|
+
* whatever was typed, and the narrowing happens once, where the value is read.
|
|
37
|
+
*/
|
|
38
|
+
dialect?: string;
|
|
28
39
|
/**
|
|
29
40
|
* Comma-separated global identifiers the code needs, e.g.
|
|
30
41
|
* `"fetch,Buffer,process,URL,crypto"`. Each maps to the corresponding
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RolldownOptions, OutputOptions, RolldownPluginOption } from 'rolldown';
|
|
2
|
-
import type { App } from '@gjsify/rolldown-plugin-gjsify';
|
|
2
|
+
import type { App, SourceDialect } from '@gjsify/rolldown-plugin-gjsify';
|
|
3
3
|
import type { ConfigDataLibrary, ConfigDataTypescript } from './index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Plugin entry resolvable by package name from the project's `node_modules`, so
|
|
@@ -82,6 +82,20 @@ export interface ConfigData {
|
|
|
82
82
|
* working ANSI colors). Default: true.
|
|
83
83
|
*/
|
|
84
84
|
consoleShim?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* The dialect the SOURCE is written in (ADR 0032 § 2 + § 8). `'react-native'`
|
|
87
|
+
* aliases `react-native` onto `@gjsify/react-native` and gates every named
|
|
88
|
+
* import against the support table at BUILD time. `gjs` and `node` app builds.
|
|
89
|
+
* Default: unset.
|
|
90
|
+
*
|
|
91
|
+
* Opt-in and never inferred from the dependency list: a monorepo with a phone
|
|
92
|
+
* leg installs the real `react-native` on purpose, and redirecting the
|
|
93
|
+
* specifier for every build in that tree would change what that leg resolves.
|
|
94
|
+
*
|
|
95
|
+
* NOT `gjsify.runtimes['react-native']`, which answers the opposite question —
|
|
96
|
+
* see the header of `SourceDialect`.
|
|
97
|
+
*/
|
|
98
|
+
dialect?: SourceDialect;
|
|
85
99
|
/** Comma-separated global identifiers to register. Format: see CliBuildOptions. */
|
|
86
100
|
globals?: string;
|
|
87
101
|
/**
|
|
@@ -496,8 +510,66 @@ export interface ConfigDataShip extends AppMetadata {
|
|
|
496
510
|
extraFiles?: Record<string, string>;
|
|
497
511
|
/** Arguments the launcher appends before the user's own. */
|
|
498
512
|
execArgs?: string[];
|
|
513
|
+
/**
|
|
514
|
+
* How `--target flatpak` builds the app: the runtime it links against and
|
|
515
|
+
* what the finished app is allowed to do.
|
|
516
|
+
*
|
|
517
|
+
* The new home of the `gjsify.flatpak` BUILD keys (ADR 0024 § 8). The old
|
|
518
|
+
* spelling still resolves and warns — the window, and what deliberately did
|
|
519
|
+
* NOT move, is `utils/ship/flatpak-config.ts`.
|
|
520
|
+
*/
|
|
521
|
+
flatpak?: ShipFlatpakOptions;
|
|
499
522
|
}
|
|
500
|
-
/**
|
|
523
|
+
/**
|
|
524
|
+
* The Flatpak knobs `gjsify ship` reads. Every one has a derived default.
|
|
525
|
+
*
|
|
526
|
+
* There is no `modules` / `extraModules` here, and that is a decision rather
|
|
527
|
+
* than an omission: under `ship` the module list IS the staged payload
|
|
528
|
+
* (`buildsystem: simple` + `cp -a stage/.`), and an escape hatch injecting
|
|
529
|
+
* arbitrary build modules would put the second staging model back in the tree —
|
|
530
|
+
* the one thing ADR 0024 § 8 gates the whole migration on. A project that has
|
|
531
|
+
* to BUILD something inside the sandbox still has `gjsify flatpak init` +
|
|
532
|
+
* `gjsify flatpak build`, unchanged.
|
|
533
|
+
*/
|
|
534
|
+
export interface ShipFlatpakOptions {
|
|
535
|
+
/**
|
|
536
|
+
* Runtime family. Default `'gnome'` — a GJS bundle needs GLib/GObject/GIO at
|
|
537
|
+
* runtime, and no GJS interpreter ships in the Freedesktop runtime.
|
|
538
|
+
*/
|
|
539
|
+
runtime?: 'gnome' | 'freedesktop';
|
|
540
|
+
/** Runtime/SDK version, e.g. `'50'` for GNOME or `'24.08'` for Freedesktop. */
|
|
541
|
+
runtimeVersion?: string;
|
|
542
|
+
/**
|
|
543
|
+
* The branch the app is exported under — the last segment of
|
|
544
|
+
* `app/<id>/<arch>/<branch>`. Default `'stable'`, which is what Flathub
|
|
545
|
+
* publishes and what `flatpak install` resolves without being told.
|
|
546
|
+
*/
|
|
547
|
+
branch?: string;
|
|
548
|
+
/** Extra SDK extensions, e.g. `['org.freedesktop.Sdk.Extension.llvm17']`. */
|
|
549
|
+
sdkExtensions?: string[];
|
|
550
|
+
/**
|
|
551
|
+
* Path components prepended to `PATH` inside the build sandbox. Derived from
|
|
552
|
+
* `sdkExtensions` when unset, because an extension whose `bin` is not on
|
|
553
|
+
* PATH is one the build cannot use.
|
|
554
|
+
*/
|
|
555
|
+
appendPath?: string[];
|
|
556
|
+
/** Finish-args (capabilities). Default depends on `kind`: GUI args for an app, none for a CLI. */
|
|
557
|
+
finishArgs?: string[];
|
|
558
|
+
/** Cleanup globs applied to `/app`, e.g. `['/include', '/lib/pkgconfig']`. */
|
|
559
|
+
cleanup?: string[];
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Flatpak-toolchain config for the `gjsify flatpak` subcommand group.
|
|
563
|
+
*
|
|
564
|
+
* Six of these keys — `runtime`, `runtimeVersion`, `sdkExtensions`,
|
|
565
|
+
* `appendPath`, `finishArgs`, `cleanup` — have a second home at
|
|
566
|
+
* {@link ShipFlatpakOptions}, and `gjsify ship` warns when it reads them from
|
|
567
|
+
* here (ADR 0024 § 8; the window is `utils/ship/flatpak-config.ts`). They are
|
|
568
|
+
* NOT marked `@deprecated`: for the subcommands in this group they are still
|
|
569
|
+
* the only spelling, and those commands have not moved under `ship` yet
|
|
570
|
+
* (`status/open-todos.md`). Nothing else in this block is deprecated at all —
|
|
571
|
+
* the {@link AppMetadata} half is a designed alias, not a legacy one.
|
|
572
|
+
*/
|
|
501
573
|
export interface ConfigDataFlatpak extends AppMetadata {
|
|
502
574
|
/** Reverse-DNS app id, e.g. `eu.jumplink.Learn6502`. Defaults to `package.json#name` if it looks like a reverse-DNS id. */
|
|
503
575
|
appId?: string;
|