wyvrnpm 2.10.2 → 2.12.2
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/README.md +1914 -1860
- package/bin/{wyvrn.js → wyvrnpm.js} +66 -0
- package/cmake/cpp.cmake +9 -9
- package/cmake/functions.cmake +224 -224
- package/cmake/macros.cmake +284 -284
- package/package.json +3 -2
- package/src/auth.js +66 -66
- package/src/binary-dir.js +95 -0
- package/src/bootstrap/cookbook.js +196 -196
- package/src/bootstrap/detect.js +150 -150
- package/src/bootstrap/index.js +220 -220
- package/src/bootstrap/version.js +72 -72
- package/src/build/cache.js +344 -344
- package/src/build/clone.js +170 -170
- package/src/build/cmake.js +342 -342
- package/src/build/index.js +299 -297
- package/src/build/msvc-env.js +260 -260
- package/src/build/recipe.js +188 -188
- package/src/commands/add.js +141 -141
- package/src/commands/bootstrap.js +96 -96
- package/src/commands/build.js +482 -452
- package/src/commands/cache.js +189 -189
- package/src/commands/clean.js +80 -80
- package/src/commands/configure.js +92 -92
- package/src/commands/init.js +70 -70
- package/src/commands/install-skill.js +115 -115
- package/src/commands/install.js +730 -674
- package/src/commands/link.js +320 -320
- package/src/commands/profile.js +237 -237
- package/src/commands/publish.js +584 -555
- package/src/commands/show.js +252 -252
- package/src/commands/version.js +187 -0
- package/src/compat.js +273 -273
- package/src/conf/index.js +415 -391
- package/src/conf/namespaces.js +94 -94
- package/src/context.js +230 -230
- package/src/http-fetch.js +53 -53
- package/src/ignore.js +118 -118
- package/src/logger.js +122 -122
- package/src/options.js +303 -303
- package/src/settings-overrides.js +152 -152
- package/src/toolchain/deps.js +164 -164
- package/src/toolchain/index.js +212 -212
- package/src/toolchain/presets.js +356 -340
- package/src/toolchain/template.cmake +77 -77
- package/src/upload-built.js +265 -265
- package/src/version-range.js +301 -301
- package/src/zip-safe.js +52 -52
- package/src/zip-stream.js +126 -0
package/src/commands/publish.js
CHANGED
|
@@ -1,555 +1,584 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const os = require('os');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const { getProvider } = require('../providers');
|
|
9
|
-
const {
|
|
10
|
-
readRecipeConf,
|
|
11
|
-
mergeConfLayers,
|
|
12
|
-
unflattenConf,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const {
|
|
16
|
-
const {
|
|
17
|
-
const {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (n
|
|
35
|
-
if (n < 1024
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
* `
|
|
82
|
-
*
|
|
83
|
-
* install
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
* `
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// ---------------------------------------------------------------------------
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
let
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// ---------------------------------------------------------------------------
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
`
|
|
272
|
-
|
|
273
|
-
`
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
`
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
//
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
log.warn(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
//
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
log.
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
`
|
|
338
|
-
`
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
// the
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
...
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
log.info(
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
//
|
|
399
|
-
// the
|
|
400
|
-
//
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
|
|
405
|
-
let
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
//
|
|
454
|
-
//
|
|
455
|
-
// zip
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const { StreamingZipWriter } = require('../zip-stream');
|
|
8
|
+
const { getProvider } = require('../providers');
|
|
9
|
+
const {
|
|
10
|
+
readRecipeConf,
|
|
11
|
+
mergeConfLayers,
|
|
12
|
+
unflattenConf,
|
|
13
|
+
readLocalOverlay,
|
|
14
|
+
} = require('../conf');
|
|
15
|
+
const { resolveBinaryDirRoot } = require('../binary-dir');
|
|
16
|
+
const { hashProfile, sha256Of, getGitSha, getGitRepo, isShaOnRemote } = require('../profile');
|
|
17
|
+
const { defaultCompatBlock } = require('../compat');
|
|
18
|
+
const { buildContext } = require('../context');
|
|
19
|
+
const {
|
|
20
|
+
normalizeOptionsDeclaration,
|
|
21
|
+
resolveEffectiveOptions,
|
|
22
|
+
} = require('../options');
|
|
23
|
+
const { loadIgnorePatterns, isIgnored } = require('../ignore');
|
|
24
|
+
const { hasBuildRecipe, normalizeRecipe } = require('../build/recipe');
|
|
25
|
+
const log = require('../logger');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Human-friendly byte formatter for the dry-run summary.
|
|
29
|
+
*
|
|
30
|
+
* @param {number} n
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
function formatBytes(n) {
|
|
34
|
+
if (!Number.isFinite(n) || n < 0) return `${n}`;
|
|
35
|
+
if (n < 1024) return `${n} B`;
|
|
36
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
37
|
+
if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(2)} MB`;
|
|
38
|
+
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Compute the registry URLs/paths that `v2Publish` would write to. Pure —
|
|
43
|
+
* no network, no filesystem. Mirrors the keys produced by S3/HTTP/File
|
|
44
|
+
* providers (§4.5 of CLAUDE.md), so dry-run can surface the exact shape
|
|
45
|
+
* of the upload without actually performing it.
|
|
46
|
+
*
|
|
47
|
+
* @param {{
|
|
48
|
+
* source: string,
|
|
49
|
+
* name: string,
|
|
50
|
+
* version: string,
|
|
51
|
+
* profileHash: string,
|
|
52
|
+
* gitSha?: string | null,
|
|
53
|
+
* gitRepo?: string | null,
|
|
54
|
+
* updateLatest?: boolean,
|
|
55
|
+
* }} opts
|
|
56
|
+
* @returns {Array<{ path: string, kind: 'wyvrn.zip'|'wyvrn.json'|'source.json'|'versions.json'|'latest.json' }>}
|
|
57
|
+
*/
|
|
58
|
+
function planPublishUrls({
|
|
59
|
+
source, name, version, profileHash,
|
|
60
|
+
gitSha = null, gitRepo = null, updateLatest = true,
|
|
61
|
+
}) {
|
|
62
|
+
const base = source.replace(/\/$/, '');
|
|
63
|
+
const v2root = `${base}/v2/${name}`;
|
|
64
|
+
const buildRoot = `${v2root}/${version}/${profileHash}`;
|
|
65
|
+
|
|
66
|
+
const plan = [
|
|
67
|
+
{ path: `${buildRoot}/wyvrn.zip`, kind: 'wyvrn.zip' },
|
|
68
|
+
{ path: `${buildRoot}/wyvrn.json`, kind: 'wyvrn.json' },
|
|
69
|
+
];
|
|
70
|
+
if (gitSha || gitRepo) {
|
|
71
|
+
plan.push({ path: `${v2root}/${version}/source.json`, kind: 'source.json' });
|
|
72
|
+
}
|
|
73
|
+
plan.push({ path: `${v2root}/versions.json`, kind: 'versions.json' });
|
|
74
|
+
if (updateLatest) {
|
|
75
|
+
plan.push({ path: `${v2root}/latest.json`, kind: 'latest.json' });
|
|
76
|
+
}
|
|
77
|
+
return plan;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Walk `dir` recursively and add every file to `zip`, skipping anything that
|
|
82
|
+
* matches `.wyvrnignore` patterns OR whose zip-relative path is already in
|
|
83
|
+
* `reservedRelPaths`. The reserved set lets the install-tree overlay win
|
|
84
|
+
* on path collisions (e.g. `include/foo.h` shipped by both source and
|
|
85
|
+
* install trees).
|
|
86
|
+
*/
|
|
87
|
+
function addFolderFiltered(zip, dir, base, patterns, reservedRelPaths = new Set()) {
|
|
88
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
89
|
+
const fullPath = path.join(dir, entry);
|
|
90
|
+
const relPath = path.relative(base, fullPath).replace(/\\/g, '/');
|
|
91
|
+
const stat = fs.statSync(fullPath);
|
|
92
|
+
const isDir = stat.isDirectory();
|
|
93
|
+
if (isIgnored(relPath, isDir, patterns)) {
|
|
94
|
+
log.info(` Ignoring: ${relPath}${isDir ? '/' : ''}`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (isDir) {
|
|
98
|
+
addFolderFiltered(zip, fullPath, base, patterns, reservedRelPaths);
|
|
99
|
+
} else if (reservedRelPaths.has(relPath)) {
|
|
100
|
+
// Install tree has a canonical version of this file; let that win.
|
|
101
|
+
log.info(` Install-tree wins: ${relPath}`);
|
|
102
|
+
} else {
|
|
103
|
+
const zipDir = path.dirname(relPath).replace(/\\/g, '/');
|
|
104
|
+
zip.addLocalFile(fullPath, zipDir === '.' ? '' : zipDir);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Collect every file under `dir` as a `{ fullPath, relPath }` pair. `relPath`
|
|
111
|
+
* is forward-slash-separated and relative to `dir` — so the install tree's
|
|
112
|
+
* `install/cmake/FooConfig.cmake` lands as `cmake/FooConfig.cmake` at the
|
|
113
|
+
* zip root, which is where `find_package(Foo CONFIG)` expects it on the
|
|
114
|
+
* consumer side after extraction into `wyvrn_internal/<name>/`.
|
|
115
|
+
*/
|
|
116
|
+
function collectInstallTreeFiles(dir) {
|
|
117
|
+
const files = [];
|
|
118
|
+
function walk(current) {
|
|
119
|
+
for (const entry of fs.readdirSync(current)) {
|
|
120
|
+
const full = path.join(current, entry);
|
|
121
|
+
const stat = fs.statSync(full);
|
|
122
|
+
if (stat.isDirectory()) {
|
|
123
|
+
walk(full);
|
|
124
|
+
} else {
|
|
125
|
+
const rel = path.relative(dir, full).replace(/\\/g, '/');
|
|
126
|
+
files.push({ fullPath: full, relPath: rel });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
walk(dir);
|
|
131
|
+
return files;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function addInstallTree(zip, installDir) {
|
|
135
|
+
const files = collectInstallTreeFiles(installDir);
|
|
136
|
+
const added = new Set();
|
|
137
|
+
for (const { fullPath, relPath } of files) {
|
|
138
|
+
const zipDir = path.dirname(relPath).replace(/\\/g, '/');
|
|
139
|
+
zip.addLocalFile(fullPath, zipDir === '.' ? '' : zipDir);
|
|
140
|
+
added.add(relPath);
|
|
141
|
+
}
|
|
142
|
+
return added;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// Source resolution
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
function resolveSource(argv, ctx) {
|
|
150
|
+
let { source, awsProfile } = argv;
|
|
151
|
+
const { config } = ctx;
|
|
152
|
+
let sourceEntry = null;
|
|
153
|
+
|
|
154
|
+
if (source) {
|
|
155
|
+
const named = config.publishSources.find((s) => s.name === source);
|
|
156
|
+
if (named) {
|
|
157
|
+
log.info(`Using publish source "${named.name}" from config`);
|
|
158
|
+
source = named.url;
|
|
159
|
+
awsProfile = awsProfile ?? named.profile;
|
|
160
|
+
sourceEntry = named;
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
if (config.publishSources.length === 0) {
|
|
164
|
+
log.error(
|
|
165
|
+
'--source is required (or configure one with: ' +
|
|
166
|
+
'wyvrnpm configure add-source --kind publish ...)',
|
|
167
|
+
);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
const first = config.publishSources[0];
|
|
171
|
+
log.info(`Using publish source "${first.name}" from config`);
|
|
172
|
+
source = first.url;
|
|
173
|
+
awsProfile = awsProfile ?? first.profile;
|
|
174
|
+
sourceEntry = first;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Auth flows through ctx.auth.for — same literal > --token-env >
|
|
178
|
+
// config.token > config.tokenEnv precedence, centralised once in
|
|
179
|
+
// buildContext.
|
|
180
|
+
const { token } = ctx.auth.for(sourceEntry);
|
|
181
|
+
|
|
182
|
+
return { source, awsProfile, token };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// Main publish command
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
async function publish(argv) {
|
|
190
|
+
// Shared per-invocation state (config, profile, manifest lazy, auth,
|
|
191
|
+
// CLI options/conf parsing, JSON-mode toggle). See src/context.js +
|
|
192
|
+
// claude/PLAN-COMMAND-CONTEXT.md. Also throws on malformed --option /
|
|
193
|
+
// --conf so bad inputs surface before we hit the registry.
|
|
194
|
+
let ctx;
|
|
195
|
+
try {
|
|
196
|
+
ctx = buildContext(argv);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
log.error(err.message);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const { source, awsProfile, token } = resolveSource(argv, ctx);
|
|
203
|
+
const { path: publishPath, force } = argv;
|
|
204
|
+
const dryRun = Boolean(argv.dryRun);
|
|
205
|
+
const jsonOut = ctx.flags.format === 'json';
|
|
206
|
+
if (dryRun) {
|
|
207
|
+
log.info('DRY RUN — no upload will occur. Previewing publish plan only.');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ── Load manifest ─────────────────────────────────────────────────────────
|
|
211
|
+
// ctx.manifest is a lazy getter — throws on first access if the file is
|
|
212
|
+
// missing or malformed, which is exactly what we want here.
|
|
213
|
+
const { manifestPath } = ctx;
|
|
214
|
+
let manifest;
|
|
215
|
+
try {
|
|
216
|
+
manifest = ctx.manifest;
|
|
217
|
+
} catch (err) {
|
|
218
|
+
log.error(err.message);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const { name, version } = manifest;
|
|
223
|
+
if (!name || !version) {
|
|
224
|
+
log.error('Manifest must have "name" and "version" fields');
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const srcDir = path.resolve(publishPath || '.');
|
|
229
|
+
if (!fs.existsSync(srcDir)) {
|
|
230
|
+
log.error(`Publish path does not exist: ${srcDir}`);
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ── Resolve provider ──────────────────────────────────────────────────────
|
|
235
|
+
let provider;
|
|
236
|
+
try {
|
|
237
|
+
provider = getProvider(source);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
log.error(err.message);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ── Load build profile ────────────────────────────────────────────────────
|
|
244
|
+
// Resolved once in buildContext(). argv.profile → config.defaultProfile
|
|
245
|
+
// → "default" fallback is applied there. `--profile` to publish still
|
|
246
|
+
// works identically — it just flows through ctx.
|
|
247
|
+
const {
|
|
248
|
+
name: activeProfileName,
|
|
249
|
+
profile: buildProfile,
|
|
250
|
+
fromFile,
|
|
251
|
+
} = ctx.profiles.host;
|
|
252
|
+
|
|
253
|
+
// ── Resolve this package's own declared options ─────────────────────────
|
|
254
|
+
// Publishing needs to commit to a specific value for every option the
|
|
255
|
+
// recipe declares. Sources for overrides: CLI `-o <pkg>:...` flags
|
|
256
|
+
// (scoped to the package being published), else the recipe's defaults.
|
|
257
|
+
const ownDeclaration = manifest.options
|
|
258
|
+
? normalizeOptionsDeclaration(manifest.options, `${name}@${version}`)
|
|
259
|
+
: null;
|
|
260
|
+
const cliOptionsByPkg = ctx.cliOptions;
|
|
261
|
+
const effectiveOptions = resolveEffectiveOptions(
|
|
262
|
+
ownDeclaration,
|
|
263
|
+
{}, // consumer-side overrides don't apply to self-publish
|
|
264
|
+
cliOptionsByPkg[name] ?? {},
|
|
265
|
+
`${name}@${version}`,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const profileHash = hashProfile(buildProfile, effectiveOptions);
|
|
269
|
+
|
|
270
|
+
log.info(
|
|
271
|
+
`Publishing ${name}@${version}\n` +
|
|
272
|
+
`Profile : "${activeProfileName}"${fromFile ? '' : ' (auto-detected — save with `wyvrnpm configure profile detect`)'}\n` +
|
|
273
|
+
` ${buildProfile.os}/${buildProfile.arch} ` +
|
|
274
|
+
`${buildProfile.compiler} ${buildProfile['compiler.version']} ` +
|
|
275
|
+
`C++${buildProfile['compiler.cppstd']}` +
|
|
276
|
+
(buildProfile['compiler.runtime'] ? ` [${buildProfile['compiler.runtime']}]` : '') + '\n' +
|
|
277
|
+
(effectiveOptions ? `Options : ${JSON.stringify(effectiveOptions)}\n` : '') +
|
|
278
|
+
`Profile hash: ${profileHash}\n` +
|
|
279
|
+
`Provider : ${provider.constructor.providerName}`,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
// ── Check v2 existence ────────────────────────────────────────────────────
|
|
283
|
+
// Dry-run softens the overwrite-without-force error to a warning so the
|
|
284
|
+
// full plan still prints — CI callers should parse `--format json` and
|
|
285
|
+
// gate on `wouldOverwrite` rather than on the exit code.
|
|
286
|
+
const v2AlreadyExists = await provider.v2Exists({ source, name, version, profileHash, awsProfile, token });
|
|
287
|
+
if (v2AlreadyExists) {
|
|
288
|
+
if (force) {
|
|
289
|
+
log.warn(`${name}@${version} [${profileHash}] already exists — overwriting (--force)`);
|
|
290
|
+
} else if (dryRun) {
|
|
291
|
+
log.warn(
|
|
292
|
+
`${name}@${version} [${profileHash}] already exists on the registry. ` +
|
|
293
|
+
`A real publish would refuse without --force.`,
|
|
294
|
+
);
|
|
295
|
+
} else {
|
|
296
|
+
log.error(
|
|
297
|
+
`${name}@${version} [${profileHash}] already exists.\n` +
|
|
298
|
+
' Bump the version, use a different profile, or pass --force to overwrite.',
|
|
299
|
+
);
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ── Read the author's lockfile snapshot for `publishedLock` audit ────────
|
|
305
|
+
// Dependencies in wyvrn.json are published verbatim (ranges stay as ranges).
|
|
306
|
+
// The exact versions from wyvrn.lock are attached as `publishedLock` metadata
|
|
307
|
+
// on the uploaded manifest so future forensics can tell what the author
|
|
308
|
+
// actually tested against — without constraining consumer resolution.
|
|
309
|
+
const lockPath = path.join(path.dirname(manifestPath), 'wyvrn.lock');
|
|
310
|
+
const publishedLock = {};
|
|
311
|
+
if (fs.existsSync(lockPath)) {
|
|
312
|
+
try {
|
|
313
|
+
const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
|
314
|
+
for (const [pkgName, entry] of Object.entries(lock.packages ?? {})) {
|
|
315
|
+
const ver = typeof entry === 'string' ? entry : entry.version;
|
|
316
|
+
if (ver) publishedLock[pkgName] = ver;
|
|
317
|
+
}
|
|
318
|
+
log.info(`Lock snapshot: ${Object.keys(publishedLock).length} package(s) recorded in publishedLock`);
|
|
319
|
+
} catch {
|
|
320
|
+
log.warn('could not read wyvrn.lock — publishedLock audit block will be empty');
|
|
321
|
+
}
|
|
322
|
+
} else {
|
|
323
|
+
log.warn('no wyvrn.lock found — run `wyvrnpm install` before publishing so publishedLock records tested versions');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ── Git metadata ──────────────────────────────────────────────────────────
|
|
327
|
+
const gitSha = getGitSha(srcDir);
|
|
328
|
+
const gitRepo = getGitRepo(srcDir);
|
|
329
|
+
if (gitSha) log.info(`Git commit : ${gitSha}`);
|
|
330
|
+
if (gitRepo) log.info(`Git remote : ${gitRepo}`);
|
|
331
|
+
|
|
332
|
+
// Warn (but don't block) if HEAD isn't reachable from origin. Downstream
|
|
333
|
+
// `--build=missing` consumers will fail to fetch this commit — the fix
|
|
334
|
+
// is almost always `git push` before publishing.
|
|
335
|
+
if (gitSha && gitRepo && !isShaOnRemote(gitSha, srcDir)) {
|
|
336
|
+
log.warn(
|
|
337
|
+
`commit ${gitSha.slice(0, 12)} is not reachable from any remote ` +
|
|
338
|
+
`branch or tag on origin.\n` +
|
|
339
|
+
` Consumers building this package from source will fail to retrieve it.\n` +
|
|
340
|
+
` Fix: push your branch (or tag the commit) before publishing.`,
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ── .wyvrnignore ──────────────────────────────────────────────────────────
|
|
345
|
+
const ignoreFile = path.join(srcDir, '.wyvrnignore');
|
|
346
|
+
const ignorePatterns = loadIgnorePatterns(ignoreFile);
|
|
347
|
+
if (ignorePatterns.length > 0) {
|
|
348
|
+
log.info(`Loaded ${ignorePatterns.length} pattern(s) from .wyvrnignore`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ── Build zip ─────────────────────────────────────────────────────────────
|
|
352
|
+
const tmpZipPath = path.join(os.tmpdir(), `wyvrnpm-${name}-${version}-${Date.now()}.zip`);
|
|
353
|
+
|
|
354
|
+
// ── Inject default `compatibility` block if the source manifest lacks one ──
|
|
355
|
+
// Written to a temp file so the source wyvrn.json is never modified.
|
|
356
|
+
// A published manifest must always carry a compat block so install-time
|
|
357
|
+
// resolution has something to evaluate (default = all fields `exact`,
|
|
358
|
+
// identical semantics to pre-phase-2 strict behaviour).
|
|
359
|
+
const tmpManifestPath = path.join(
|
|
360
|
+
os.tmpdir(),
|
|
361
|
+
`wyvrnpm-manifest-${name}-${version}-${Date.now()}.json`,
|
|
362
|
+
);
|
|
363
|
+
// ── publishedConf (PLAN-CONF.md §4.6) ────────────────────────────────────
|
|
364
|
+
// Publish reads ONLY the recipe conf + CLI --conf — wyvrn.local.json is
|
|
365
|
+
// intentionally ignored (plan §4.5 / §8.4) so dev-local overlays never
|
|
366
|
+
// leak into shared artefacts. Recorded on the published manifest as
|
|
367
|
+
// informational metadata: it tells maintainers what the publisher's
|
|
368
|
+
// CMake cache vars were at build time but does NOT drive resolution on
|
|
369
|
+
// the consumer side.
|
|
370
|
+
let publishedConf = {};
|
|
371
|
+
try {
|
|
372
|
+
const recipeConfFlat = readRecipeConf(manifest);
|
|
373
|
+
// ctx.cliConf is pre-parsed by buildContext. Malformed --conf would
|
|
374
|
+
// have thrown up there before we reached this block.
|
|
375
|
+
publishedConf = mergeConfLayers(recipeConfFlat, {}, ctx.cliConf);
|
|
376
|
+
} catch (err) {
|
|
377
|
+
log.error(`conf resolution failed: ${err.message}`);
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
if (Object.keys(publishedConf).length > 0) {
|
|
381
|
+
log.info(`Published conf: ${JSON.stringify(publishedConf)}`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const manifestForPublish = {
|
|
385
|
+
...manifest,
|
|
386
|
+
compatibility: manifest.compatibility ?? defaultCompatBlock(),
|
|
387
|
+
...(Object.keys(publishedConf).length > 0
|
|
388
|
+
? { publishedConf: unflattenConf(publishedConf) }
|
|
389
|
+
: {}),
|
|
390
|
+
};
|
|
391
|
+
fs.writeFileSync(tmpManifestPath, JSON.stringify(manifestForPublish, null, 2), 'utf8');
|
|
392
|
+
if (!manifest.compatibility) {
|
|
393
|
+
log.info('Injecting default `compatibility` block (all fields `exact`) — override by adding one to wyvrn.json');
|
|
394
|
+
} else {
|
|
395
|
+
log.info(`Using package-declared compatibility: ${JSON.stringify(manifest.compatibility)}`);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// ── Resolve binaryDirRoot for install-tree lookup ────────────────────────
|
|
399
|
+
// Publish reads the overlay only to LOCATE the install tree on disk —
|
|
400
|
+
// not to fold any of its `conf` data into the published artefact (that
|
|
401
|
+
// remains explicitly excluded per CLAUDE.md §4.7 "Publish reads recipe
|
|
402
|
+
// + CLI only"). The build-dir path is a local filesystem detail, not
|
|
403
|
+
// ABI/config that ships with the zip, so reading `binaryDirRoot` here
|
|
404
|
+
// doesn't leak dev-machine state into the registry.
|
|
405
|
+
let publishBinaryDirRoot;
|
|
406
|
+
try {
|
|
407
|
+
const overlay = readLocalOverlay(srcDir);
|
|
408
|
+
publishBinaryDirRoot = resolveBinaryDirRoot({
|
|
409
|
+
cliBuildDir: argv.buildDir,
|
|
410
|
+
localOverlayBinaryDirRoot: overlay.binaryDirRoot,
|
|
411
|
+
});
|
|
412
|
+
} catch (err) {
|
|
413
|
+
log.error(err.message);
|
|
414
|
+
process.exit(1);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ── Install-tree overlay ──────────────────────────────────────────────────
|
|
418
|
+
// Publish zips the source tree (filtered by .wyvrnignore), but consumers
|
|
419
|
+
// actually need the install tree — `lib/cmake/<Name>/<Name>Config.cmake`,
|
|
420
|
+
// the installed headers, compiled libs — so find_package() works after
|
|
421
|
+
// extraction. When a build recipe is declared, locate the per-profile
|
|
422
|
+
// install dir (populated by `wyvrnpm build --config <cfg> --install`) and
|
|
423
|
+
// overlay its contents on top of the source zip. Install-tree files win
|
|
424
|
+
// on path collisions (e.g. `include/`).
|
|
425
|
+
let installOverlayDir = null;
|
|
426
|
+
let installOverlayPaths = new Set();
|
|
427
|
+
if (hasBuildRecipe(manifest)) {
|
|
428
|
+
try {
|
|
429
|
+
const recipe = normalizeRecipe(manifest.build, effectiveOptions, manifest);
|
|
430
|
+
const candidate = path.join(
|
|
431
|
+
srcDir, publishBinaryDirRoot, `wyvrn-${activeProfileName}`, recipe.installDir,
|
|
432
|
+
);
|
|
433
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
434
|
+
installOverlayDir = candidate;
|
|
435
|
+
} else {
|
|
436
|
+
log.warn(
|
|
437
|
+
`build recipe is declared but no install tree found at ${candidate}.\n` +
|
|
438
|
+
' Consumers\' find_package() will likely fail. Fix: run\n' +
|
|
439
|
+
' wyvrnpm build --config Debug --install\n' +
|
|
440
|
+
' wyvrnpm build --config Release --install\n' +
|
|
441
|
+
' wyvrnpm build --config RelWithDebInfo --install\n' +
|
|
442
|
+
' wyvrnpm build --config MinSizeRel --install\n' +
|
|
443
|
+
' then re-run publish.',
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
} catch (err) {
|
|
447
|
+
log.warn(`could not locate install tree: ${err.message} — publishing source tree only`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
try {
|
|
452
|
+
log.info(`Zipping ${srcDir} ...`);
|
|
453
|
+
// StreamingZipWriter — replaces adm-zip on the write path. archiver
|
|
454
|
+
// streams each file in via fs.createReadStream rather than the
|
|
455
|
+
// sync-readFileSync adm-zip used, so multi-GB artefacts (gRPC's
|
|
456
|
+
// static libs hit ~2.7 GB at RelWithDebInfo, the bug that prompted
|
|
457
|
+
// the swap) no longer trip Node's ~2-GiB Buffer cap. archiver
|
|
458
|
+
// auto-promotes to ZIP64 when total size or entry count crosses
|
|
459
|
+
// the 4 GB / 65535 thresholds; node-stream-zip on the consumer
|
|
460
|
+
// side reads ZIP64 transparently.
|
|
461
|
+
const zip = new StreamingZipWriter(tmpZipPath);
|
|
462
|
+
if (installOverlayDir) {
|
|
463
|
+
// Collect install-tree paths first so the source-tree walker can skip
|
|
464
|
+
// anything the install tree will add canonically.
|
|
465
|
+
const peek = collectInstallTreeFiles(installOverlayDir);
|
|
466
|
+
installOverlayPaths = new Set(peek.map((f) => f.relPath));
|
|
467
|
+
}
|
|
468
|
+
addFolderFiltered(zip, srcDir, srcDir, ignorePatterns, installOverlayPaths);
|
|
469
|
+
if (installOverlayDir) {
|
|
470
|
+
log.info(`Overlaying install tree: ${installOverlayDir}`);
|
|
471
|
+
const added = addInstallTree(zip, installOverlayDir);
|
|
472
|
+
log.info(` Added ${added.size} file(s) from install tree`);
|
|
473
|
+
}
|
|
474
|
+
await zip.finalize();
|
|
475
|
+
|
|
476
|
+
const contentSha256 = sha256Of(tmpZipPath);
|
|
477
|
+
const zipBytes = fs.statSync(tmpZipPath).size;
|
|
478
|
+
const manifestBytes = fs.statSync(tmpManifestPath).size;
|
|
479
|
+
log.info(`Content SHA256 : ${contentSha256}`);
|
|
480
|
+
log.info(`Zip size : ${formatBytes(zipBytes)} (${zipBytes} bytes)`);
|
|
481
|
+
|
|
482
|
+
// ── Dry run: print the plan, skip the actual upload ──────────────────
|
|
483
|
+
// Mirrors `npm publish --dry-run` — every step above (profile, options,
|
|
484
|
+
// zip, hash, v2Exists) runs; only the provider write is skipped.
|
|
485
|
+
if (dryRun) {
|
|
486
|
+
const plan = planPublishUrls({
|
|
487
|
+
source, name, version, profileHash, gitSha, gitRepo, updateLatest: true,
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
if (!jsonOut) {
|
|
491
|
+
console.log();
|
|
492
|
+
console.log(`Would publish ${name}@${version} [${profileHash}] to ${source}`);
|
|
493
|
+
console.log(` contentSha256 : ${contentSha256}`);
|
|
494
|
+
console.log(` zip size : ${formatBytes(zipBytes)}`);
|
|
495
|
+
console.log(` manifest size : ${formatBytes(manifestBytes)}`);
|
|
496
|
+
if (v2AlreadyExists) {
|
|
497
|
+
console.log(` overwrite : YES — ${force ? '--force set, would proceed' : 'NOT SET, real publish would refuse'}`);
|
|
498
|
+
}
|
|
499
|
+
console.log();
|
|
500
|
+
console.log('Registry writes:');
|
|
501
|
+
for (const { path: p, kind } of plan) {
|
|
502
|
+
console.log(` → ${p} (${kind})`);
|
|
503
|
+
}
|
|
504
|
+
console.log();
|
|
505
|
+
log.success('Dry run OK — nothing uploaded.');
|
|
506
|
+
} else {
|
|
507
|
+
const payload = {
|
|
508
|
+
command: 'publish',
|
|
509
|
+
dryRun: true,
|
|
510
|
+
wyvrnpmVersion: require('../../package.json').version,
|
|
511
|
+
name,
|
|
512
|
+
version,
|
|
513
|
+
profileHash,
|
|
514
|
+
source,
|
|
515
|
+
contentSha256,
|
|
516
|
+
zipBytes,
|
|
517
|
+
manifestBytes,
|
|
518
|
+
options: effectiveOptions ?? null,
|
|
519
|
+
gitSha: gitSha ?? null,
|
|
520
|
+
gitRepo: gitRepo ?? null,
|
|
521
|
+
wouldOverwrite: Boolean(v2AlreadyExists),
|
|
522
|
+
forceSet: Boolean(force),
|
|
523
|
+
publishedLock,
|
|
524
|
+
publishedConf: Object.keys(publishedConf).length > 0 ? unflattenConf(publishedConf) : null,
|
|
525
|
+
plan,
|
|
526
|
+
};
|
|
527
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
528
|
+
}
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// ── v2 publish ────────────────────────────────────────────────────────
|
|
533
|
+
log.info('Publishing (v2) ...');
|
|
534
|
+
await provider.v2Publish(
|
|
535
|
+
{ manifest: tmpManifestPath, zip: tmpZipPath },
|
|
536
|
+
{
|
|
537
|
+
source,
|
|
538
|
+
name,
|
|
539
|
+
version,
|
|
540
|
+
profileHash,
|
|
541
|
+
// buildSettings stored in metadata for debugging — NOT wyvrn.json
|
|
542
|
+
buildSettings: effectiveOptions
|
|
543
|
+
? { ...buildProfile, options: effectiveOptions }
|
|
544
|
+
: buildProfile,
|
|
545
|
+
contentSha256,
|
|
546
|
+
gitSha,
|
|
547
|
+
gitRepo,
|
|
548
|
+
publishedLock,
|
|
549
|
+
awsProfile,
|
|
550
|
+
token,
|
|
551
|
+
},
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
log.success(`Successfully published ${name}@${version} [${profileHash}] to ${source}`);
|
|
555
|
+
|
|
556
|
+
if (jsonOut) {
|
|
557
|
+
const payload = {
|
|
558
|
+
command: 'publish',
|
|
559
|
+
wyvrnpmVersion: require('../../package.json').version,
|
|
560
|
+
name,
|
|
561
|
+
version,
|
|
562
|
+
profileHash,
|
|
563
|
+
source,
|
|
564
|
+
contentSha256,
|
|
565
|
+
options: effectiveOptions ?? null,
|
|
566
|
+
gitSha: gitSha ?? null,
|
|
567
|
+
gitRepo: gitRepo ?? null,
|
|
568
|
+
publishedAt: new Date().toISOString(),
|
|
569
|
+
};
|
|
570
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
571
|
+
}
|
|
572
|
+
} finally {
|
|
573
|
+
if (fs.existsSync(tmpZipPath)) fs.unlinkSync(tmpZipPath);
|
|
574
|
+
if (fs.existsSync(tmpManifestPath)) fs.unlinkSync(tmpManifestPath);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
module.exports = publish;
|
|
579
|
+
// Exported for tests only
|
|
580
|
+
module.exports.addFolderFiltered = addFolderFiltered;
|
|
581
|
+
module.exports.addInstallTree = addInstallTree;
|
|
582
|
+
module.exports.collectInstallTreeFiles = collectInstallTreeFiles;
|
|
583
|
+
module.exports.planPublishUrls = planPublishUrls;
|
|
584
|
+
module.exports.formatBytes = formatBytes;
|