@gjsify/cli 0.4.12 → 0.4.14
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 +29 -23
- package/lib/actions/build.d.ts +11 -1
- package/lib/actions/build.js +79 -3
- package/lib/bundler-pick.d.ts +7 -0
- package/lib/bundler-pick.js +17 -0
- package/lib/commands/build.js +8 -0
- package/lib/commands/fix.d.ts +9 -0
- package/lib/commands/fix.js +60 -0
- package/lib/commands/flatpak/diff.d.ts +12 -0
- package/lib/commands/flatpak/diff.js +165 -0
- package/lib/commands/flatpak/index.d.ts +4 -1
- package/lib/commands/flatpak/index.js +11 -5
- package/lib/commands/flatpak/init.d.ts +1 -0
- package/lib/commands/flatpak/init.js +41 -7
- package/lib/commands/flatpak/release.d.ts +13 -0
- package/lib/commands/flatpak/release.js +152 -0
- package/lib/commands/flatpak/sync-flathub.d.ts +26 -0
- package/lib/commands/flatpak/sync-flathub.js +311 -0
- package/lib/commands/format.d.ts +12 -0
- package/lib/commands/format.js +98 -0
- package/lib/commands/index.d.ts +5 -0
- package/lib/commands/index.js +5 -0
- package/lib/commands/lint.d.ts +9 -0
- package/lib/commands/lint.js +60 -0
- package/lib/commands/test.d.ts +12 -0
- package/lib/commands/test.js +206 -0
- package/lib/commands/upgrade.d.ts +13 -0
- package/lib/commands/upgrade.js +402 -0
- package/lib/index.js +6 -1
- package/lib/templates/biome.json.tmpl +79 -0
- package/lib/types/cli-build-options.d.ts +7 -0
- package/lib/types/config-data.d.ts +47 -0
- package/lib/utils/biome-resolve.d.ts +47 -0
- package/lib/utils/biome-resolve.js +204 -0
- package/package.json +16 -16
package/lib/actions/build.d.ts
CHANGED
|
@@ -35,9 +35,19 @@ export declare class BuildAction {
|
|
|
35
35
|
*/
|
|
36
36
|
private applyShebang;
|
|
37
37
|
/** Application mode */
|
|
38
|
-
buildApp(app?: App
|
|
38
|
+
buildApp(app?: App, opts?: {
|
|
39
|
+
watch?: boolean;
|
|
40
|
+
}): Promise<RolldownOutput[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Drive `rolldown.watch(...)`: rebuild on source change, apply the
|
|
43
|
+
* post-bundle shebang hook on each successful build, surface errors
|
|
44
|
+
* without exiting, clean up on SIGINT/SIGTERM. Resolves only when the
|
|
45
|
+
* watcher closes — keeps the CLI process alive across rebuilds.
|
|
46
|
+
*/
|
|
47
|
+
private runWatchLoop;
|
|
39
48
|
start(buildType?: {
|
|
40
49
|
library?: boolean;
|
|
41
50
|
app?: App;
|
|
51
|
+
watch?: boolean;
|
|
42
52
|
}): Promise<RolldownOutput[]>;
|
|
43
53
|
}
|
package/lib/actions/build.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { runBundle, bundleToChunks } from "../bundler-pick.js";
|
|
1
|
+
import { runBundle, runWatch, bundleToChunks } from "../bundler-pick.js";
|
|
2
2
|
import { gjsifyPlugin, textLoaderPlugin, resolveShebangLine } from "@gjsify/rolldown-plugin-gjsify";
|
|
3
3
|
import { resolveUserPlugins } from "../utils/resolve-plugin-by-name.js";
|
|
4
4
|
import { resolveGlobalsList, writeRegisterInjectFile, detectAutoGlobals, } from "@gjsify/rolldown-plugin-gjsify/globals";
|
|
@@ -178,7 +178,7 @@ export class BuildAction {
|
|
|
178
178
|
console.debug(`[gjsify] --shebang: wrote ${line} + chmod 0o755 to ${outfile}`);
|
|
179
179
|
}
|
|
180
180
|
/** Application mode */
|
|
181
|
-
async buildApp(app = "gjs") {
|
|
181
|
+
async buildApp(app = "gjs", opts = {}) {
|
|
182
182
|
const { verbose, typescript, exclude, library: pkg, aliases, excludeGlobals, } = this.configData;
|
|
183
183
|
const userBundler = normalizeBundlerOptions(this.configData);
|
|
184
184
|
const formatRaw = userBundler.output?.format ??
|
|
@@ -281,17 +281,93 @@ export class BuildAction {
|
|
|
281
281
|
// Rolldown doesn't understand) would crash the build.
|
|
282
282
|
plugins: [...pnpPlugins, ...userPlugins, ...cfg.plugins],
|
|
283
283
|
};
|
|
284
|
+
if (opts.watch) {
|
|
285
|
+
await this.runWatchLoop(finalOpts, app, outfile, verbose);
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
284
288
|
const writeResult = await runBundle(finalOpts);
|
|
285
289
|
if (app === "gjs" && this.configData.shebang) {
|
|
286
290
|
await this.applyShebang(outfile, verbose);
|
|
287
291
|
}
|
|
288
292
|
return [writeResult];
|
|
289
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Drive `rolldown.watch(...)`: rebuild on source change, apply the
|
|
296
|
+
* post-bundle shebang hook on each successful build, surface errors
|
|
297
|
+
* without exiting, clean up on SIGINT/SIGTERM. Resolves only when the
|
|
298
|
+
* watcher closes — keeps the CLI process alive across rebuilds.
|
|
299
|
+
*/
|
|
300
|
+
async runWatchLoop(finalOpts, app, outfile, verbose) {
|
|
301
|
+
const watcher = await runWatch(finalOpts);
|
|
302
|
+
const closed = new Promise((resolve) => {
|
|
303
|
+
watcher.on("close", () => resolve());
|
|
304
|
+
});
|
|
305
|
+
let closing = false;
|
|
306
|
+
const shutdown = async () => {
|
|
307
|
+
if (closing)
|
|
308
|
+
return;
|
|
309
|
+
closing = true;
|
|
310
|
+
console.log("\n[gjsify build --watch] stopping watcher…");
|
|
311
|
+
try {
|
|
312
|
+
await watcher.close();
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
console.error("[gjsify build --watch] watcher close error:", err);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
process.on("SIGINT", shutdown);
|
|
319
|
+
process.on("SIGTERM", shutdown);
|
|
320
|
+
watcher.on("event", async (event) => {
|
|
321
|
+
switch (event.code) {
|
|
322
|
+
case "START":
|
|
323
|
+
if (verbose)
|
|
324
|
+
console.log("[gjsify build --watch] rebuild start");
|
|
325
|
+
break;
|
|
326
|
+
case "BUNDLE_START":
|
|
327
|
+
console.log("[gjsify build --watch] building…");
|
|
328
|
+
break;
|
|
329
|
+
case "BUNDLE_END":
|
|
330
|
+
console.log(`[gjsify build --watch] built in ${event.duration}ms`);
|
|
331
|
+
try {
|
|
332
|
+
if (app === "gjs" && this.configData.shebang) {
|
|
333
|
+
await this.applyShebang(outfile, verbose);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
finally {
|
|
337
|
+
await event.result.close();
|
|
338
|
+
}
|
|
339
|
+
break;
|
|
340
|
+
case "END":
|
|
341
|
+
console.log("[gjsify build --watch] waiting for changes…");
|
|
342
|
+
break;
|
|
343
|
+
case "ERROR":
|
|
344
|
+
console.error("[gjsify build --watch] build failed:", event.error?.message ?? event.error);
|
|
345
|
+
if (verbose && event.error?.stack)
|
|
346
|
+
console.error(event.error.stack);
|
|
347
|
+
try {
|
|
348
|
+
await event.result.close();
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
// best-effort cleanup
|
|
352
|
+
}
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
if (verbose) {
|
|
357
|
+
watcher.on("change", (id, change) => {
|
|
358
|
+
console.log(`[gjsify build --watch] ${change.event}: ${id}`);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
await closed;
|
|
362
|
+
}
|
|
290
363
|
async start(buildType = { app: "gjs" }) {
|
|
291
364
|
if (buildType.library) {
|
|
365
|
+
if (buildType.watch) {
|
|
366
|
+
throw new Error("gjsify build: --watch is not supported with --library (library mode would emit watcher rebuilds for every produced format; use --app gjs|node|browser instead).");
|
|
367
|
+
}
|
|
292
368
|
return await this.buildLibrary();
|
|
293
369
|
}
|
|
294
|
-
return await this.buildApp(buildType.app);
|
|
370
|
+
return await this.buildApp(buildType.app, { watch: buildType.watch });
|
|
295
371
|
}
|
|
296
372
|
}
|
|
297
373
|
async function runOneLibraryBuild(args) {
|
package/lib/bundler-pick.d.ts
CHANGED
|
@@ -65,6 +65,13 @@ export declare function bundleToChunks(input: {
|
|
|
65
65
|
rolldownInput: import('rolldown').InputOptions;
|
|
66
66
|
format: 'esm' | 'cjs' | 'iife';
|
|
67
67
|
}): Promise<string[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Watch source files and rebuild on change. Only npm rolldown supports
|
|
70
|
+
* this path — `@gjsify/rolldown-native` does not surface a watcher API yet.
|
|
71
|
+
* Returns the watcher; the caller registers `event` / `close` listeners
|
|
72
|
+
* and is responsible for invoking `watcher.close()` on shutdown.
|
|
73
|
+
*/
|
|
74
|
+
export declare function runWatch(finalOpts: BundlerOptions): Promise<import('rolldown').RolldownWatcher>;
|
|
68
75
|
/**
|
|
69
76
|
* Run a bundle with the picked engine. Drop-in replacement for the
|
|
70
77
|
* `rolldown(opts).write(opts.output)` flow used directly in build.ts.
|
package/lib/bundler-pick.js
CHANGED
|
@@ -86,6 +86,23 @@ export async function bundleToChunks(input) {
|
|
|
86
86
|
await build.close();
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Watch source files and rebuild on change. Only npm rolldown supports
|
|
91
|
+
* this path — `@gjsify/rolldown-native` does not surface a watcher API yet.
|
|
92
|
+
* Returns the watcher; the caller registers `event` / `close` listeners
|
|
93
|
+
* and is responsible for invoking `watcher.close()` on shutdown.
|
|
94
|
+
*/
|
|
95
|
+
export async function runWatch(finalOpts) {
|
|
96
|
+
if (await shouldUseNative()) {
|
|
97
|
+
throw new Error('`gjsify build --watch` requires the npm `rolldown` engine. The native engine ' +
|
|
98
|
+
'(`@gjsify/rolldown-native`) does not expose a watcher API. Run the watch loop ' +
|
|
99
|
+
'under Node (`node lib/index.js build … --watch`) or set `GJSIFY_BUNDLER=npm`.');
|
|
100
|
+
}
|
|
101
|
+
const specifier = 'rolldown';
|
|
102
|
+
const mod = (await import(/* @vite-ignore */ specifier));
|
|
103
|
+
const output = finalOpts.output ?? {};
|
|
104
|
+
return mod.watch({ ...finalOpts, output });
|
|
105
|
+
}
|
|
89
106
|
/**
|
|
90
107
|
* Run a bundle with the picked engine. Drop-in replacement for the
|
|
91
108
|
* `rolldown(opts).write(opts.output)` flow used directly in build.ts.
|
package/lib/commands/build.js
CHANGED
|
@@ -130,6 +130,13 @@ export const buildCommand = {
|
|
|
130
130
|
description: "Comma-separated global identifiers to remove from auto-detection results. Use for false positives from dead browser-compat code whose polyfills require unavailable native libraries (e.g. --exclude-globals fetch,XMLHttpRequest).",
|
|
131
131
|
type: 'string',
|
|
132
132
|
normalize: true,
|
|
133
|
+
})
|
|
134
|
+
.option('watch', {
|
|
135
|
+
alias: 'w',
|
|
136
|
+
description: "Watch source files and rebuild on change. Logs each rebuild with duration; clean SIGINT shutdown. Only valid with --app gjs|node|browser (rejected with --library). Requires the npm `rolldown` engine — run under Node, not the GJS-bundled CLI.",
|
|
137
|
+
type: 'boolean',
|
|
138
|
+
normalize: true,
|
|
139
|
+
default: false,
|
|
133
140
|
});
|
|
134
141
|
},
|
|
135
142
|
handler: async (args) => {
|
|
@@ -139,6 +146,7 @@ export const buildCommand = {
|
|
|
139
146
|
await action.start({
|
|
140
147
|
library: args.library,
|
|
141
148
|
app: args.app,
|
|
149
|
+
watch: args.watch,
|
|
142
150
|
});
|
|
143
151
|
}
|
|
144
152
|
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// `gjsify fix` — runs biome's combined `check --write` mode.
|
|
2
|
+
//
|
|
3
|
+
// Equivalent to biome's `check` (format + safe-lint-fix + organize-imports).
|
|
4
|
+
// Default writes fixes in-place; pass `--no-write` to report-only.
|
|
5
|
+
// Naming: deliberately distinct from `gjsify check` (which verifies
|
|
6
|
+
// system dependencies).
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
import { BiomeNotFoundError, findBiomeConfig, printBiomeNotFound, runBiome, } from '../utils/biome-resolve.js';
|
|
9
|
+
export const fixCommand = {
|
|
10
|
+
command: 'fix [paths..]',
|
|
11
|
+
description: 'Run Biome check --write — format + safe-lint-fix + organize-imports in one pass.',
|
|
12
|
+
builder: (yargs) => {
|
|
13
|
+
return yargs
|
|
14
|
+
.positional('paths', {
|
|
15
|
+
description: 'Files or directories to fix. Default: `.`',
|
|
16
|
+
type: 'string',
|
|
17
|
+
array: true,
|
|
18
|
+
})
|
|
19
|
+
.option('write', {
|
|
20
|
+
description: 'Apply fixes in place (default: true). Pass --no-write to report only.',
|
|
21
|
+
type: 'boolean',
|
|
22
|
+
default: true,
|
|
23
|
+
})
|
|
24
|
+
.option('config-path', {
|
|
25
|
+
description: 'Path to a biome.json. Default: walks up from cwd to find one.',
|
|
26
|
+
type: 'string',
|
|
27
|
+
normalize: true,
|
|
28
|
+
})
|
|
29
|
+
.option('verbose', {
|
|
30
|
+
description: 'Echo the resolved biome binary + args before spawning.',
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
default: false,
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
handler: async (args) => {
|
|
36
|
+
const cwd = process.cwd();
|
|
37
|
+
const paths = args.paths?.length
|
|
38
|
+
? args.paths
|
|
39
|
+
: ['.'];
|
|
40
|
+
const biomeArgs = ['check'];
|
|
41
|
+
if (args.write !== false)
|
|
42
|
+
biomeArgs.push('--write');
|
|
43
|
+
const configPath = args.configPath ?? findBiomeConfig(cwd) ?? undefined;
|
|
44
|
+
if (configPath)
|
|
45
|
+
biomeArgs.push(`--config-path=${resolve(configPath, '..')}`);
|
|
46
|
+
biomeArgs.push(...paths);
|
|
47
|
+
try {
|
|
48
|
+
const code = await runBiome(biomeArgs, { cwd, verbose: args.verbose });
|
|
49
|
+
process.exitCode = code;
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (err instanceof BiomeNotFoundError) {
|
|
53
|
+
printBiomeNotFound(err);
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Command } from '../../types/index.js';
|
|
2
|
+
interface DiffOptions {
|
|
3
|
+
version?: string;
|
|
4
|
+
appId?: string;
|
|
5
|
+
flathubRepo?: string;
|
|
6
|
+
against?: string;
|
|
7
|
+
detail?: boolean;
|
|
8
|
+
sourceIndex?: number;
|
|
9
|
+
verbose?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const flatpakDiffCommand: Command<unknown, DiffOptions>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// `gjsify flatpak diff` — report drift between the local config + git
|
|
2
|
+
// state and the per-app Flathub tracking-repo manifest.
|
|
3
|
+
//
|
|
4
|
+
// The most common failure mode is *version drift*: a release was cut
|
|
5
|
+
// locally (a new `git tag`), but the Flathub-repo manifest still pins
|
|
6
|
+
// the previous tag, so end-users on flatpak install never see the
|
|
7
|
+
// release. This command surfaces that drift before
|
|
8
|
+
// `gjsify flatpak sync-flathub` is needed (the symmetric command that
|
|
9
|
+
// fixes it).
|
|
10
|
+
//
|
|
11
|
+
// Workflow:
|
|
12
|
+
// 1. Resolve appId + flathub-repo (same precedence as sync-flathub)
|
|
13
|
+
// 2. Fetch the Flathub manifest (or read `--against <local>`)
|
|
14
|
+
// 3. Resolve the local latest git tag (`git describe --tags --abbrev=0`)
|
|
15
|
+
// 4. Compare module[0].sources's `tag` + `commit` to the local state
|
|
16
|
+
// 5. Exit 0 when in sync, 1 when there's drift, with a clear message
|
|
17
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
18
|
+
import { execFile } from 'node:child_process';
|
|
19
|
+
import { promisify } from 'node:util';
|
|
20
|
+
import { Config } from '../../config.js';
|
|
21
|
+
import { readPackageJson } from './utils.js';
|
|
22
|
+
const execFileAsync = promisify(execFile);
|
|
23
|
+
export const flatpakDiffCommand = {
|
|
24
|
+
command: 'diff',
|
|
25
|
+
description: 'Compare the per-app Flathub tracking-repo manifest against the local git state and report version / commit drift.',
|
|
26
|
+
builder: (yargs) => {
|
|
27
|
+
return yargs
|
|
28
|
+
.version(false)
|
|
29
|
+
.option('version', {
|
|
30
|
+
description: 'Local version to compare against. Default: `git describe --tags --abbrev=0` in cwd.',
|
|
31
|
+
type: 'string',
|
|
32
|
+
})
|
|
33
|
+
.option('app-id', {
|
|
34
|
+
description: 'Reverse-DNS app id. Default: `gjsify.flatpak.appId`.',
|
|
35
|
+
type: 'string',
|
|
36
|
+
})
|
|
37
|
+
.option('flathub-repo', {
|
|
38
|
+
description: 'Flathub tracking-repo (owner/name). Default: `gjsify.flatpak.flathubRepo` or `flathub/<appId>`.',
|
|
39
|
+
type: 'string',
|
|
40
|
+
})
|
|
41
|
+
.option('against', {
|
|
42
|
+
description: 'Read the Flathub manifest from a local file instead of fetching it. Useful in CI or offline.',
|
|
43
|
+
type: 'string',
|
|
44
|
+
})
|
|
45
|
+
.option('detail', {
|
|
46
|
+
description: 'Also print the full Flathub manifest source entry alongside the resolved local version.',
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
default: false,
|
|
49
|
+
})
|
|
50
|
+
.option('source-index', {
|
|
51
|
+
description: 'Index into modules[0].sources[] to inspect (when the manifest has multiple sources).',
|
|
52
|
+
type: 'number',
|
|
53
|
+
})
|
|
54
|
+
.option('verbose', {
|
|
55
|
+
description: 'Echo fetch URL + resolved values.',
|
|
56
|
+
type: 'boolean',
|
|
57
|
+
default: false,
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
handler: async (args) => {
|
|
61
|
+
const cwd = process.cwd();
|
|
62
|
+
const cfg = new Config();
|
|
63
|
+
const configData = await cfg.forBuild({}).catch(() => ({}));
|
|
64
|
+
const flatpak = configData.flatpak ?? {};
|
|
65
|
+
const appId = args.appId ??
|
|
66
|
+
flatpak.appId ??
|
|
67
|
+
readPackageJson(cwd).name;
|
|
68
|
+
if (!appId) {
|
|
69
|
+
throw new Error('[gjsify flatpak diff] no app id available — pass --app-id or set gjsify.flatpak.appId.');
|
|
70
|
+
}
|
|
71
|
+
const flathubRepo = args.flathubRepo ??
|
|
72
|
+
flatpak.flathubRepo ??
|
|
73
|
+
`flathub/${appId}`;
|
|
74
|
+
const localVersion = args.version ?? (await resolveLatestTag(cwd, args.verbose));
|
|
75
|
+
const remoteSource = await loadFlathubSource({ appId, flathubRepo, against: args.against, verbose: args.verbose }, args.sourceIndex);
|
|
76
|
+
const remoteTag = remoteSource?.tag;
|
|
77
|
+
const remoteCommit = remoteSource?.commit;
|
|
78
|
+
console.log(`[gjsify flatpak diff] appId=${appId}`);
|
|
79
|
+
console.log(`[gjsify flatpak diff] flathubRepo=${flathubRepo}`);
|
|
80
|
+
console.log(`[gjsify flatpak diff] flathub: tag=${remoteTag ?? '(missing)'} commit=${remoteCommit ?? '(missing)'}`);
|
|
81
|
+
console.log(`[gjsify flatpak diff] local: tag=${localVersion ?? '(none)'}`);
|
|
82
|
+
if (args.detail && remoteSource) {
|
|
83
|
+
console.log('[gjsify flatpak diff] flathub manifest source:');
|
|
84
|
+
console.log(JSON.stringify(remoteSource, null, 2));
|
|
85
|
+
}
|
|
86
|
+
if (!localVersion) {
|
|
87
|
+
console.warn('[gjsify flatpak diff] no local git tag found — cut a release (`git tag vX.Y.Z`) ' +
|
|
88
|
+
'or pass --version vX.Y.Z to compare against an explicit value.');
|
|
89
|
+
// Still surface remote state, but exit cleanly so this isn't a fatal CI step.
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (!remoteTag) {
|
|
93
|
+
console.warn('[gjsify flatpak diff] flathub manifest has no `tag` field on the inspected source.');
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
if (remoteTag === localVersion) {
|
|
97
|
+
console.log(`✅ in sync (${localVersion})`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
console.log(`❌ drift detected — flathub=${remoteTag} vs local=${localVersion}`);
|
|
101
|
+
console.log(` run \`gjsify flatpak sync-flathub --version ${localVersion}\` to update the Flathub manifest.`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
// ─── Internal helpers ────────────────────────────────────────────────────
|
|
106
|
+
async function loadFlathubSource(args, sourceIndex) {
|
|
107
|
+
let raw;
|
|
108
|
+
if (args.against) {
|
|
109
|
+
if (!existsSync(args.against)) {
|
|
110
|
+
throw new Error(`[gjsify flatpak diff] --against path ${args.against} does not exist`);
|
|
111
|
+
}
|
|
112
|
+
raw = readFileSync(args.against, 'utf-8');
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// Try main + master raw URLs on GitHub. Flathub repos historically use
|
|
116
|
+
// master; newer ones may use main. No XDG cache: fresh fetch each run.
|
|
117
|
+
raw = await fetchFlathubManifest(args.flathubRepo, args.appId, args.verbose);
|
|
118
|
+
}
|
|
119
|
+
let manifest;
|
|
120
|
+
try {
|
|
121
|
+
manifest = JSON.parse(raw);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
throw new Error(`[gjsify flatpak diff] failed to parse Flathub manifest as JSON: ${err.message}`);
|
|
125
|
+
}
|
|
126
|
+
const modules = manifest.modules ?? [];
|
|
127
|
+
const sources = modules[0]?.sources ?? [];
|
|
128
|
+
if (sources.length === 0)
|
|
129
|
+
return null;
|
|
130
|
+
const idx = sourceIndex ?? sources.findIndex((s) => s?.type === 'git');
|
|
131
|
+
if (idx < 0 || idx >= sources.length)
|
|
132
|
+
return null;
|
|
133
|
+
return sources[idx] ?? null;
|
|
134
|
+
}
|
|
135
|
+
async function fetchFlathubManifest(flathubRepo, appId, verbose) {
|
|
136
|
+
for (const branch of ['master', 'main']) {
|
|
137
|
+
const url = `https://raw.githubusercontent.com/${flathubRepo}/${branch}/${appId}.json`;
|
|
138
|
+
if (verbose)
|
|
139
|
+
console.log(`[gjsify flatpak diff] fetch ${url}`);
|
|
140
|
+
try {
|
|
141
|
+
const res = await fetch(url);
|
|
142
|
+
if (res.ok)
|
|
143
|
+
return await res.text();
|
|
144
|
+
if (verbose)
|
|
145
|
+
console.log(`[gjsify flatpak diff] ${branch} → HTTP ${res.status}`);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
if (verbose)
|
|
149
|
+
console.log(`[gjsify flatpak diff] ${branch} fetch error: ${err.message}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
throw new Error(`[gjsify flatpak diff] could not fetch flathub manifest from ${flathubRepo} on master or main`);
|
|
153
|
+
}
|
|
154
|
+
async function resolveLatestTag(cwd, verbose) {
|
|
155
|
+
try {
|
|
156
|
+
const { stdout } = await execFileAsync('git', ['describe', '--tags', '--abbrev=0'], { cwd });
|
|
157
|
+
const tag = stdout.trim();
|
|
158
|
+
if (verbose)
|
|
159
|
+
console.log(`[gjsify flatpak diff] local latest tag → ${tag}`);
|
|
160
|
+
return tag || null;
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -4,5 +4,8 @@ import { flatpakBuildCommand } from './build.js';
|
|
|
4
4
|
import { flatpakDepsCommand } from './deps.js';
|
|
5
5
|
import { flatpakCiCommand } from './ci.js';
|
|
6
6
|
import { flatpakCheckCommand } from './check.js';
|
|
7
|
+
import { flatpakSyncFlathubCommand } from './sync-flathub.js';
|
|
8
|
+
import { flatpakDiffCommand } from './diff.js';
|
|
9
|
+
import { flatpakReleaseCommand } from './release.js';
|
|
7
10
|
export declare const flatpakCommand: Command;
|
|
8
|
-
export { flatpakInitCommand, flatpakBuildCommand, flatpakDepsCommand, flatpakCiCommand, flatpakCheckCommand, };
|
|
11
|
+
export { flatpakInitCommand, flatpakBuildCommand, flatpakDepsCommand, flatpakCiCommand, flatpakCheckCommand, flatpakSyncFlathubCommand, flatpakDiffCommand, flatpakReleaseCommand, };
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
// `gjsify flatpak` — yargs subcommand-group dispatcher.
|
|
2
2
|
//
|
|
3
|
-
// Wires {init, build, deps, ci, check
|
|
4
|
-
// `Command<>` so it composes the
|
|
5
|
-
// `gsettings` at the top level.
|
|
3
|
+
// Wires {init, build, deps, ci, check, sync-flathub, diff, release}.
|
|
4
|
+
// Each subcommand is a self-contained `Command<>` so it composes the
|
|
5
|
+
// same way as `gresource` / `gettext` / `gsettings` at the top level.
|
|
6
6
|
import { flatpakInitCommand } from './init.js';
|
|
7
7
|
import { flatpakBuildCommand } from './build.js';
|
|
8
8
|
import { flatpakDepsCommand } from './deps.js';
|
|
9
9
|
import { flatpakCiCommand } from './ci.js';
|
|
10
10
|
import { flatpakCheckCommand } from './check.js';
|
|
11
|
+
import { flatpakSyncFlathubCommand } from './sync-flathub.js';
|
|
12
|
+
import { flatpakDiffCommand } from './diff.js';
|
|
13
|
+
import { flatpakReleaseCommand } from './release.js';
|
|
11
14
|
export const flatpakCommand = {
|
|
12
15
|
command: 'flatpak <subcommand>',
|
|
13
|
-
description: 'Flatpak toolchain: init/build/deps/ci/check subcommands for shipping GJS apps and CLIs as Flatpaks.',
|
|
16
|
+
description: 'Flatpak toolchain: init/build/deps/ci/check/sync-flathub/diff/release subcommands for shipping GJS apps and CLIs as Flatpaks.',
|
|
14
17
|
builder: (yargs) => {
|
|
15
18
|
return yargs
|
|
16
19
|
.command(flatpakInitCommand.command, flatpakInitCommand.description, flatpakInitCommand.builder, flatpakInitCommand.handler)
|
|
@@ -18,8 +21,11 @@ export const flatpakCommand = {
|
|
|
18
21
|
.command(flatpakDepsCommand.command, flatpakDepsCommand.description, flatpakDepsCommand.builder, flatpakDepsCommand.handler)
|
|
19
22
|
.command(flatpakCiCommand.command, flatpakCiCommand.description, flatpakCiCommand.builder, flatpakCiCommand.handler)
|
|
20
23
|
.command(flatpakCheckCommand.command, flatpakCheckCommand.description, flatpakCheckCommand.builder, flatpakCheckCommand.handler)
|
|
24
|
+
.command(flatpakSyncFlathubCommand.command, flatpakSyncFlathubCommand.description, flatpakSyncFlathubCommand.builder, flatpakSyncFlathubCommand.handler)
|
|
25
|
+
.command(flatpakDiffCommand.command, flatpakDiffCommand.description, flatpakDiffCommand.builder, flatpakDiffCommand.handler)
|
|
26
|
+
.command(flatpakReleaseCommand.command, flatpakReleaseCommand.description, flatpakReleaseCommand.builder, flatpakReleaseCommand.handler)
|
|
21
27
|
.demandCommand(1)
|
|
22
28
|
.strict();
|
|
23
29
|
},
|
|
24
30
|
};
|
|
25
|
-
export { flatpakInitCommand, flatpakBuildCommand, flatpakDepsCommand, flatpakCiCommand, flatpakCheckCommand, };
|
|
31
|
+
export { flatpakInitCommand, flatpakBuildCommand, flatpakDepsCommand, flatpakCiCommand, flatpakCheckCommand, flatpakSyncFlathubCommand, flatpakDiffCommand, flatpakReleaseCommand, };
|
|
@@ -16,6 +16,7 @@ import { dirname, resolve } from 'node:path';
|
|
|
16
16
|
import { DEFAULT_CLI_FINISH_ARGS, DEFAULT_GUI_FINISH_ARGS, looksLikeAppId, readPackageJson, resolveRuntime, } from './utils.js';
|
|
17
17
|
import { renderDesktop, renderFlathubJson, renderMetainfoApp, renderMetainfoCli, validateScaffoldInputs, } from './scaffold.js';
|
|
18
18
|
import { Config } from '../../config.js';
|
|
19
|
+
import { BiomeNotFoundError, hasBiomeDevDep, runBiome, } from '../../utils/biome-resolve.js';
|
|
19
20
|
export const flatpakInitCommand = {
|
|
20
21
|
command: 'init',
|
|
21
22
|
description: 'Generate Flatpak manifest + MetaInfo XML + .desktop + flathub.json from `gjsify.flatpak` config.',
|
|
@@ -85,6 +86,12 @@ export const flatpakInitCommand = {
|
|
|
85
86
|
description: 'Print the resolved manifest fields before writing',
|
|
86
87
|
type: 'boolean',
|
|
87
88
|
default: false,
|
|
89
|
+
})
|
|
90
|
+
.option('format', {
|
|
91
|
+
description: 'Run `gjsify format --write` on the generated files when `@biomejs/biome` is detected in the project. ' +
|
|
92
|
+
'Default: true. Pass --no-format to skip.',
|
|
93
|
+
type: 'boolean',
|
|
94
|
+
default: true,
|
|
88
95
|
});
|
|
89
96
|
},
|
|
90
97
|
handler: async (args) => {
|
|
@@ -140,13 +147,18 @@ export const flatpakInitCommand = {
|
|
|
140
147
|
sources: [{ type: 'dir', path: '.' }],
|
|
141
148
|
});
|
|
142
149
|
manifest.modules = modules;
|
|
150
|
+
const writtenFiles = [];
|
|
151
|
+
const trackWrite = (p) => {
|
|
152
|
+
if (p)
|
|
153
|
+
writtenFiles.push(p);
|
|
154
|
+
};
|
|
143
155
|
const manifestOut = args.manifest ?? `${appId}.json`;
|
|
144
156
|
const manifestPath = resolve(cwd, manifestOut);
|
|
145
|
-
writeIfFresh(manifestPath, JSON.stringify(manifest, null,
|
|
146
|
-
const
|
|
157
|
+
trackWrite(writeIfFresh(manifestPath, JSON.stringify(manifest, null, 2) + '\n', args.force ?? false, 'manifest'));
|
|
158
|
+
const pkgName = pkg.name ?? appId;
|
|
147
159
|
const scaffold = {
|
|
148
160
|
appId,
|
|
149
|
-
name: friendlyName(
|
|
161
|
+
name: flatpak.name ?? friendlyName(pkgName, appId),
|
|
150
162
|
command,
|
|
151
163
|
kind,
|
|
152
164
|
flatpak,
|
|
@@ -161,17 +173,38 @@ export const flatpakInitCommand = {
|
|
|
161
173
|
else {
|
|
162
174
|
const metainfoXml = kind === 'cli' ? renderMetainfoCli(scaffold) : renderMetainfoApp(scaffold);
|
|
163
175
|
const metainfoOut = args.metainfo ?? `data/${appId}.metainfo.xml.in`;
|
|
164
|
-
writeIfFresh(resolve(cwd, metainfoOut), metainfoXml, args.force ?? false, 'metainfo');
|
|
176
|
+
trackWrite(writeIfFresh(resolve(cwd, metainfoOut), metainfoXml, args.force ?? false, 'metainfo'));
|
|
165
177
|
if (kind === 'app') {
|
|
166
178
|
const desktopOut = args.desktop ?? `data/${appId}.desktop.in`;
|
|
167
|
-
writeIfFresh(resolve(cwd, desktopOut), renderDesktop(scaffold), args.force ?? false, 'desktop');
|
|
179
|
+
trackWrite(writeIfFresh(resolve(cwd, desktopOut), renderDesktop(scaffold), args.force ?? false, 'desktop'));
|
|
168
180
|
if (!flatpak.icon) {
|
|
169
181
|
console.warn(`[gjsify flatpak init] No gjsify.flatpak.icon set. Flathub requires a scalable SVG at\n` +
|
|
170
182
|
` data/icons/hicolor/scalable/apps/${appId}.svg`);
|
|
171
183
|
}
|
|
172
184
|
}
|
|
173
185
|
const flathubOut = args.flathubJson ?? 'flathub.json';
|
|
174
|
-
writeIfFresh(resolve(cwd, flathubOut), renderFlathubJson(kind), args.force ?? false, 'flathub.json');
|
|
186
|
+
trackWrite(writeIfFresh(resolve(cwd, flathubOut), renderFlathubJson(kind), args.force ?? false, 'flathub.json'));
|
|
187
|
+
}
|
|
188
|
+
// Optional post-format: when biome is configured in the project,
|
|
189
|
+
// run `biome format --write` on the generated files so they match
|
|
190
|
+
// the project's prettier/biome style. Default behaviour (2-space
|
|
191
|
+
// JSON) already matches biome/prettier/Flathub defaults; this
|
|
192
|
+
// step harmonises edge-case fields (line endings, trailing commas
|
|
193
|
+
// in JSONC, key sort order if biome's organize-imports has rules).
|
|
194
|
+
if (writtenFiles.length > 0 && args.format !== false && hasBiomeDevDep(cwd)) {
|
|
195
|
+
try {
|
|
196
|
+
await runBiome(['format', '--write', ...writtenFiles], { cwd });
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
if (err instanceof BiomeNotFoundError) {
|
|
200
|
+
// Biome configured but binary missing — non-fatal warning.
|
|
201
|
+
console.warn(`[gjsify flatpak init] post-format skipped: @biomejs/biome declared but binary not installed. ` +
|
|
202
|
+
`Run \`gjsify install\` then re-run with --force, or pass --no-format.`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
throw err;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
175
208
|
}
|
|
176
209
|
if (args.verbose) {
|
|
177
210
|
console.log(`[gjsify flatpak init] kind=${kind} runtime=${runtimeId} ${runtimeVersion} sdk=${sdk}`);
|
|
@@ -183,11 +216,12 @@ export const flatpakInitCommand = {
|
|
|
183
216
|
function writeIfFresh(path, content, force, label) {
|
|
184
217
|
if (existsSync(path) && !force) {
|
|
185
218
|
console.log(`[gjsify flatpak init] skipped ${label}: ${path} (exists; --force to overwrite)`);
|
|
186
|
-
return;
|
|
219
|
+
return null;
|
|
187
220
|
}
|
|
188
221
|
mkdirSync(dirname(path), { recursive: true });
|
|
189
222
|
writeFileSync(path, content, 'utf-8');
|
|
190
223
|
console.log(`[gjsify flatpak init] wrote ${label}: ${path}`);
|
|
224
|
+
return path;
|
|
191
225
|
}
|
|
192
226
|
function friendlyName(pkgName, appId) {
|
|
193
227
|
if (pkgName.startsWith('@')) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Command } from '../../types/index.js';
|
|
2
|
+
interface ReleaseOptions {
|
|
3
|
+
version: string;
|
|
4
|
+
skipTag?: boolean;
|
|
5
|
+
skipCheck?: boolean;
|
|
6
|
+
skipInit?: boolean;
|
|
7
|
+
pushTag?: boolean;
|
|
8
|
+
dryRun?: boolean;
|
|
9
|
+
flathubRepo?: string;
|
|
10
|
+
verbose?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const flatpakReleaseCommand: Command<unknown, ReleaseOptions>;
|
|
13
|
+
export {};
|