@gjsify/cli 0.64.4 → 0.64.5
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/lib/commands/ship.js
CHANGED
|
@@ -26,12 +26,16 @@ import { CHANNELS, CHANNEL_ENV, parseDeclaredChannel } from '../utils/channel.js
|
|
|
26
26
|
import { mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
27
27
|
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
28
28
|
import { Config } from '../config.js';
|
|
29
|
+
import { ensurePkgDir } from './dlx.js';
|
|
29
30
|
import { renderDesktopEntry, renderMetainfoApp, renderMetainfoCli, validateAppMetadata, } from '../utils/app-metadata.js';
|
|
30
31
|
import { readPackageJson } from '../utils/pkg-json.js';
|
|
32
|
+
import { parseSpec } from '../utils/parse-spec.js';
|
|
33
|
+
import { cliVersion } from '../utils/publish-headers.js';
|
|
31
34
|
import { describeExit, spawnToCompletion } from '../utils/spawn.js';
|
|
32
35
|
import { buildDeb } from '../utils/ship/deb.js';
|
|
33
36
|
import { deriveDepends, warnAboutGjsFloor, warnAboutNodeFloor } from '../utils/ship/depends.js';
|
|
34
37
|
import { isGtkRuntimeTarget, stageAppRuntime } from '../utils/ship/app-runtime.js';
|
|
38
|
+
import { acquireGtkRuntime } from '../utils/ship/gtk-runtime-acquire.js';
|
|
35
39
|
import { discoverPayload } from '../utils/ship/discover.js';
|
|
36
40
|
import { appDirFor, appDirPayload, appImageHostRequirements, appImageRuntimeNotice, buildAppImage, findPinnedRuntime, stampAppDirTimes, } from '../utils/ship/appimage.js';
|
|
37
41
|
import { buildDmgImage, dmgVolumeDir, dmgVolumeName } from '../utils/ship/dmg.js';
|
|
@@ -411,7 +415,7 @@ async function assemble(args) {
|
|
|
411
415
|
// `.deb`/`.rpm` `Depends:` is the promise that `node` or `gjs` is on `PATH`, and
|
|
412
416
|
// carrying a second copy inside the payload would be a private interpreter no
|
|
413
417
|
// distribution update ever patches.
|
|
414
|
-
const runtime = resolveCarriedRuntime(layout, settings, projectDir);
|
|
418
|
+
const runtime = await resolveCarriedRuntime(layout, settings, projectDir);
|
|
415
419
|
const stageInputs = {
|
|
416
420
|
bundleFiles: discovered.bundleFiles,
|
|
417
421
|
launcher: renderLauncher(settings, basename(settings.bundlePath), layout, runtime.launcher),
|
|
@@ -761,21 +765,35 @@ function announceSigning(sign, notarize) {
|
|
|
761
765
|
* thing to make non-vacuous on this layout, since a JavaScript bundle has no
|
|
762
766
|
* cputype to disagree with.
|
|
763
767
|
*/
|
|
764
|
-
function resolveCarriedRuntime(layout, settings, projectDir) {
|
|
768
|
+
async function resolveCarriedRuntime(layout, settings, projectDir) {
|
|
765
769
|
const empty = { files: [], launcher: {}, found: [], missing: [] };
|
|
766
770
|
if (layout.os === 'linux' || settings.app !== 'node')
|
|
767
771
|
return empty;
|
|
768
772
|
const target = `${layout.os}-${settings.arch}`;
|
|
769
773
|
if (!isGtkRuntimeTarget(target))
|
|
770
774
|
return empty;
|
|
775
|
+
// The closure is fetched, not installed: a project that lists it puts a second
|
|
776
|
+
// GLib next to Homebrew's in its dev tree (see `gtk-runtime-acquire.ts`).
|
|
777
|
+
const acquired = await acquireGtkRuntime({
|
|
778
|
+
target,
|
|
779
|
+
version: cliVersion(),
|
|
780
|
+
discovery: { cwd: projectDir },
|
|
781
|
+
ensure: async (spec) => (await ensurePkgDir(parseSpec(spec), { verbose: false, cacheMaxAge: DLX_CACHE_MAX_AGE_MIN, frozen: false }))
|
|
782
|
+
.pkgDir,
|
|
783
|
+
log: (message) => console.log(`${LOG} ${message}`),
|
|
784
|
+
});
|
|
771
785
|
return stageAppRuntime({
|
|
772
786
|
layout,
|
|
773
787
|
identity: settings,
|
|
774
788
|
target,
|
|
775
789
|
cwd: projectDir,
|
|
776
790
|
interpreter: isNodeRuntimeTarget(target) ? resolveNodeRuntime(target, { cwd: projectDir }) : null,
|
|
791
|
+
...(acquired.kind === 'fetched' ? { fetchedGtkRuntimeDir: acquired.dir } : {}),
|
|
792
|
+
...(acquired.kind === 'failed' ? { gtkFetchFailure: acquired.reason } : {}),
|
|
777
793
|
});
|
|
778
794
|
}
|
|
795
|
+
/** How long a fetched runtime stays in the dlx cache — the same week `gjsify showcase` uses. */
|
|
796
|
+
const DLX_CACHE_MAX_AGE_MIN = 60 * 24 * 7;
|
|
779
797
|
/**
|
|
780
798
|
* Phase two: pack a stage that was assembled somewhere else.
|
|
781
799
|
*
|
|
@@ -49,6 +49,13 @@ export interface ResolveRuntimeOptions {
|
|
|
49
49
|
* is Bun's global-cache behaviour, which is otherwise unreachable from Node.
|
|
50
50
|
*/
|
|
51
51
|
resolve?: ResolveInstalledPackageOptions['resolve'];
|
|
52
|
+
/**
|
|
53
|
+
* The directory of a `@gjsify/gtk-runtime-<target>` package that
|
|
54
|
+
* {@link acquireGtkRuntime} fetched for THIS run — the last candidate, after
|
|
55
|
+
* everything the author installed or pointed at. See `gtk-runtime-acquire.ts`
|
|
56
|
+
* for why it is fetched rather than installed.
|
|
57
|
+
*/
|
|
58
|
+
fetchedGtkRuntimeDir?: string;
|
|
52
59
|
}
|
|
53
60
|
/**
|
|
54
61
|
* Locate the relocated GTK closure for `target`, or `null`.
|
|
@@ -68,7 +75,11 @@ export interface ResolveRuntimeOptions {
|
|
|
68
75
|
* for a ship run.
|
|
69
76
|
* 3. the sibling monorepo directory `../gtk-runtime-<target>/gtk`, which is what
|
|
70
77
|
* an in-repo `npm run build:bundle` writes.
|
|
71
|
-
* 4. the published `@gjsify/gtk-runtime-<target>` package's `gtk
|
|
78
|
+
* 4. the published `@gjsify/gtk-runtime-<target>` package's `gtk/`, when the
|
|
79
|
+
* author installed it.
|
|
80
|
+
* 5. the same package fetched for this run (`fetchedGtkRuntimeDir`) — NOT in
|
|
81
|
+
* node-gi's probe, because node-gi never fetches: it finds the closure inside
|
|
82
|
+
* the `.app` this function put it in, and the layout there is the same.
|
|
72
83
|
*
|
|
73
84
|
* A candidate counts only when it holds BOTH the loadable-code directory and the
|
|
74
85
|
* typelib directory — node-gi's own probe, restated here rather than weakened,
|
|
@@ -195,6 +206,10 @@ export interface StageAppRuntimeInput {
|
|
|
195
206
|
nodePath: string;
|
|
196
207
|
licensePath: string;
|
|
197
208
|
} | null;
|
|
209
|
+
/** See {@link ResolveRuntimeOptions.fetchedGtkRuntimeDir}. */
|
|
210
|
+
fetchedGtkRuntimeDir?: string;
|
|
211
|
+
/** Why the fetch produced nothing — named in the `missing` line, so the cause is not guessed at. */
|
|
212
|
+
gtkFetchFailure?: string;
|
|
198
213
|
}
|
|
199
214
|
/**
|
|
200
215
|
* Stage the interpreter, the GTK closure and the addon into one bundle.
|
|
@@ -123,7 +123,11 @@ export const NODE_GI_PACKAGE = '@gjsify/node-gi';
|
|
|
123
123
|
* for a ship run.
|
|
124
124
|
* 3. the sibling monorepo directory `../gtk-runtime-<target>/gtk`, which is what
|
|
125
125
|
* an in-repo `npm run build:bundle` writes.
|
|
126
|
-
* 4. the published `@gjsify/gtk-runtime-<target>` package's `gtk
|
|
126
|
+
* 4. the published `@gjsify/gtk-runtime-<target>` package's `gtk/`, when the
|
|
127
|
+
* author installed it.
|
|
128
|
+
* 5. the same package fetched for this run (`fetchedGtkRuntimeDir`) — NOT in
|
|
129
|
+
* node-gi's probe, because node-gi never fetches: it finds the closure inside
|
|
130
|
+
* the `.app` this function put it in, and the layout there is the same.
|
|
127
131
|
*
|
|
128
132
|
* A candidate counts only when it holds BOTH the loadable-code directory and the
|
|
129
133
|
* typelib directory — node-gi's own probe, restated here rather than weakened,
|
|
@@ -155,6 +159,12 @@ export function resolveGtkRuntime(target, options = {}) {
|
|
|
155
159
|
const entry = resolveStagedPackage(packageName, options);
|
|
156
160
|
if (entry !== null)
|
|
157
161
|
candidates.push({ source: packageName, dir: join(dirname(entry), 'gtk') });
|
|
162
|
+
if (options.fetchedGtkRuntimeDir !== undefined) {
|
|
163
|
+
candidates.push({
|
|
164
|
+
source: `${packageName} (fetched for this ship run)`,
|
|
165
|
+
dir: join(options.fetchedGtkRuntimeDir, 'gtk'),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
158
168
|
for (const candidate of candidates) {
|
|
159
169
|
const libDir = join(candidate.dir, nativeSubdir);
|
|
160
170
|
const typelibDir = join(candidate.dir, 'girepository-1.0');
|
|
@@ -340,8 +350,10 @@ export function stageAppRuntime(input) {
|
|
|
340
350
|
// resolver then refuses -- a hint that is wrong is worse than none,
|
|
341
351
|
// because it costs the reader the time to follow it first.
|
|
342
352
|
const nativeSubdir = input.target.startsWith('win32-') ? 'bin' : 'lib';
|
|
343
|
-
|
|
344
|
-
|
|
353
|
+
const why = input.gtkFetchFailure === undefined ? '' : ` — fetching it failed: ${input.gtkFetchFailure}`;
|
|
354
|
+
missing.push(`no GTK closure${why} — point \`GJSIFY_GTK_RUNTIME\` at a bundle directory holding ` +
|
|
355
|
+
`\`${nativeSubdir}/\` and \`girepository-1.0/\`, or install \`${gtkRuntimePackageName(input.target)}\` ` +
|
|
356
|
+
'where the build can reach it');
|
|
345
357
|
}
|
|
346
358
|
// node-gi's JavaScript, which the bundle `require`s by name. Staged whenever
|
|
347
359
|
// the package is resolvable and reported separately from the addon: a bundle
|
|
@@ -394,6 +406,8 @@ function discoveryOptions(input) {
|
|
|
394
406
|
options.env = input.env;
|
|
395
407
|
if (input.resolve !== undefined)
|
|
396
408
|
options.resolve = input.resolve;
|
|
409
|
+
if (input.fetchedGtkRuntimeDir !== undefined)
|
|
410
|
+
options.fetchedGtkRuntimeDir = input.fetchedGtkRuntimeDir;
|
|
397
411
|
return options;
|
|
398
412
|
}
|
|
399
413
|
/**
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type GtkRuntimeTarget, type ResolveRuntimeOptions } from './app-runtime.js';
|
|
2
|
+
export type AcquiredGtkRuntime =
|
|
3
|
+
/** Something already resolves — nothing was fetched, and nothing will be. */
|
|
4
|
+
{
|
|
5
|
+
kind: 'present';
|
|
6
|
+
}
|
|
7
|
+
/** Fetched; `dir` is the PACKAGE directory, holding `gtk/`. */
|
|
8
|
+
| {
|
|
9
|
+
kind: 'fetched';
|
|
10
|
+
dir: string;
|
|
11
|
+
}
|
|
12
|
+
/** Could not be had; `reason` is what to tell the person, verbatim. */
|
|
13
|
+
| {
|
|
14
|
+
kind: 'failed';
|
|
15
|
+
reason: string;
|
|
16
|
+
};
|
|
17
|
+
export interface AcquireGtkRuntimeInput {
|
|
18
|
+
target: GtkRuntimeTarget;
|
|
19
|
+
/** The version to fetch: the CLI's own, because addon and closure are a matched pair. */
|
|
20
|
+
version: string;
|
|
21
|
+
/** The same discovery options the staging step will use — so "present" means what it will mean there. */
|
|
22
|
+
discovery?: ResolveRuntimeOptions;
|
|
23
|
+
/**
|
|
24
|
+
* Fetch a package spec into a cache and return its directory. Injected: the
|
|
25
|
+
* real one is `ensurePkgDir`, and a test must not touch a registry.
|
|
26
|
+
*/
|
|
27
|
+
ensure: (spec: string) => Promise<string>;
|
|
28
|
+
/** One progress line. The closure is ~100 MB, so a silent first run looks like a hang. */
|
|
29
|
+
log?: (message: string) => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Make sure a GTK closure is reachable for `target`, fetching it only if nothing
|
|
33
|
+
* else provides one. Never throws: a ship run with no closure is a PARTIAL result
|
|
34
|
+
* that `stageAppRuntime` reports, not an error (see its doc).
|
|
35
|
+
*/
|
|
36
|
+
export declare function acquireGtkRuntime(input: AcquireGtkRuntimeInput): Promise<AcquiredGtkRuntime>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Getting the GTK closure for a ship run when the project does not carry it.
|
|
2
|
+
//
|
|
3
|
+
// WHY IT IS FETCHED AND NOT INSTALLED. `@gjsify/gtk-runtime-<target>` is 100 MB of
|
|
4
|
+
// GLib, GObject, GTK and friends, and a project that lists it — as
|
|
5
|
+
// `optionalDependencies`, which the `os`/`cpu` gate turns into "installed on every
|
|
6
|
+
// Mac" — gets it into its own `node_modules`. On macOS that breaks every build and
|
|
7
|
+
// every run that goes through a Homebrew GJS. Every darwin prebuild
|
|
8
|
+
// carries the rpath `@loader_path` → `…/gtk-runtime-darwin-<arch>/gtk/lib` →
|
|
9
|
+
// `<brew>/lib` (ADR 0023: bundle before system, `docs/prebuilds.md`), so the first
|
|
10
|
+
// `dlopen` out of `node_modules/@gjsify/*-native-darwin-*` finds the bundle's
|
|
11
|
+
// GLib/GObject beside the one `gjs` already loaded from Homebrew: two GObject type
|
|
12
|
+
// registries in one process, which shows as `No signal 'context_response' on
|
|
13
|
+
// object 'GVolumeMonitor'` and a segfault, or as `GNotificationCenterDelegate is
|
|
14
|
+
// implemented in both …`. The invariant ADR 0023 names — one registry per process
|
|
15
|
+
// — is violated by the dev tree itself.
|
|
16
|
+
//
|
|
17
|
+
// The closure is only ever needed at the moment it is COPIED into a bundle, so it
|
|
18
|
+
// is acquired then: into the dlx cache (`ensurePkgDir`, the same machinery
|
|
19
|
+
// `gjsify showcase` already uses for the same package), never into the project.
|
|
20
|
+
// What this does NOT change: the search order in `resolveGtkRuntime()` keeps its
|
|
21
|
+
// four existing candidates, so a project that still installs the package ships
|
|
22
|
+
// exactly as before — it merely stays exposed to the dev-tree failure above until
|
|
23
|
+
// it drops the dependency.
|
|
24
|
+
//
|
|
25
|
+
// EXACT VERSION, no range and no fallback. The addon's `@rpath` is
|
|
26
|
+
// `@loader_path/gtk/lib` — addon and closure are a matched pair published from the
|
|
27
|
+
// same release — so "the nearest published version" would be a guess about GTK ABI
|
|
28
|
+
// that `assertPayloadMatchesArch` cannot check. A CLI version with no published
|
|
29
|
+
// runtime (a dev or `next` build) fails with the reason named, and
|
|
30
|
+
// `GJSIFY_GTK_RUNTIME` is the way past it.
|
|
31
|
+
import { gtkRuntimePackageName, resolveGtkRuntime, } from './app-runtime.js';
|
|
32
|
+
/**
|
|
33
|
+
* Make sure a GTK closure is reachable for `target`, fetching it only if nothing
|
|
34
|
+
* else provides one. Never throws: a ship run with no closure is a PARTIAL result
|
|
35
|
+
* that `stageAppRuntime` reports, not an error (see its doc).
|
|
36
|
+
*/
|
|
37
|
+
export async function acquireGtkRuntime(input) {
|
|
38
|
+
const discovery = input.discovery ?? {};
|
|
39
|
+
if (resolveGtkRuntime(input.target, discovery) !== null)
|
|
40
|
+
return { kind: 'present' };
|
|
41
|
+
const name = gtkRuntimePackageName(input.target);
|
|
42
|
+
const spec = `${name}@${input.version}`;
|
|
43
|
+
input.log?.(`${name} is not installed here, and is not meant to be — fetching ${spec} into the cache ` +
|
|
44
|
+
'(about 100 MB, once)');
|
|
45
|
+
let dir;
|
|
46
|
+
try {
|
|
47
|
+
dir = await input.ensure(spec);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return { kind: 'failed', reason: error instanceof Error ? error.message : String(error) };
|
|
51
|
+
}
|
|
52
|
+
// The same completeness probe the staging step applies, so a package that
|
|
53
|
+
// resolved but holds no `gtk/` (an unpopulated checkout, a truncated cache
|
|
54
|
+
// entry) is reported HERE, with its name, instead of as a bare "no GTK closure".
|
|
55
|
+
if (resolveGtkRuntime(input.target, { ...discovery, fetchedGtkRuntimeDir: dir }) === null) {
|
|
56
|
+
return { kind: 'failed', reason: `${spec} was fetched to ${dir} but holds no complete gtk/ closure` };
|
|
57
|
+
}
|
|
58
|
+
return { kind: 'fetched', dir };
|
|
59
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.64.
|
|
3
|
+
"version": "0.64.5",
|
|
4
4
|
"description": "CLI for Gjsify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -142,22 +142,22 @@
|
|
|
142
142
|
"cli"
|
|
143
143
|
],
|
|
144
144
|
"dependencies": {
|
|
145
|
-
"@gjsify/blueprint": "^0.64.
|
|
146
|
-
"@gjsify/buffer": "^0.64.
|
|
147
|
-
"@gjsify/create-app": "^0.64.
|
|
148
|
-
"@gjsify/node-globals": "^0.64.
|
|
149
|
-
"@gjsify/node-polyfills": "^0.64.
|
|
150
|
-
"@gjsify/npm-registry": "^0.64.
|
|
151
|
-
"@gjsify/resolve-npm": "^0.64.
|
|
152
|
-
"@gjsify/rolldown-plugin-gjsify": "^0.64.
|
|
153
|
-
"@gjsify/vite-plugin-blueprint": "^0.64.
|
|
154
|
-
"@gjsify/rolldown-plugin-pnp": "^0.64.
|
|
155
|
-
"@gjsify/semver": "^0.64.
|
|
156
|
-
"@gjsify/tar": "^0.64.
|
|
157
|
-
"@gjsify/tsc": "^0.64.
|
|
158
|
-
"@gjsify/utils": "^0.64.
|
|
159
|
-
"@gjsify/web-polyfills": "^0.64.
|
|
160
|
-
"@gjsify/workspace": "^0.64.
|
|
145
|
+
"@gjsify/blueprint": "^0.64.5",
|
|
146
|
+
"@gjsify/buffer": "^0.64.5",
|
|
147
|
+
"@gjsify/create-app": "^0.64.5",
|
|
148
|
+
"@gjsify/node-globals": "^0.64.5",
|
|
149
|
+
"@gjsify/node-polyfills": "^0.64.5",
|
|
150
|
+
"@gjsify/npm-registry": "^0.64.5",
|
|
151
|
+
"@gjsify/resolve-npm": "^0.64.5",
|
|
152
|
+
"@gjsify/rolldown-plugin-gjsify": "^0.64.5",
|
|
153
|
+
"@gjsify/vite-plugin-blueprint": "^0.64.5",
|
|
154
|
+
"@gjsify/rolldown-plugin-pnp": "^0.64.5",
|
|
155
|
+
"@gjsify/semver": "^0.64.5",
|
|
156
|
+
"@gjsify/tar": "^0.64.5",
|
|
157
|
+
"@gjsify/tsc": "^0.64.5",
|
|
158
|
+
"@gjsify/utils": "^0.64.5",
|
|
159
|
+
"@gjsify/web-polyfills": "^0.64.5",
|
|
160
|
+
"@gjsify/workspace": "^0.64.5",
|
|
161
161
|
"acorn": "^8.17.0",
|
|
162
162
|
"cosmiconfig": "^9.0.2",
|
|
163
163
|
"get-tsconfig": "^4.14.0",
|
|
@@ -166,13 +166,13 @@
|
|
|
166
166
|
"yargs": "^18.0.0"
|
|
167
167
|
},
|
|
168
168
|
"devDependencies": {
|
|
169
|
-
"@gjsify/unit": "^0.64.
|
|
169
|
+
"@gjsify/unit": "^0.64.5",
|
|
170
170
|
"@types/yargs": "^17.0.35",
|
|
171
171
|
"typescript": "^6.0.3"
|
|
172
172
|
},
|
|
173
173
|
"peerDependencies": {
|
|
174
|
-
"@gjsify/oxfmt-native": "^0.64.
|
|
175
|
-
"@gjsify/rolldown-native": "^0.64.
|
|
174
|
+
"@gjsify/oxfmt-native": "^0.64.5",
|
|
175
|
+
"@gjsify/rolldown-native": "^0.64.5"
|
|
176
176
|
},
|
|
177
177
|
"peerDependenciesMeta": {
|
|
178
178
|
"@gjsify/oxfmt-native": {
|