@gjsify/cli 0.43.0 → 0.45.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 +453 -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/gettext.d.ts +2 -1
- package/lib/commands/gettext.js +105 -47
- package/lib/commands/onboard.d.ts +7 -0
- package/lib/commands/onboard.js +349 -96
- package/lib/commands/ship.d.ts +3 -0
- package/lib/commands/ship.js +911 -70
- package/lib/commands/trust.d.ts +6 -0
- package/lib/commands/trust.js +1 -1
- package/lib/config.js +25 -0
- package/lib/types/cli-build-options.d.ts +11 -0
- package/lib/types/config-data.d.ts +137 -3
- package/lib/utils/flatpak-runtime.d.ts +41 -0
- package/lib/utils/flatpak-runtime.js +54 -0
- package/lib/utils/msgfmt-merge.d.ts +35 -0
- package/lib/utils/msgfmt-merge.js +102 -0
- package/lib/utils/npm-otp.d.ts +27 -2
- package/lib/utils/npm-otp.js +82 -7
- package/lib/utils/onboard-discovery.d.ts +68 -0
- package/lib/utils/onboard-discovery.js +178 -0
- package/lib/utils/onboard-probe.d.ts +118 -1
- package/lib/utils/onboard-probe.js +153 -4
- package/lib/utils/prompt-output.d.ts +12 -0
- package/lib/utils/prompt-output.js +54 -0
- package/lib/utils/prompt.js +22 -2
- package/lib/utils/publishable-packages.d.ts +1 -1
- package/lib/utils/publishable-packages.js +1 -0
- package/lib/utils/resolve-npm-package.d.ts +56 -0
- package/lib/utils/resolve-npm-package.js +69 -0
- package/lib/utils/ship/app-runtime.d.ts +214 -0
- package/lib/utils/ship/app-runtime.js +429 -0
- package/lib/utils/ship/depends.d.ts +54 -7
- package/lib/utils/ship/depends.js +120 -6
- package/lib/utils/ship/discover.js +18 -1
- package/lib/utils/ship/dmg.d.ts +71 -0
- package/lib/utils/ship/dmg.js +181 -0
- 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 +116 -3
- package/lib/utils/ship/formats.js +869 -11
- package/lib/utils/ship/launcher.d.ts +13 -3
- package/lib/utils/ship/launcher.js +362 -17
- package/lib/utils/ship/layout.d.ts +268 -0
- package/lib/utils/ship/layout.js +351 -0
- package/lib/utils/ship/localize-metadata.d.ts +22 -0
- package/lib/utils/ship/localize-metadata.js +126 -0
- package/lib/utils/ship/mime.js +1 -9
- package/lib/utils/ship/msi.d.ts +126 -0
- package/lib/utils/ship/msi.js +417 -0
- package/lib/utils/ship/node-runtime.d.ts +71 -0
- package/lib/utils/ship/node-runtime.js +122 -0
- package/lib/utils/ship/payload.d.ts +156 -5
- package/lib/utils/ship/payload.js +580 -14
- package/lib/utils/ship/plan.d.ts +21 -0
- package/lib/utils/ship/plan.js +20 -7
- package/lib/utils/ship/plist.d.ts +58 -0
- package/lib/utils/ship/plist.js +198 -0
- package/lib/utils/ship/rpm.js +9 -3
- package/lib/utils/ship/schemas.d.ts +61 -0
- package/lib/utils/ship/schemas.js +118 -0
- package/lib/utils/ship/scripts.js +7 -3
- package/lib/utils/ship/settings.d.ts +19 -1
- package/lib/utils/ship/settings.js +19 -1
- package/lib/utils/ship/share-dirs.d.ts +23 -0
- package/lib/utils/ship/share-dirs.js +46 -0
- package/lib/utils/ship/signing.d.ts +195 -0
- package/lib/utils/ship/signing.js +317 -0
- package/lib/utils/ship/stage-manifest.d.ts +50 -13
- package/lib/utils/ship/stage-manifest.js +137 -14
- package/lib/utils/ship/stage-writer.d.ts +48 -0
- package/lib/utils/ship/stage-writer.js +71 -1
- package/lib/utils/ship/types.d.ts +264 -16
- package/lib/utils/ship/xml.d.ts +7 -0
- package/lib/utils/ship/xml.js +25 -0
- package/lib/utils/ship/zip.d.ts +69 -0
- package/lib/utils/ship/zip.js +245 -0
- package/lib/utils/trust-registry.js +5 -0
- package/package.json +19 -19
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// The deprecation window on the `gjsify.flatpak` build keys (ADR 0024 § 8).
|
|
2
|
+
//
|
|
3
|
+
// § 8 names the cost of the migration and does not let it be skipped:
|
|
4
|
+
// "`gjsify.flatpak` is a published config contract, so the keys move with a
|
|
5
|
+
// deprecation window in which both spellings resolve and the old one warns."
|
|
6
|
+
// This file is that window.
|
|
7
|
+
//
|
|
8
|
+
// WHAT MOVED and what did not — the table is `docs/ship-formats.md`, and the
|
|
9
|
+
// short form is: the six BUILD keys moved and warn from the old spelling; the
|
|
10
|
+
// `AppMetadata` half is a designed ALIAS and is NOT deprecated (both blocks
|
|
11
|
+
// describe the same app, and warning on it would print for every project that
|
|
12
|
+
// has a `gjsify.flatpak` block at all); and the TOOLCHAIN keys are untouched
|
|
13
|
+
// because `gjsify flatpak <sub>` has not moved, so deprecating them would warn
|
|
14
|
+
// on commands with nowhere else to read from.
|
|
15
|
+
//
|
|
16
|
+
// A WINDOW HAS TWO SIDES, and the first cut only built one. The six build keys
|
|
17
|
+
// are read by `gjsify flatpak init` and `flatpak ci` as well, and those
|
|
18
|
+
// commands have NOT moved — so a project that did what this file's warning told
|
|
19
|
+
// it to do, and moved them, silently lost them there: `flatpak init` fell back
|
|
20
|
+
// to its own defaults and wrote a manifest against a different
|
|
21
|
+
// `org.gnome.Platform` version with different finish-args, committed, at exit 0.
|
|
22
|
+
// That is why {@link pickFlatpakBuildKeys} is exported and BOTH command groups
|
|
23
|
+
// go through it. "Both spellings resolve" is now true of every reader of these
|
|
24
|
+
// keys, which is what makes the advice safe to follow.
|
|
25
|
+
//
|
|
26
|
+
// WHY A NAMED VERSION, IN ONE PLACE. The precedent is
|
|
27
|
+
// `utils/normalize-bundler-options.ts`, the `esbuild` → `bundler` shim, and it
|
|
28
|
+
// also carries the precedent's DEFECT: its warning text names 0.5.0, the tree is
|
|
29
|
+
// far past it, and its header now has to say "removing the shim means fixing
|
|
30
|
+
// that string too". So the version here is a constant the message reads, and the
|
|
31
|
+
// spec asserts the message names it.
|
|
32
|
+
import { DEFAULT_CLI_FINISH_ARGS, DEFAULT_GUI_FINISH_ARGS, deriveAppendPath, resolveRuntime, } from '../flatpak-runtime.js';
|
|
33
|
+
/**
|
|
34
|
+
* The release that stops reading `gjsify.flatpak` for the keys below.
|
|
35
|
+
*
|
|
36
|
+
* A major, not the next minor: the block is in published releases and in
|
|
37
|
+
* downstream `package.json`s that this repository does not control, and a
|
|
38
|
+
* window measured in weeks is a window nobody notices before it closes.
|
|
39
|
+
*/
|
|
40
|
+
export const LEGACY_FLATPAK_KEYS_REMOVED_IN = '1.0.0';
|
|
41
|
+
/** The keys whose new home is `gjsify.ship.flatpak`, in the order the warning lists them. */
|
|
42
|
+
export const MIGRATED_FLATPAK_KEYS = [
|
|
43
|
+
'runtime',
|
|
44
|
+
'runtimeVersion',
|
|
45
|
+
'sdkExtensions',
|
|
46
|
+
'appendPath',
|
|
47
|
+
'finishArgs',
|
|
48
|
+
'cleanup',
|
|
49
|
+
];
|
|
50
|
+
/**
|
|
51
|
+
* `gjsify.ship.flatpak.<key>` when set, else `gjsify.flatpak.<key>` — per KEY,
|
|
52
|
+
* noting which side answered.
|
|
53
|
+
*
|
|
54
|
+
* Per-key and not per-block: a project migrating one key at a time must not
|
|
55
|
+
* lose the five it has not moved yet. That is also what makes the warning
|
|
56
|
+
* actionable — it names the keys still coming from the old block, so the reader
|
|
57
|
+
* knows what to edit rather than being told a block is deprecated.
|
|
58
|
+
*
|
|
59
|
+
* A LOOP over {@link MIGRATED_FLATPAK_KEYS}, not six hand-written picks. Written
|
|
60
|
+
* out, the list was the one unbound copy of this vocabulary: a key added to it
|
|
61
|
+
* with no `pick` call behind it compiled fine, went missing from the window, and
|
|
62
|
+
* the only test that mentioned the constant compared it to a literal copy of
|
|
63
|
+
* itself. Reading the keys off the constant is what makes "this list IS the
|
|
64
|
+
* window" true rather than asserted.
|
|
65
|
+
*
|
|
66
|
+
* NO default is applied here. The defaults differ per reader — `ship` derives
|
|
67
|
+
* `finishArgs` from `kind`, `flatpak init` also merges `--sdk-extension` flags —
|
|
68
|
+
* and a default applied here would be a third answer neither of them chose.
|
|
69
|
+
*/
|
|
70
|
+
export function pickFlatpakBuildKeys(preferred, legacy) {
|
|
71
|
+
const ship = preferred ?? {};
|
|
72
|
+
// Assembled through an index signature because the value type varies per
|
|
73
|
+
// key; the cast is on the finished object, where every key is present.
|
|
74
|
+
const values = {};
|
|
75
|
+
const fromLegacy = [];
|
|
76
|
+
for (const key of MIGRATED_FLATPAK_KEYS) {
|
|
77
|
+
const own = ship[key];
|
|
78
|
+
if (own !== undefined) {
|
|
79
|
+
values[key] = own;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const inherited = legacy[key];
|
|
83
|
+
if (inherited !== undefined)
|
|
84
|
+
fromLegacy.push(key);
|
|
85
|
+
values[key] = inherited;
|
|
86
|
+
}
|
|
87
|
+
return { values: values, fromLegacy };
|
|
88
|
+
}
|
|
89
|
+
/** The branch a `ship` Flatpak is exported under when the project names none. */
|
|
90
|
+
export const DEFAULT_SHIP_FLATPAK_BRANCH = 'stable';
|
|
91
|
+
/**
|
|
92
|
+
* Resolve the Flatpak half of `gjsify.ship`, reading the legacy block where the
|
|
93
|
+
* new one is silent, and applying `ship`'s own defaults.
|
|
94
|
+
*
|
|
95
|
+
* The per-key fallback itself is {@link pickFlatpakBuildKeys}, shared with
|
|
96
|
+
* `gjsify flatpak init`/`ci`; what is `ship`-specific and stays here is the
|
|
97
|
+
* defaulting — `branch`, the `kind`-dependent finish-args, the derived
|
|
98
|
+
* `appendPath` — plus the warning, which only `ship` prints because only `ship`
|
|
99
|
+
* has somewhere else to point at.
|
|
100
|
+
*/
|
|
101
|
+
export function resolveShipFlatpakSettings(input) {
|
|
102
|
+
const shipFlatpak = input.ship.flatpak ?? {};
|
|
103
|
+
const { values, fromLegacy } = pickFlatpakBuildKeys(shipFlatpak, input.flatpak);
|
|
104
|
+
const resolved = resolveRuntime(values, {});
|
|
105
|
+
const extensions = values.sdkExtensions ?? [];
|
|
106
|
+
const { appendPath, finishArgs, cleanup } = values;
|
|
107
|
+
const settings = {
|
|
108
|
+
runtime: resolved.runtimeId,
|
|
109
|
+
runtimeVersion: resolved.runtimeVersion,
|
|
110
|
+
sdk: resolved.sdk,
|
|
111
|
+
branch: shipFlatpak.branch ?? DEFAULT_SHIP_FLATPAK_BRANCH,
|
|
112
|
+
sdkExtensions: [...extensions],
|
|
113
|
+
// Derived from the extensions when nothing was declared, and only then:
|
|
114
|
+
// an extension whose `/usr/lib/sdk/<x>/bin` is not on PATH inside the
|
|
115
|
+
// sandbox is an extension the build cannot use, which looks like the
|
|
116
|
+
// extension not being installed.
|
|
117
|
+
appendPath: appendPath ?? (extensions.length > 0 ? deriveAppendPath(extensions) : []),
|
|
118
|
+
finishArgs: finishArgs ?? (input.kind === 'cli' ? DEFAULT_CLI_FINISH_ARGS : DEFAULT_GUI_FINISH_ARGS),
|
|
119
|
+
cleanup: cleanup ?? [],
|
|
120
|
+
};
|
|
121
|
+
return { settings, warnings: fromLegacy.length === 0 ? [] : [deprecationWarning(fromLegacy)] };
|
|
122
|
+
}
|
|
123
|
+
/** One line, naming every key still read from the old block and where each goes. */
|
|
124
|
+
function deprecationWarning(keys) {
|
|
125
|
+
const list = keys.map((key) => `gjsify.flatpak.${key}`).join(', ');
|
|
126
|
+
const moved = keys.map((key) => `gjsify.ship.flatpak.${key}`).join(', ');
|
|
127
|
+
return (`DEPRECATION: ${list} ${keys.length > 1 ? 'are' : 'is'} read by \`gjsify ship\` for compatibility and ` +
|
|
128
|
+
`will be removed in ${LEGACY_FLATPAK_KEYS_REMOVED_IN}. Move ${keys.length > 1 ? 'them' : 'it'} to ` +
|
|
129
|
+
`${moved} — both spellings resolve until then, and \`gjsify.ship.flatpak\` wins where both are set. ` +
|
|
130
|
+
'The app METADATA in `gjsify.flatpak` (name, summary, developer, categories, licence) is NOT ' +
|
|
131
|
+
'deprecated: both blocks describe the same application and either may carry it (ADR 0024 § 8).');
|
|
132
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { PackSettings } from './types.js';
|
|
2
|
+
export interface FlatpakManifestInput {
|
|
3
|
+
settings: PackSettings;
|
|
4
|
+
/** Absolute path of the staged payload — the tree `readStage` validated. */
|
|
5
|
+
stageDir: string;
|
|
6
|
+
/** Absolute path of this format's rendered overlay (the licence file). */
|
|
7
|
+
overlayDir: string;
|
|
8
|
+
/** The stage's own closure file, which is not payload and must not be copied into `/app`. */
|
|
9
|
+
stageManifestFile: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The generated Flatpak manifest, as a plain object.
|
|
13
|
+
*
|
|
14
|
+
* Pure, so every field is unit-testable without flatpak tooling — and the
|
|
15
|
+
* suite that needs that is the one running in CI, where there is none.
|
|
16
|
+
*
|
|
17
|
+
* The app id is what makes this legal Flatpak: every file flatpak EXPORTS out
|
|
18
|
+
* of `/app/share` has to be named after it, and `planStage` already enforces
|
|
19
|
+
* exactly that for its own reasons — the desktop entry, the metainfo, the icon,
|
|
20
|
+
* the GSettings schema and the shared-mime-info document are all
|
|
21
|
+
* `<appId>[.…].<ext>`, because `share/glib-2.0/schemas/` and
|
|
22
|
+
* `share/mime/packages/` are directories shared between packages. Two
|
|
23
|
+
* independent constraints, one naming rule.
|
|
24
|
+
*/
|
|
25
|
+
export declare function renderShipFlatpakManifest(input: FlatpakManifestInput): Record<string, unknown>;
|
|
26
|
+
export interface FlatpakPackInput extends FlatpakManifestInput {
|
|
27
|
+
/** Working root for the manifest, the build dir, the repo and the builder's state. */
|
|
28
|
+
workDir: string;
|
|
29
|
+
/** Absolute path the finished `.flatpak` is written to. */
|
|
30
|
+
target: string;
|
|
31
|
+
/** Flatpak's spelling of the architecture, e.g. `x86_64`. */
|
|
32
|
+
archLabel: string;
|
|
33
|
+
verbose: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build the app and export a single-file bundle at `input.target`.
|
|
37
|
+
*
|
|
38
|
+
* The only packer in this tree that execs anything, and the descriptor says so
|
|
39
|
+
* (`FORMATS.flatpak.host.requiredTools`). ADR 0024 § A3 is explicit that this
|
|
40
|
+
* costs the independent oracle the hand-written deb/rpm writers bought: what
|
|
41
|
+
* pays it back here is that a `.flatpak` is an OSTree static delta, so
|
|
42
|
+
* `flatpak build-import-bundle` + `ostree ls -R` read the artifact back with
|
|
43
|
+
* code this tree did not write.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildFlatpakBundle(input: FlatpakPackInput): Promise<void>;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// The Flatpak packer — ADR 0024 § 8, stage 6.
|
|
2
|
+
//
|
|
3
|
+
// This is a MOVE, not a second implementation. `gjsify flatpak` has emitted
|
|
4
|
+
// Flatpak manifests since long before `ship` existed; what it emitted was a
|
|
5
|
+
// `buildsystem: meson` module, which made every app carry meson glue whose only
|
|
6
|
+
// job was to call `gjsify build` and copy the result into a prefix — 158 lines
|
|
7
|
+
// of it in Learn6502, 66 of them `GI_TYPELIB_PATH`/`LD_LIBRARY_PATH` plumbing.
|
|
8
|
+
// `ship` already produces that prefix. So the module becomes:
|
|
9
|
+
//
|
|
10
|
+
// buildsystem: simple
|
|
11
|
+
// build-commands: mkdir -p /app; cp -a stage/. /app/; cp -a overlay/. /app/
|
|
12
|
+
//
|
|
13
|
+
// and meson leaves the sandbox. Nothing else about the payload changes — the
|
|
14
|
+
// launcher derives its own prefix at runtime (§ 3), which is exactly why one
|
|
15
|
+
// staged tree can be `/usr` in a `.deb` and `/app` here.
|
|
16
|
+
//
|
|
17
|
+
// FOUR THINGS ABOUT flatpak-builder 1.4.10 WERE MEASURED before this file was
|
|
18
|
+
// written, because each had a plausible wrong answer and flatpak-builder reports
|
|
19
|
+
// none of them: an absolute `dir` source `path` works, `skip` works and is what
|
|
20
|
+
// keeps the stage's own sidecar out of `/app`, `cp -a` preserves the mode, and
|
|
21
|
+
// `--show-manifest` is NOT a validator (it accepted an unknown source property
|
|
22
|
+
// and `buildsystem: "nonsense"` at exit 0, so it must never be used as a gate).
|
|
23
|
+
// The numbers, the commands and the oracle that replaces `--show-manifest`:
|
|
24
|
+
// `docs/ship-formats.md`.
|
|
25
|
+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
26
|
+
import { join } from 'node:path';
|
|
27
|
+
import { describeExit, spawnToCompletion } from '../spawn.js';
|
|
28
|
+
/**
|
|
29
|
+
* The generated Flatpak manifest, as a plain object.
|
|
30
|
+
*
|
|
31
|
+
* Pure, so every field is unit-testable without flatpak tooling — and the
|
|
32
|
+
* suite that needs that is the one running in CI, where there is none.
|
|
33
|
+
*
|
|
34
|
+
* The app id is what makes this legal Flatpak: every file flatpak EXPORTS out
|
|
35
|
+
* of `/app/share` has to be named after it, and `planStage` already enforces
|
|
36
|
+
* exactly that for its own reasons — the desktop entry, the metainfo, the icon,
|
|
37
|
+
* the GSettings schema and the shared-mime-info document are all
|
|
38
|
+
* `<appId>[.…].<ext>`, because `share/glib-2.0/schemas/` and
|
|
39
|
+
* `share/mime/packages/` are directories shared between packages. Two
|
|
40
|
+
* independent constraints, one naming rule.
|
|
41
|
+
*/
|
|
42
|
+
export function renderShipFlatpakManifest(input) {
|
|
43
|
+
const { settings, stageDir, overlayDir } = input;
|
|
44
|
+
const flatpak = settings.flatpak;
|
|
45
|
+
const manifest = {
|
|
46
|
+
id: settings.appId,
|
|
47
|
+
runtime: flatpak.runtime,
|
|
48
|
+
'runtime-version': flatpak.runtimeVersion,
|
|
49
|
+
sdk: flatpak.sdk,
|
|
50
|
+
branch: flatpak.branch,
|
|
51
|
+
// `bin/<binaryName>`, which is the one executable the stage plans. NOT
|
|
52
|
+
// the app id, which is what `gjsify flatpak init` defaults to: under
|
|
53
|
+
// `ship` the launcher's name is decided by `gjsify.ship.binaryName`, and
|
|
54
|
+
// a `command` naming a file that is not in `/app/bin` produces an app
|
|
55
|
+
// that installs and cannot be launched.
|
|
56
|
+
command: settings.binaryName,
|
|
57
|
+
'finish-args': flatpak.finishArgs,
|
|
58
|
+
};
|
|
59
|
+
if (flatpak.sdkExtensions.length > 0)
|
|
60
|
+
manifest['sdk-extensions'] = flatpak.sdkExtensions;
|
|
61
|
+
if (flatpak.appendPath.length > 0) {
|
|
62
|
+
manifest['build-options'] = { 'append-path': flatpak.appendPath.join(':') };
|
|
63
|
+
}
|
|
64
|
+
if (flatpak.cleanup.length > 0)
|
|
65
|
+
manifest.cleanup = flatpak.cleanup;
|
|
66
|
+
manifest.modules = [
|
|
67
|
+
{
|
|
68
|
+
name: settings.binaryName,
|
|
69
|
+
buildsystem: 'simple',
|
|
70
|
+
// `mkdir -p /app` first: flatpak-builder creates the app directory,
|
|
71
|
+
// but a module whose only command is a `cp` into a path that does
|
|
72
|
+
// not exist yet is one refactor away from failing, and the mkdir is
|
|
73
|
+
// free. `stage/.` and not `stage` — `cp -a stage /app/` would
|
|
74
|
+
// create `/app/stage`, which installs cleanly and puts the whole
|
|
75
|
+
// payload one directory too deep.
|
|
76
|
+
'build-commands': ['mkdir -p /app', 'cp -a stage/. /app/', 'cp -a overlay/. /app/'],
|
|
77
|
+
sources: [
|
|
78
|
+
{ type: 'dir', path: stageDir, dest: 'stage', skip: [input.stageManifestFile] },
|
|
79
|
+
{ type: 'dir', path: overlayDir, dest: 'overlay' },
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
return manifest;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Build the app and export a single-file bundle at `input.target`.
|
|
87
|
+
*
|
|
88
|
+
* The only packer in this tree that execs anything, and the descriptor says so
|
|
89
|
+
* (`FORMATS.flatpak.host.requiredTools`). ADR 0024 § A3 is explicit that this
|
|
90
|
+
* costs the independent oracle the hand-written deb/rpm writers bought: what
|
|
91
|
+
* pays it back here is that a `.flatpak` is an OSTree static delta, so
|
|
92
|
+
* `flatpak build-import-bundle` + `ostree ls -R` read the artifact back with
|
|
93
|
+
* code this tree did not write.
|
|
94
|
+
*/
|
|
95
|
+
export async function buildFlatpakBundle(input) {
|
|
96
|
+
const { settings, workDir, archLabel } = input;
|
|
97
|
+
const manifestPath = join(workDir, `${settings.appId}.json`);
|
|
98
|
+
const buildDir = join(workDir, 'build');
|
|
99
|
+
const repoDir = join(workDir, 'repo');
|
|
100
|
+
// Kept between runs: it is flatpak-builder's own ostree cache, and dropping
|
|
101
|
+
// it turns every re-pack into a full rebuild of every module.
|
|
102
|
+
const stateDir = join(workDir, 'state');
|
|
103
|
+
mkdirSync(workDir, { recursive: true });
|
|
104
|
+
// The repo is wiped, the state dir is not. A repo that survives holds the
|
|
105
|
+
// PREVIOUS commit on the same ref, and `build-bundle` would happily export
|
|
106
|
+
// it if this run's export failed for any reason that did not fail the
|
|
107
|
+
// build — a stale artifact wearing the new version's filename.
|
|
108
|
+
rmSync(repoDir, { recursive: true, force: true, maxRetries: 5 });
|
|
109
|
+
writeFileSync(manifestPath, `${JSON.stringify(renderShipFlatpakManifest(input), null, 2)}\n`);
|
|
110
|
+
await run('flatpak-builder', [
|
|
111
|
+
'--force-clean',
|
|
112
|
+
// Needed wherever FUSE is unavailable — containers, most CI runners.
|
|
113
|
+
// It only disables a read-only-bind optimisation, and a build that
|
|
114
|
+
// cannot start is worse than one that copies a few more bytes.
|
|
115
|
+
'--disable-rofiles-fuse',
|
|
116
|
+
'--delete-build-dirs',
|
|
117
|
+
`--state-dir=${stateDir}`,
|
|
118
|
+
`--arch=${archLabel}`,
|
|
119
|
+
`--repo=${repoDir}`,
|
|
120
|
+
buildDir,
|
|
121
|
+
manifestPath,
|
|
122
|
+
], input, 'flatpak-builder not found. Install it (Fedora: `sudo dnf install flatpak-builder`).');
|
|
123
|
+
// The branch is passed explicitly even though the manifest declares it: the
|
|
124
|
+
// two have to name the same ref, and `build-bundle`'s default is `master`
|
|
125
|
+
// while a manifest that sets `branch` exports somewhere else. Leaving it
|
|
126
|
+
// implicit is how this exports nothing and says "ref not found".
|
|
127
|
+
await run('flatpak', ['build-bundle', `--arch=${archLabel}`, repoDir, input.target, settings.appId, settings.flatpak.branch], input, 'flatpak not found. Install it via your distro: see https://flathub.org/setup.');
|
|
128
|
+
}
|
|
129
|
+
async function run(cmd, args, input, notFoundHint) {
|
|
130
|
+
if (input.verbose)
|
|
131
|
+
console.log(`[gjsify ship] ${cmd} ${args.join(' ')}`);
|
|
132
|
+
// `completion: 'return'` because `ship` packs the remaining formats and
|
|
133
|
+
// prints the artifact list afterwards, so it cannot end in `process.exit()`
|
|
134
|
+
// the way `gjsify flatpak build` does. Under GJS that selects the blocking
|
|
135
|
+
// path, which captures the child's output and re-emits it at the end
|
|
136
|
+
// instead of streaming it — the documented cost of a spawn a caller has to
|
|
137
|
+
// return from (see utils/spawn.ts), and the reason `--verbose` prints the
|
|
138
|
+
// invocation before the silence starts.
|
|
139
|
+
const result = await spawnToCompletion(cmd, args, {
|
|
140
|
+
completion: 'return',
|
|
141
|
+
cwd: input.workDir,
|
|
142
|
+
stdio: 'inherit',
|
|
143
|
+
notFound: () => new Error(`gjsify ship: ${notFoundHint}`),
|
|
144
|
+
});
|
|
145
|
+
if (result.code !== 0) {
|
|
146
|
+
throw new Error(`gjsify ship: ${cmd} failed with ${describeExit(result)}.`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -1,5 +1,118 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type Layout } from './layout.js';
|
|
2
|
+
import type { FormatDescriptor, FormatId, HostOs, PackSettings, RequiredTools } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The directory `windows-dir` produces, and the top level `windows-dir-zip` puts
|
|
5
|
+
* its entries under.
|
|
6
|
+
*
|
|
7
|
+
* ONE function with TWO readers, deliberately: the zip has to expand to the same
|
|
8
|
+
* directory the other row writes, and two literals would be two names a user is
|
|
9
|
+
* asked to believe are one.
|
|
10
|
+
*/
|
|
11
|
+
export declare function windowsProgramDirName(settings: PackSettings): string;
|
|
12
|
+
/**
|
|
13
|
+
* The tools ONE host needs for a format, whichever shape the row declares.
|
|
14
|
+
*
|
|
15
|
+
* The one place the union in {@link RequiredTools} is opened, so a caller cannot
|
|
16
|
+
* read the map and get `undefined.filter` or read the array on a row that has a
|
|
17
|
+
* map. An OS the map does not name answers `[]` — not because nothing is needed
|
|
18
|
+
* there, but because {@link assertHostCanFinish} has already refused that host by
|
|
19
|
+
* the time this is asked, and inventing a tool list for a host that cannot finish
|
|
20
|
+
* the format would answer a question nobody may ask.
|
|
21
|
+
*/
|
|
22
|
+
export declare function requiredToolsOn(tools: RequiredTools, host: string): readonly string[];
|
|
23
|
+
/**
|
|
24
|
+
* Every tool any host could need for a format — the question `installHint`'s
|
|
25
|
+
* presence is gated on.
|
|
26
|
+
*
|
|
27
|
+
* Deliberately NOT what {@link assertToolsInstalled} checks. "Does this row need a
|
|
28
|
+
* hint at all" is a property of the ROW; "is this tool here" is a question about
|
|
29
|
+
* one machine, and answering the first with the second would let a row declare a
|
|
30
|
+
* Windows-only tool and no hint, because the gate happened to run on Linux.
|
|
31
|
+
*/
|
|
32
|
+
export declare function allRequiredTools(tools: RequiredTools): readonly string[];
|
|
2
33
|
export declare const FORMATS: Record<FormatId, FormatDescriptor>;
|
|
3
34
|
export declare const FORMAT_IDS: FormatId[];
|
|
4
|
-
/**
|
|
5
|
-
|
|
35
|
+
/**
|
|
36
|
+
* What `gjsify ship <os>` builds when nothing said otherwise — every format that
|
|
37
|
+
* wraps THAT OS's layout and needs no tool and no particular host.
|
|
38
|
+
*
|
|
39
|
+
* A SECOND derivation from the same table, and it has to be one: `FORMAT_IDS`
|
|
40
|
+
* used to be both "every format that exists" and "every format to build by
|
|
41
|
+
* default", which was the same list only while every format was `finishOn:
|
|
42
|
+
* 'any'`. Adding Flatpak to `FORMAT_IDS` alone would have made a bare
|
|
43
|
+
* `gjsify ship` demand `flatpak-builder` of every project that ever packaged a
|
|
44
|
+
* `.deb` — and on a host without it, of every `release-cut.yml` run, which
|
|
45
|
+
* packs `@gjsify/cli` itself on a bare ubuntu runner. A host-bound format is
|
|
46
|
+
* opt-in through `--target` or `gjsify.ship.targets`; `--help` still lists all
|
|
47
|
+
* of them.
|
|
48
|
+
*
|
|
49
|
+
* `layoutOs` is the SECOND criterion, and it is what the layout axis needed: a
|
|
50
|
+
* `.app` and its zip and a Windows program directory are all `finishOn: 'any'`,
|
|
51
|
+
* so on the one criterion a bare `gjsify ship` on Linux would start emitting
|
|
52
|
+
* five artifacts (ADR 0024 issue #1354, open question 3). Answered with § A2's
|
|
53
|
+
* positional rather than instead of it — the positional picks the layout, this
|
|
54
|
+
* picks the formats that wrap it, and `gjsify ship` on Linux keeps emitting
|
|
55
|
+
* exactly `deb` and `rpm`.
|
|
56
|
+
*
|
|
57
|
+
* An EMPTY answer is legal and is what darwin and windows give today: their
|
|
58
|
+
* layouts assemble (`--stage`) and no format wraps them yet. `assemble` refuses
|
|
59
|
+
* a pack with nothing to pack rather than exiting 0 having produced no artifact.
|
|
60
|
+
*/
|
|
61
|
+
export declare function defaultFormatIds(layoutOs: HostOs): FormatId[];
|
|
62
|
+
/** Every format that wraps one OS's layout, tool-bound ones included. */
|
|
63
|
+
export declare function formatIdsFor(layoutOs: HostOs): FormatId[];
|
|
64
|
+
/**
|
|
65
|
+
* Refuse a format this host cannot finish, BEFORE anything is built or written.
|
|
66
|
+
*
|
|
67
|
+
* `host` is injected so both branches are unit-testable from any machine — the
|
|
68
|
+
* same reason `resolvePrebuildDirName` and `checkTypeSkew`'s readers are pure.
|
|
69
|
+
* The message names the two-phase split rather than just saying no: the answer
|
|
70
|
+
* to "I am on macOS and want a Flatpak" is `--stage` here and `--from-stage` on
|
|
71
|
+
* a Linux runner, and a refusal that does not say so reads as "unsupported".
|
|
72
|
+
*/
|
|
73
|
+
export declare function assertHostCanFinish(format: FormatDescriptor, host?: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* Refuse a format whose tools are not installed, with the tool named.
|
|
76
|
+
*
|
|
77
|
+
* Separate from {@link assertHostCanFinish} because the two failures have
|
|
78
|
+
* different fixes — one needs a different machine, the other needs a package —
|
|
79
|
+
* and separate from the packer's own `notFound` handler because it fires BEFORE
|
|
80
|
+
* a build: `gjsify ship` runs the project's `build` script first, and finding
|
|
81
|
+
* out afterwards that `flatpak-builder` is absent costs the whole build.
|
|
82
|
+
*/
|
|
83
|
+
export declare function assertToolsInstalled(format: FormatDescriptor, present?: (cmd: string) => boolean, host?: string): void;
|
|
84
|
+
/**
|
|
85
|
+
* Parse an explicit `--target deb,rpm` into a sorted, deduplicated descriptor list.
|
|
86
|
+
*
|
|
87
|
+
* A format belonging to another layout is an ERROR here, not a filter:
|
|
88
|
+
* `gjsify ship darwin --target deb` is a mistake worth naming, and dropping it
|
|
89
|
+
* silently would stage the darwin layout, pack nothing and exit 0 — the shape
|
|
90
|
+
* the empty-list refusal above already exists to prevent, arriving through a
|
|
91
|
+
* different door.
|
|
92
|
+
*
|
|
93
|
+
* The FLAG is what makes that the right answer, and it is the whole difference
|
|
94
|
+
* from {@link configuredFormats}: a value typed on this command line is a claim
|
|
95
|
+
* about THIS run.
|
|
96
|
+
*/
|
|
97
|
+
export declare function resolveFormats(raw: readonly string[], layout: Layout): FormatDescriptor[];
|
|
98
|
+
/**
|
|
99
|
+
* The same names from `gjsify.ship.targets`, FILTERED to the current layout.
|
|
100
|
+
*
|
|
101
|
+
* A configured list is a project-level DEFAULT — "when I ship this, build these"
|
|
102
|
+
* — written once and read by every run, so it cannot be a claim about a layout
|
|
103
|
+
* the author had not heard of when they wrote it. Refusing it the way
|
|
104
|
+
* {@link resolveFormats} refuses a flag makes the new positional unusable in
|
|
105
|
+
* every project that has the key, and this repository is the proof: with
|
|
106
|
+
* `targets: ["deb", "rpm"]` in `packages/infra/cli/package.json`,
|
|
107
|
+
* `gjsify ship darwin --stage` exited 1 telling the author to run
|
|
108
|
+
* `gjsify ship darwin --stage`. There was no `--target` value that got a darwin
|
|
109
|
+
* stage out of such a project at all.
|
|
110
|
+
*
|
|
111
|
+
* Filtering is safe HERE and only here, because an empty result is not a silent
|
|
112
|
+
* success: `assemble` refuses a PACK with nothing to pack, and a `--stage` run
|
|
113
|
+
* legitimately wants exactly this — assemble the layout, wrap nothing.
|
|
114
|
+
*/
|
|
115
|
+
export declare function configuredFormats(raw: readonly string[], layout: Layout): {
|
|
116
|
+
formats: FormatDescriptor[];
|
|
117
|
+
dropped: FormatId[];
|
|
118
|
+
};
|