@gjsify/cli 0.44.0 → 0.46.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.
Files changed (81) hide show
  1. package/dist/cli.gjs.mjs +202 -167
  2. package/lib/commands/gettext.d.ts +2 -1
  3. package/lib/commands/gettext.js +105 -47
  4. package/lib/commands/onboard.d.ts +7 -0
  5. package/lib/commands/onboard.js +349 -96
  6. package/lib/commands/ship.d.ts +3 -0
  7. package/lib/commands/ship.js +850 -58
  8. package/lib/commands/trust.d.ts +6 -0
  9. package/lib/commands/trust.js +1 -1
  10. package/lib/commands/upgrade.d.ts +23 -0
  11. package/lib/commands/upgrade.js +95 -12
  12. package/lib/templates/install.mjs.tmpl +2 -2
  13. package/lib/types/config-data.d.ts +127 -1
  14. package/lib/utils/gi-search-path.js +11 -3
  15. package/lib/utils/msgfmt-merge.d.ts +35 -0
  16. package/lib/utils/msgfmt-merge.js +102 -0
  17. package/lib/utils/npm-otp.d.ts +27 -2
  18. package/lib/utils/npm-otp.js +82 -7
  19. package/lib/utils/onboard-discovery.d.ts +68 -0
  20. package/lib/utils/onboard-discovery.js +178 -0
  21. package/lib/utils/onboard-probe.d.ts +118 -1
  22. package/lib/utils/onboard-probe.js +153 -4
  23. package/lib/utils/prompt-output.d.ts +12 -0
  24. package/lib/utils/prompt-output.js +54 -0
  25. package/lib/utils/prompt.js +22 -2
  26. package/lib/utils/publishable-packages.d.ts +1 -1
  27. package/lib/utils/publishable-packages.js +1 -0
  28. package/lib/utils/resolve-npm-package.d.ts +56 -0
  29. package/lib/utils/resolve-npm-package.js +69 -0
  30. package/lib/utils/ship/app-runtime.d.ts +214 -0
  31. package/lib/utils/ship/app-runtime.js +429 -0
  32. package/lib/utils/ship/depends.d.ts +50 -0
  33. package/lib/utils/ship/depends.js +117 -3
  34. package/lib/utils/ship/discover.js +103 -1
  35. package/lib/utils/ship/dmg.d.ts +71 -0
  36. package/lib/utils/ship/dmg.js +181 -0
  37. package/lib/utils/ship/flatpak.js +1 -1
  38. package/lib/utils/ship/formats.d.ts +108 -7
  39. package/lib/utils/ship/formats.js +778 -15
  40. package/lib/utils/ship/launcher.d.ts +13 -3
  41. package/lib/utils/ship/launcher.js +419 -17
  42. package/lib/utils/ship/layout.d.ts +330 -0
  43. package/lib/utils/ship/layout.js +504 -0
  44. package/lib/utils/ship/localize-metadata.d.ts +22 -0
  45. package/lib/utils/ship/localize-metadata.js +126 -0
  46. package/lib/utils/ship/mime.js +1 -9
  47. package/lib/utils/ship/msi.d.ts +126 -0
  48. package/lib/utils/ship/msi.js +434 -0
  49. package/lib/utils/ship/node-runtime.d.ts +71 -0
  50. package/lib/utils/ship/node-runtime.js +122 -0
  51. package/lib/utils/ship/payload.d.ts +156 -5
  52. package/lib/utils/ship/payload.js +599 -14
  53. package/lib/utils/ship/pe-launcher.d.ts +29 -0
  54. package/lib/utils/ship/pe-launcher.js +663 -0
  55. package/lib/utils/ship/plan.d.ts +21 -0
  56. package/lib/utils/ship/plan.js +55 -7
  57. package/lib/utils/ship/plist.d.ts +98 -0
  58. package/lib/utils/ship/plist.js +256 -0
  59. package/lib/utils/ship/rpm.js +16 -3
  60. package/lib/utils/ship/schemas.d.ts +61 -0
  61. package/lib/utils/ship/schemas.js +118 -0
  62. package/lib/utils/ship/scripts.js +7 -3
  63. package/lib/utils/ship/settings.d.ts +91 -2
  64. package/lib/utils/ship/settings.js +93 -1
  65. package/lib/utils/ship/share-dirs.d.ts +53 -0
  66. package/lib/utils/ship/share-dirs.js +76 -0
  67. package/lib/utils/ship/signing.d.ts +311 -0
  68. package/lib/utils/ship/signing.js +506 -0
  69. package/lib/utils/ship/stage-manifest.d.ts +40 -12
  70. package/lib/utils/ship/stage-manifest.js +99 -13
  71. package/lib/utils/ship/stage-writer.d.ts +48 -0
  72. package/lib/utils/ship/stage-writer.js +91 -2
  73. package/lib/utils/ship/types.d.ts +191 -14
  74. package/lib/utils/ship/xml.d.ts +7 -0
  75. package/lib/utils/ship/xml.js +25 -0
  76. package/lib/utils/ship/zip.d.ts +69 -0
  77. package/lib/utils/ship/zip.js +245 -0
  78. package/lib/utils/system-gi.d.ts +20 -5
  79. package/lib/utils/system-gi.js +36 -13
  80. package/lib/utils/trust-registry.js +5 -0
  81. package/package.json +19 -19
@@ -0,0 +1,311 @@
1
+ import { type PayloadEntry } from './payload.js';
2
+ import type { HostOs } from './types.js';
3
+ /**
4
+ * Everything that is per-OS about making a signature, as data.
5
+ *
6
+ * The same shape argument `FormatDescriptor` makes one directory over: a second
7
+ * OS is a row, not a branch. It is a separate table from `FORMATS` on purpose —
8
+ * § A14 measured that a format declares where it can be PACKED and never what it
9
+ * can be signed with, and darwin has two format rows that must not be able to
10
+ * disagree about their signature.
11
+ */
12
+ export interface SignerDescriptor {
13
+ /** The layout whose payload this signer signs. */
14
+ layoutOs: HostOs;
15
+ /** The command that makes the signature. */
16
+ tool: string;
17
+ /** OSes whose tooling can run {@link tool}. */
18
+ signOn: readonly HostOs[];
19
+ /** Where {@link tool} comes from, in this signer's own words. */
20
+ installHint: string;
21
+ /**
22
+ * Which payload files carry a signature on this OS.
23
+ *
24
+ * `classifyBinary`'s vocabulary, so the predicate is the magic number and
25
+ * not a filename pattern: a darwin closure names its images `.dylib`,
26
+ * `.so`, `.node` and nothing at all (the interpreter), and a suffix list
27
+ * would sign three of those four.
28
+ */
29
+ signs: 'macho' | 'pe';
30
+ /** argv after {@link tool}, for ONE file. */
31
+ args: (input: SignArgs) => readonly string[];
32
+ /**
33
+ * The entitlements this signer asks for, or `[]` where the OS has none.
34
+ *
35
+ * `[]` is an ANSWER and not an omission — the same rule `Layout.metadata`
36
+ * states. Windows has no equivalent concept at all, and a signer that granted
37
+ * nothing on macOS would be a different decision from one that has nothing to
38
+ * grant.
39
+ */
40
+ entitlements: readonly string[];
41
+ /**
42
+ * Does this signer seal the ARTIFACT ROOT after signing the images inside it?
43
+ *
44
+ * Only true where the OS has a bundle format, which is darwin alone. The root
45
+ * itself is not on the descriptor because it belongs to the LAYOUT
46
+ * (`Layout.root`), and a second copy here is where the two would come apart.
47
+ */
48
+ sealsBundle: boolean;
49
+ /**
50
+ * Payload paths a seal is allowed to ADD, given the artifact root.
51
+ *
52
+ * A PREFIX rather than a free hand: `codesign` on a bundle writes
53
+ * `Contents/_CodeSignature/{CodeResources,CodeDirectory,CodeRequirements,
54
+ * CodeRequirements-1,CodeSignature}` and nothing else, so anything appearing
55
+ * outside that directory is something else having run — which is exactly the
56
+ * class {@link signPayload}'s file-set check exists to catch.
57
+ */
58
+ sealAddPrefix: (bundleRoot: string) => string;
59
+ /** The config key a project spells its default identity under. */
60
+ configKey: string;
61
+ /** What a signature from this signer does NOT yet cover — printed once per run. */
62
+ gap: string;
63
+ }
64
+ /**
65
+ * The signers, keyed by the LAYOUT they sign — never by the host they run on.
66
+ *
67
+ * Linux is deliberately absent rather than present-and-empty. A `.deb` and an
68
+ * `.rpm` carry no per-file signature: the artifact is signed as a whole, by the
69
+ * repository that serves it (`debsigs`, `rpmsign`), with a key that belongs to
70
+ * that repository and not to this build. A row here would make `--sign` on Linux
71
+ * look like it did something.
72
+ */
73
+ export declare const SIGNERS: Partial<Record<HostOs, SignerDescriptor>>;
74
+ /** `gjsify.ship.sign` as declared — one entry per {@link HostOs}. */
75
+ export type SignConfig = Partial<Record<HostOs, {
76
+ identity?: string;
77
+ }>>;
78
+ /**
79
+ * What this run will do about signatures.
80
+ *
81
+ * A union rather than an optional string, because the three states need three
82
+ * different words and collapsing them is exactly what § A13 warns about: "no
83
+ * signer for this layout" is silence, "a slot exists and is empty" is a printed
84
+ * skip, and "here is an identity" is work.
85
+ */
86
+ export type SignPlan = {
87
+ kind: 'unsupported';
88
+ } | {
89
+ kind: 'skip';
90
+ message: string;
91
+ } | {
92
+ kind: 'sign';
93
+ identity: string;
94
+ signer: SignerDescriptor;
95
+ source: 'flag' | 'config';
96
+ };
97
+ /**
98
+ * Resolve the identity for this run, or say why there is none.
99
+ *
100
+ * `flag` beats `config` and neither is a yargs `default:` — the uniform
101
+ * precedence `resolveShipSettings` documents. `config` is `undefined` on the
102
+ * `--from-stage` path and that is not an omission: § A14 amends § A1 to *a
103
+ * format declares where it can be packed; the RUN declares what it can sign
104
+ * with*, and the finishing host has no project to read a default out of.
105
+ */
106
+ export declare function resolveSignPlan(input: {
107
+ /** `--sign` as typed. `''` is a value: it is `[ -z "$SIGN" ]` (§ A13). */
108
+ flag?: string;
109
+ /** `gjsify.ship.sign`, when a project is in reach. */
110
+ config?: SignConfig;
111
+ layoutOs: HostOs;
112
+ }): SignPlan;
113
+ /**
114
+ * Refuse a signing run this host cannot make, and say which half is missing.
115
+ *
116
+ * Two checks and not one, for the same reason `assertCanPack` splits them: the
117
+ * wrong OS needs another machine, an absent tool needs a package. Both are
118
+ * knowable before anything is packed.
119
+ */
120
+ export declare function assertHostCanSign(signer: SignerDescriptor): void;
121
+ /**
122
+ * What a signing run did to the payload's FILE SET, split three ways.
123
+ *
124
+ * PURE, and extracted from {@link signPayload} for one reason: it is the whole of
125
+ * the seal decision and the only half of it that can be checked without a macOS
126
+ * host. Everything else about a bundle seal needs `codesign`, which this
127
+ * repository has on exactly one CI leg; this function needs nothing.
128
+ *
129
+ * `sealPrefix` absent means "this signer seals nothing", and then EVERY addition
130
+ * is unexpected — which is what the file did before ADR 0040 and is still the
131
+ * right answer for `signtool`.
132
+ */
133
+ export declare function partitionSignedFileSet(input: {
134
+ /** Paths found under the scratch directory after the tool ran. */
135
+ arrived: readonly string[];
136
+ /** Paths the payload had before it. */
137
+ planned: readonly string[];
138
+ /** Directory prefix the seal is allowed to write into, when there is one. */
139
+ sealPrefix?: string;
140
+ }): {
141
+ sealed: string[];
142
+ unexpected: string[];
143
+ removed: string[];
144
+ };
145
+ /** What one invocation of a signing tool is given. */
146
+ export interface SignArgs {
147
+ identity: string;
148
+ /** Absolute path of the file — or, for a bundle seal, of the bundle DIRECTORY. */
149
+ file: string;
150
+ /** Absolute path of the entitlements plist this run wrote, or absent when the signer grants none. */
151
+ entitlements?: string;
152
+ /** Is {@link identity} the reserved ad-hoc `-`? */
153
+ adhoc: boolean;
154
+ }
155
+ /** `codesign --sign -`: the one identity value that names no identity at all. */
156
+ export declare const ADHOC_IDENTITY = "-";
157
+ export interface SignPayloadInput {
158
+ /** What `readStage` returned — the PRE-sign tree, already held against the manifest. */
159
+ payload: readonly PayloadEntry[];
160
+ identity: string;
161
+ signer: SignerDescriptor;
162
+ /** A scratch directory this function owns and WIPES. Never the arriving stage. */
163
+ workDir: string;
164
+ /**
165
+ * `Layout.root` — the payload-relative directory that IS the artifact, or `''`.
166
+ *
167
+ * Read only by a signer that seals: it is what `codesign` is pointed at, and
168
+ * it comes from the layout rather than from this table because the layout is
169
+ * what decided the `<App>.app` name in the first place.
170
+ */
171
+ bundleRoot: string;
172
+ /**
173
+ * Where to write the entitlements plist, OUTSIDE {@link workDir}.
174
+ *
175
+ * Outside is not a preference: the file-set check below lists `workDir` and
176
+ * compares it with the payload, so an entitlements file written inside would
177
+ * read as a file the signer added.
178
+ */
179
+ entitlementsPath: string;
180
+ /** Print every invocation. */
181
+ verbose: boolean;
182
+ log: (line: string) => void;
183
+ }
184
+ /**
185
+ * Re-sign every signable image in the payload, and hand back the new bytes.
186
+ *
187
+ * The signature is made on FILES because that is the only interface `codesign`
188
+ * and `signtool` have, so the payload is materialised into `workDir`, signed
189
+ * there, and read back. `workDir` is a scratch directory rather than the stage:
190
+ * writing into an arriving stage would make the next `--from-stage` run of the
191
+ * same tree fail `readStage`'s size comparison (measured — see the header), and
192
+ * `writeStage`'s wipe would delete the payload it was about to pack.
193
+ *
194
+ * TWO STEPS ON DARWIN, ONE EVERYWHERE ELSE: every Mach-O in the closure, then the
195
+ * bundle itself. The second is what `codesign --verify <App>.app` reads, and it
196
+ * writes five regular files into `Contents/_CodeSignature/` that the returned
197
+ * payload carries — so the seal reaches the `.zip` and the `.dmg` alike, which is
198
+ * the correction ADR 0040 makes to this file's previous claim that it could not.
199
+ *
200
+ * THE FILE SET MAY ONLY GROW WHERE THE SEAL WRITES, and that is asserted rather
201
+ * than assumed. Both tools rewrite an image in place, so a payload that gained a
202
+ * path outside `sealAddPrefix` — or lost one anywhere — means something else ran,
203
+ * and its files would be packed with no mode from the plan.
204
+ */
205
+ export declare function signPayload(input: SignPayloadInput): Promise<PayloadEntry[]>;
206
+ /**
207
+ * What this run will do about notarisation.
208
+ *
209
+ * A SECOND credential and an unrelated one (§ A15): `--sign` names an identity
210
+ * the signing machine holds, `--notarize` names an account credential. They are
211
+ * two flags because they are two inputs, and the reference keeps them in two
212
+ * scripts.
213
+ */
214
+ export interface NotaryDescriptor {
215
+ layoutOs: HostOs;
216
+ tool: string;
217
+ notarizeOn: readonly HostOs[];
218
+ installHint: string;
219
+ /**
220
+ * argv after {@link tool}.
221
+ *
222
+ * `credential` is threaded straight into the flag the command consumes, and
223
+ * that is the whole design of {@link resolveNotaryPlan}'s guard. § A15
224
+ * measured the trap: `refs/node/tools/osx-notarize.sh` guards on three
225
+ * environment variables it never uses and then submits with a
226
+ * `--keychain-profile` nothing checked, so the skip is reachable from an
227
+ * input the next line does not read. Here the guarded value IS the argument.
228
+ *
229
+ * ⚠️ `--keychain-profile` is the ONLY shape § A15 found evidenced, and the
230
+ * App Store Connect API-key form (`--key`/`--key-id`/`--issuer`) is NOT
231
+ * implemented because nothing measured it. UNVERIFIED, and marked as such:
232
+ * no run in this repository has ever invoked `notarytool` — notarisation
233
+ * needs an Apple account, which ad-hoc signing deliberately does not.
234
+ */
235
+ args: (credential: string, artifact: string) => readonly string[];
236
+ }
237
+ export declare const NOTARIES: Partial<Record<HostOs, NotaryDescriptor>>;
238
+ export type NotaryPlan = {
239
+ kind: 'unsupported';
240
+ } | {
241
+ kind: 'skip';
242
+ message: string;
243
+ } | {
244
+ kind: 'notarize';
245
+ credential: string;
246
+ notary: NotaryDescriptor;
247
+ };
248
+ /**
249
+ * Resolve the notarisation credential, or say why there is none.
250
+ *
251
+ * Deliberately NOT defaultable from the project. An identity is a name a machine
252
+ * may hold for years and is worth writing into a repository; a notarisation
253
+ * credential is a per-run account credential, and a config key for it is an
254
+ * invitation to commit one.
255
+ */
256
+ export declare function resolveNotaryPlan(input: {
257
+ flag?: string;
258
+ layoutOs: HostOs;
259
+ sign: SignPlan;
260
+ }): NotaryPlan;
261
+ /**
262
+ * Submit one written artifact for notarisation.
263
+ *
264
+ * ⚠️ UNVERIFIED END TO END. Everything up to the `spawnToCompletion` is covered
265
+ * — the argv by `signing.spec.ts`, the guard and both refusals by
266
+ * `tests/e2e/ship-signing` — and the invocation itself has never run: it needs
267
+ * an Apple Developer account, and § A17's whole point is that M6's proof does
268
+ * not. Whoever first runs this on a real credential should record what it did
269
+ * beside § A15, which is where the question was left open.
270
+ */
271
+ export declare function notarizeArtifact(input: {
272
+ plan: Extract<NotaryPlan, {
273
+ kind: 'notarize';
274
+ }>;
275
+ /** The artifact to submit. Must be a FILE — notarytool takes an archive or an installer. */
276
+ artifact: string;
277
+ verbose: boolean;
278
+ log: (line: string) => void;
279
+ }): Promise<void>;
280
+ /**
281
+ * Attach the notarisation ticket to an artifact that can hold one.
282
+ *
283
+ * WHY IT IS A SEPARATE CALL AND NOT THE TAIL OF {@link notarizeArtifact}: not
284
+ * every artifact the notary service ACCEPTS can carry a ticket, and the pair that
285
+ * comes apart is exactly the one this command produces. Apple:
286
+ * *"While you can notarize a ZIP archive, you can't staple to it directly.
287
+ * Instead, run stapler against each item that you added to the archive. Then
288
+ * create a new ZIP file containing the stapled items for distribution."*
289
+ * `stapler(1)` gives the accepting side of the same list: *"stapler works only
290
+ * with UDIF disk images, signed \"flat\" installer packages, and certain
291
+ * code-signed executable bundles such as \".app\"."* So the `.dmg` is stapled
292
+ * here and the `.zip` is told, in the artifact log, what a person has to do
293
+ * instead — which is a fact about the CONTAINER and therefore lives on the format
294
+ * (`canCarryTicket`), not in a branch here.
295
+ *
296
+ * ⚠️ UNVERIFIED END TO END, for the same reason `notarizeArtifact` is: stapling
297
+ * needs a real notarisation, which needs an Apple account (ADR 0024 § A15, § A17).
298
+ * The argv is unit-tested and the invocation has never run in this repository.
299
+ *
300
+ * `stapler(1)` also states the constraint that decides the ORDER: *"Stapling does
301
+ * not invalidate the code signature and must be run after an executable or archive
302
+ * has been code-signed and notarized … Code-signing a supported file format
303
+ * invalidates any stapled tickets"* — so nothing may re-sign after this, and
304
+ * nothing in `packOne` does.
305
+ */
306
+ export declare function stapleArtifact(input: {
307
+ notary: NotaryDescriptor;
308
+ artifact: string;
309
+ verbose: boolean;
310
+ log: (line: string) => void;
311
+ }): Promise<void>;