@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.
- package/dist/cli.gjs.mjs +202 -167
- 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 +850 -58
- package/lib/commands/trust.d.ts +6 -0
- package/lib/commands/trust.js +1 -1
- package/lib/commands/upgrade.d.ts +23 -0
- package/lib/commands/upgrade.js +95 -12
- package/lib/templates/install.mjs.tmpl +2 -2
- package/lib/types/config-data.d.ts +127 -1
- package/lib/utils/gi-search-path.js +11 -3
- 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 +50 -0
- package/lib/utils/ship/depends.js +117 -3
- package/lib/utils/ship/discover.js +103 -1
- package/lib/utils/ship/dmg.d.ts +71 -0
- package/lib/utils/ship/dmg.js +181 -0
- package/lib/utils/ship/flatpak.js +1 -1
- package/lib/utils/ship/formats.d.ts +108 -7
- package/lib/utils/ship/formats.js +778 -15
- package/lib/utils/ship/launcher.d.ts +13 -3
- package/lib/utils/ship/launcher.js +419 -17
- package/lib/utils/ship/layout.d.ts +330 -0
- package/lib/utils/ship/layout.js +504 -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 +434 -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 +599 -14
- package/lib/utils/ship/pe-launcher.d.ts +29 -0
- package/lib/utils/ship/pe-launcher.js +663 -0
- package/lib/utils/ship/plan.d.ts +21 -0
- package/lib/utils/ship/plan.js +55 -7
- package/lib/utils/ship/plist.d.ts +98 -0
- package/lib/utils/ship/plist.js +256 -0
- package/lib/utils/ship/rpm.js +16 -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 +91 -2
- package/lib/utils/ship/settings.js +93 -1
- package/lib/utils/ship/share-dirs.d.ts +53 -0
- package/lib/utils/ship/share-dirs.js +76 -0
- package/lib/utils/ship/signing.d.ts +311 -0
- package/lib/utils/ship/signing.js +506 -0
- package/lib/utils/ship/stage-manifest.d.ts +40 -12
- package/lib/utils/ship/stage-manifest.js +99 -13
- package/lib/utils/ship/stage-writer.d.ts +48 -0
- package/lib/utils/ship/stage-writer.js +91 -2
- package/lib/utils/ship/types.d.ts +191 -14
- 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/system-gi.d.ts +20 -5
- package/lib/utils/system-gi.js +36 -13
- package/lib/utils/trust-registry.js +5 -0
- 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>;
|