wyvrnpm 2.10.0 → 2.11.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/README.md +50 -0
- package/bin/wyvrn.js +52 -1
- package/claude/skills/wyvrnpm.skill +0 -0
- package/package.json +1 -1
- package/src/commands/publish.js +120 -0
- package/src/commands/version.js +187 -0
package/README.md
CHANGED
|
@@ -235,6 +235,51 @@ wyvrnpm add zlib fmt@10.2.0.0 spdlog
|
|
|
235
235
|
|
|
236
236
|
---
|
|
237
237
|
|
|
238
|
+
### `wyvrnpm version`
|
|
239
|
+
|
|
240
|
+
Read, set, or partially update the top-level `version` field in `wyvrn.json`. Designed for CI/CD — bumps the build component from a pipeline variable, prints the result for capture, and never touches git (CI pipelines own tagging and commits).
|
|
241
|
+
|
|
242
|
+
- **Bare** — prints the current version on stdout, naked (no `[wyvrn]` prefix), so `VER=$(wyvrnpm version)` works straight away.
|
|
243
|
+
- **Full set** — `wyvrnpm version 1.2.3.4` writes the value verbatim. Strict 4-part `x.y.z.b` validation; leading zeros and 3-part semver are rejected.
|
|
244
|
+
- **Component flags** — `--major / --minor / --patch / --build N` replace one component while leaving the others alone. Combine flags freely (`--minor 3 --build 0`).
|
|
245
|
+
- Positional and component flags are mutually exclusive — pick a mode.
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
# Read the current version (CI capture)
|
|
249
|
+
VER=$(wyvrnpm version)
|
|
250
|
+
|
|
251
|
+
# Inject the build number from a pipeline variable
|
|
252
|
+
wyvrnpm version --build "$BUILD_ID"
|
|
253
|
+
|
|
254
|
+
# Cut a minor release — reset the build component to 0
|
|
255
|
+
wyvrnpm version --minor 3 --build 0
|
|
256
|
+
|
|
257
|
+
# Set a full version verbatim
|
|
258
|
+
wyvrnpm version 2.0.0.0
|
|
259
|
+
|
|
260
|
+
# CI-friendly capture
|
|
261
|
+
wyvrnpm version --build "$BUILD_ID" --format json
|
|
262
|
+
# {"command":"version","previous":"1.2.3.4","version":"1.2.3.99","written":true,...}
|
|
263
|
+
|
|
264
|
+
# Preview without writing
|
|
265
|
+
wyvrnpm version --build 99 --dry-run
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Options**
|
|
269
|
+
|
|
270
|
+
| Option | Default | Description |
|
|
271
|
+
|---|---|---|
|
|
272
|
+
| `<newVersion>` positional | — | Full 4-part version (e.g. `1.2.3.4`). Mutex with `--major/--minor/--patch/--build`. |
|
|
273
|
+
| `--major` | — | Replace the 1st component. |
|
|
274
|
+
| `--minor` | — | Replace the 2nd component. |
|
|
275
|
+
| `--patch` | — | Replace the 3rd component. |
|
|
276
|
+
| `--build` | — | Replace the 4th component (typical CI use). |
|
|
277
|
+
| `--dry-run` | `false` | Compute and print the proposed version without writing. |
|
|
278
|
+
| `--format` | `text` | Output format: `text` (human) or `json` (CI — single object on stdout). |
|
|
279
|
+
| `--manifest` | `./wyvrn.json` | Path to the manifest file. |
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
238
283
|
### `wyvrnpm install`
|
|
239
284
|
|
|
240
285
|
Resolves the full dependency graph and downloads all packages.
|
|
@@ -431,6 +476,10 @@ wyvrnpm publish --path ./dist
|
|
|
431
476
|
|
|
432
477
|
# Overwrite an existing build (same version + profile hash)
|
|
433
478
|
wyvrnpm publish --force
|
|
479
|
+
|
|
480
|
+
# Dry-run — show the upload plan without touching the registry
|
|
481
|
+
wyvrnpm publish --dry-run
|
|
482
|
+
wyvrnpm publish --dry-run --format json | jq '.wouldOverwrite, .plan'
|
|
434
483
|
```
|
|
435
484
|
|
|
436
485
|
**Options**
|
|
@@ -445,6 +494,7 @@ wyvrnpm publish --force
|
|
|
445
494
|
| `--path` | `.` | Directory to zip and publish. |
|
|
446
495
|
| `--manifest` | `./wyvrn.json` | Path to the manifest file. |
|
|
447
496
|
| `--force` / `-f` | `false` | Overwrite an existing published version (same version + profile hash). |
|
|
497
|
+
| `--dry-run` | `false` | Run every step (profile, options, zip, SHA256, `v2Exists` check) and print the upload plan, but skip the provider write. An existing published version is reported as a warning (not an error) so the full plan still prints. Pair with `--format json` to gate CI on `wouldOverwrite`. |
|
|
448
498
|
|
|
449
499
|
During publish the tool also:
|
|
450
500
|
- reads `wyvrn.lock` to pin locked dependency versions into the uploaded manifest
|
package/bin/wyvrn.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
const yargs = require('yargs');
|
|
6
6
|
const init = require('../src/commands/init');
|
|
7
7
|
const add = require('../src/commands/add');
|
|
8
|
+
const versionCmd = require('../src/commands/version');
|
|
8
9
|
const install = require('../src/commands/install');
|
|
9
10
|
const clean = require('../src/commands/clean');
|
|
10
11
|
const publish = require('../src/commands/publish');
|
|
@@ -143,6 +144,40 @@ yargs
|
|
|
143
144
|
(argv) => add(argv),
|
|
144
145
|
)
|
|
145
146
|
|
|
147
|
+
// ── version ───────────────────────────────────────────────────────────────
|
|
148
|
+
.command(
|
|
149
|
+
'version [newVersion]',
|
|
150
|
+
'Read, set, or partially update the top-level `version` field in wyvrn.json. ' +
|
|
151
|
+
'Bare `wyvrnpm version` prints the current value on stdout (for `VER=$(...)` ' +
|
|
152
|
+
'capture). Pass a full 4-part value (1.2.3.4) or component flags (--major / ' +
|
|
153
|
+
'--minor / --patch / --build) to tweak individual parts. Designed for CI/CD — ' +
|
|
154
|
+
'pair with --format json to capture { previous, version } on stdout.',
|
|
155
|
+
(y) => {
|
|
156
|
+
y
|
|
157
|
+
.positional('newVersion', {
|
|
158
|
+
type: 'string',
|
|
159
|
+
describe: 'Full 4-part version to write verbatim (e.g. 1.2.3.4). ' +
|
|
160
|
+
'Mutually exclusive with --major/--minor/--patch/--build.',
|
|
161
|
+
})
|
|
162
|
+
.option('major', { type: 'number', description: 'Replace the 1st component (major).' })
|
|
163
|
+
.option('minor', { type: 'number', description: 'Replace the 2nd component (minor).' })
|
|
164
|
+
.option('patch', { type: 'number', description: 'Replace the 3rd component (patch).' })
|
|
165
|
+
.option('build', { type: 'number', description: 'Replace the 4th component (build) — typical CI use.' })
|
|
166
|
+
.option('dry-run', {
|
|
167
|
+
type: 'boolean',
|
|
168
|
+
default: false,
|
|
169
|
+
description: 'Compute and print the proposed version without writing wyvrn.json.',
|
|
170
|
+
})
|
|
171
|
+
.option('format', {
|
|
172
|
+
type: 'string',
|
|
173
|
+
choices: ['text', 'json'],
|
|
174
|
+
default: 'text',
|
|
175
|
+
description: 'Output format. "json" emits { command, previous, version, ... } on stdout.',
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
(argv) => versionCmd({ ...argv, dryRun: argv['dry-run'] }),
|
|
179
|
+
)
|
|
180
|
+
|
|
146
181
|
// ── install ───────────────────────────────────────────────────────────────
|
|
147
182
|
.command(
|
|
148
183
|
'install',
|
|
@@ -529,6 +564,17 @@ yargs
|
|
|
529
564
|
description: 'Overwrite an existing published version',
|
|
530
565
|
default: false,
|
|
531
566
|
})
|
|
567
|
+
.option('dry-run', {
|
|
568
|
+
type: 'boolean',
|
|
569
|
+
default: false,
|
|
570
|
+
description:
|
|
571
|
+
'Mirror of `npm publish --dry-run` — run every step (profile, ' +
|
|
572
|
+
'options, zip, SHA256, v2Exists check) and print the upload plan, ' +
|
|
573
|
+
'but skip the provider write. An existing published version is ' +
|
|
574
|
+
'reported as a warning instead of failing. Pair with --format json ' +
|
|
575
|
+
'to script bulk publish validation (plan + wouldOverwrite flag ' +
|
|
576
|
+
'land in stdout).',
|
|
577
|
+
})
|
|
532
578
|
.option('option', {
|
|
533
579
|
alias: 'o',
|
|
534
580
|
type: 'string',
|
|
@@ -554,7 +600,12 @@ yargs
|
|
|
554
600
|
})
|
|
555
601
|
;
|
|
556
602
|
},
|
|
557
|
-
(argv) => publish({
|
|
603
|
+
(argv) => publish({
|
|
604
|
+
...argv,
|
|
605
|
+
awsProfile: argv['aws-profile'],
|
|
606
|
+
tokenEnv: argv['token-env'],
|
|
607
|
+
dryRun: argv['dry-run'],
|
|
608
|
+
}),
|
|
558
609
|
)
|
|
559
610
|
|
|
560
611
|
// ── configure ─────────────────────────────────────────────────────────────
|
|
Binary file
|
package/package.json
CHANGED
package/src/commands/publish.js
CHANGED
|
@@ -22,6 +22,59 @@ const { loadIgnorePatterns, isIgnored } = require('../ignore'
|
|
|
22
22
|
const { hasBuildRecipe, normalizeRecipe } = require('../build/recipe');
|
|
23
23
|
const log = require('../logger');
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Human-friendly byte formatter for the dry-run summary.
|
|
27
|
+
*
|
|
28
|
+
* @param {number} n
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
function formatBytes(n) {
|
|
32
|
+
if (!Number.isFinite(n) || n < 0) return `${n}`;
|
|
33
|
+
if (n < 1024) return `${n} B`;
|
|
34
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
35
|
+
if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(2)} MB`;
|
|
36
|
+
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compute the registry URLs/paths that `v2Publish` would write to. Pure —
|
|
41
|
+
* no network, no filesystem. Mirrors the keys produced by S3/HTTP/File
|
|
42
|
+
* providers (§4.5 of CLAUDE.md), so dry-run can surface the exact shape
|
|
43
|
+
* of the upload without actually performing it.
|
|
44
|
+
*
|
|
45
|
+
* @param {{
|
|
46
|
+
* source: string,
|
|
47
|
+
* name: string,
|
|
48
|
+
* version: string,
|
|
49
|
+
* profileHash: string,
|
|
50
|
+
* gitSha?: string | null,
|
|
51
|
+
* gitRepo?: string | null,
|
|
52
|
+
* updateLatest?: boolean,
|
|
53
|
+
* }} opts
|
|
54
|
+
* @returns {Array<{ path: string, kind: 'wyvrn.zip'|'wyvrn.json'|'source.json'|'versions.json'|'latest.json' }>}
|
|
55
|
+
*/
|
|
56
|
+
function planPublishUrls({
|
|
57
|
+
source, name, version, profileHash,
|
|
58
|
+
gitSha = null, gitRepo = null, updateLatest = true,
|
|
59
|
+
}) {
|
|
60
|
+
const base = source.replace(/\/$/, '');
|
|
61
|
+
const v2root = `${base}/v2/${name}`;
|
|
62
|
+
const buildRoot = `${v2root}/${version}/${profileHash}`;
|
|
63
|
+
|
|
64
|
+
const plan = [
|
|
65
|
+
{ path: `${buildRoot}/wyvrn.zip`, kind: 'wyvrn.zip' },
|
|
66
|
+
{ path: `${buildRoot}/wyvrn.json`, kind: 'wyvrn.json' },
|
|
67
|
+
];
|
|
68
|
+
if (gitSha || gitRepo) {
|
|
69
|
+
plan.push({ path: `${v2root}/${version}/source.json`, kind: 'source.json' });
|
|
70
|
+
}
|
|
71
|
+
plan.push({ path: `${v2root}/versions.json`, kind: 'versions.json' });
|
|
72
|
+
if (updateLatest) {
|
|
73
|
+
plan.push({ path: `${v2root}/latest.json`, kind: 'latest.json' });
|
|
74
|
+
}
|
|
75
|
+
return plan;
|
|
76
|
+
}
|
|
77
|
+
|
|
25
78
|
/**
|
|
26
79
|
* Walk `dir` recursively and add every file to `zip`, skipping anything that
|
|
27
80
|
* matches `.wyvrnignore` patterns OR whose zip-relative path is already in
|
|
@@ -146,7 +199,11 @@ async function publish(argv) {
|
|
|
146
199
|
|
|
147
200
|
const { source, awsProfile, token } = resolveSource(argv, ctx);
|
|
148
201
|
const { path: publishPath, force } = argv;
|
|
202
|
+
const dryRun = Boolean(argv.dryRun);
|
|
149
203
|
const jsonOut = ctx.flags.format === 'json';
|
|
204
|
+
if (dryRun) {
|
|
205
|
+
log.info('DRY RUN — no upload will occur. Previewing publish plan only.');
|
|
206
|
+
}
|
|
150
207
|
|
|
151
208
|
// ── Load manifest ─────────────────────────────────────────────────────────
|
|
152
209
|
// ctx.manifest is a lazy getter — throws on first access if the file is
|
|
@@ -221,10 +278,18 @@ async function publish(argv) {
|
|
|
221
278
|
);
|
|
222
279
|
|
|
223
280
|
// ── Check v2 existence ────────────────────────────────────────────────────
|
|
281
|
+
// Dry-run softens the overwrite-without-force error to a warning so the
|
|
282
|
+
// full plan still prints — CI callers should parse `--format json` and
|
|
283
|
+
// gate on `wouldOverwrite` rather than on the exit code.
|
|
224
284
|
const v2AlreadyExists = await provider.v2Exists({ source, name, version, profileHash, awsProfile, token });
|
|
225
285
|
if (v2AlreadyExists) {
|
|
226
286
|
if (force) {
|
|
227
287
|
log.warn(`${name}@${version} [${profileHash}] already exists — overwriting (--force)`);
|
|
288
|
+
} else if (dryRun) {
|
|
289
|
+
log.warn(
|
|
290
|
+
`${name}@${version} [${profileHash}] already exists on the registry. ` +
|
|
291
|
+
`A real publish would refuse without --force.`,
|
|
292
|
+
);
|
|
228
293
|
} else {
|
|
229
294
|
log.error(
|
|
230
295
|
`${name}@${version} [${profileHash}] already exists.\n` +
|
|
@@ -380,7 +445,60 @@ async function publish(argv) {
|
|
|
380
445
|
zip.writeZip(tmpZipPath);
|
|
381
446
|
|
|
382
447
|
const contentSha256 = sha256Of(tmpZipPath);
|
|
448
|
+
const zipBytes = fs.statSync(tmpZipPath).size;
|
|
449
|
+
const manifestBytes = fs.statSync(tmpManifestPath).size;
|
|
383
450
|
log.info(`Content SHA256 : ${contentSha256}`);
|
|
451
|
+
log.info(`Zip size : ${formatBytes(zipBytes)} (${zipBytes} bytes)`);
|
|
452
|
+
|
|
453
|
+
// ── Dry run: print the plan, skip the actual upload ──────────────────
|
|
454
|
+
// Mirrors `npm publish --dry-run` — every step above (profile, options,
|
|
455
|
+
// zip, hash, v2Exists) runs; only the provider write is skipped.
|
|
456
|
+
if (dryRun) {
|
|
457
|
+
const plan = planPublishUrls({
|
|
458
|
+
source, name, version, profileHash, gitSha, gitRepo, updateLatest: true,
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
if (!jsonOut) {
|
|
462
|
+
console.log();
|
|
463
|
+
console.log(`Would publish ${name}@${version} [${profileHash}] to ${source}`);
|
|
464
|
+
console.log(` contentSha256 : ${contentSha256}`);
|
|
465
|
+
console.log(` zip size : ${formatBytes(zipBytes)}`);
|
|
466
|
+
console.log(` manifest size : ${formatBytes(manifestBytes)}`);
|
|
467
|
+
if (v2AlreadyExists) {
|
|
468
|
+
console.log(` overwrite : YES — ${force ? '--force set, would proceed' : 'NOT SET, real publish would refuse'}`);
|
|
469
|
+
}
|
|
470
|
+
console.log();
|
|
471
|
+
console.log('Registry writes:');
|
|
472
|
+
for (const { path: p, kind } of plan) {
|
|
473
|
+
console.log(` → ${p} (${kind})`);
|
|
474
|
+
}
|
|
475
|
+
console.log();
|
|
476
|
+
log.success('Dry run OK — nothing uploaded.');
|
|
477
|
+
} else {
|
|
478
|
+
const payload = {
|
|
479
|
+
command: 'publish',
|
|
480
|
+
dryRun: true,
|
|
481
|
+
wyvrnpmVersion: require('../../package.json').version,
|
|
482
|
+
name,
|
|
483
|
+
version,
|
|
484
|
+
profileHash,
|
|
485
|
+
source,
|
|
486
|
+
contentSha256,
|
|
487
|
+
zipBytes,
|
|
488
|
+
manifestBytes,
|
|
489
|
+
options: effectiveOptions ?? null,
|
|
490
|
+
gitSha: gitSha ?? null,
|
|
491
|
+
gitRepo: gitRepo ?? null,
|
|
492
|
+
wouldOverwrite: Boolean(v2AlreadyExists),
|
|
493
|
+
forceSet: Boolean(force),
|
|
494
|
+
publishedLock,
|
|
495
|
+
publishedConf: Object.keys(publishedConf).length > 0 ? unflattenConf(publishedConf) : null,
|
|
496
|
+
plan,
|
|
497
|
+
};
|
|
498
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
499
|
+
}
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
384
502
|
|
|
385
503
|
// ── v2 publish ────────────────────────────────────────────────────────
|
|
386
504
|
log.info('Publishing (v2) ...');
|
|
@@ -433,3 +551,5 @@ module.exports = publish;
|
|
|
433
551
|
module.exports.addFolderFiltered = addFolderFiltered;
|
|
434
552
|
module.exports.addInstallTree = addInstallTree;
|
|
435
553
|
module.exports.collectInstallTreeFiles = collectInstallTreeFiles;
|
|
554
|
+
module.exports.planPublishUrls = planPublishUrls;
|
|
555
|
+
module.exports.formatBytes = formatBytes;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const { readManifest, writeManifest } = require('../manifest');
|
|
6
|
+
const log = require('../logger');
|
|
7
|
+
|
|
8
|
+
/** Strict 4-part wyvrnpm version: `x.y.z.b`, all non-negative integers, no leading zeros. */
|
|
9
|
+
const VERSION_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse a 4-part version string into `[major, minor, patch, build]`.
|
|
13
|
+
* Throws with a clear message when the string fails the wyvrnpm format.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} v
|
|
16
|
+
* @returns {[number, number, number, number]}
|
|
17
|
+
*/
|
|
18
|
+
function parseVersion(v) {
|
|
19
|
+
if (typeof v !== 'string' || v.length === 0) {
|
|
20
|
+
throw new Error('version is empty');
|
|
21
|
+
}
|
|
22
|
+
const m = VERSION_RE.exec(v);
|
|
23
|
+
if (!m) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`"${v}" is not a valid wyvrnpm version. ` +
|
|
26
|
+
'Expected 4-part x.y.z.b with non-negative integers (e.g. 1.2.3.4).',
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const COMPONENT_INDEX = { major: 0, minor: 1, patch: 2, build: 3 };
|
|
33
|
+
|
|
34
|
+
function isNonNegInt(n) {
|
|
35
|
+
return Number.isInteger(n) && n >= 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* `wyvrnpm version [<new-version>]` — read, set, or partially update the
|
|
40
|
+
* top-level `version` field in wyvrn.json. Designed for CI/CD (build-number
|
|
41
|
+
* injection + tag-the-build readback).
|
|
42
|
+
*
|
|
43
|
+
* wyvrnpm version → print the current version
|
|
44
|
+
* wyvrnpm version 1.2.3.4 → set verbatim
|
|
45
|
+
* wyvrnpm version --build 99 → only the 4th component
|
|
46
|
+
* wyvrnpm version --minor 3 --build 0 → multiple components, others left alone
|
|
47
|
+
*
|
|
48
|
+
* Positional and component flags are mutually exclusive — pick one mode.
|
|
49
|
+
* Bare invocation is read-only (never writes, never warns about an unset
|
|
50
|
+
* version on disk — it prints `(unset)` to stderr and exits 1 in that
|
|
51
|
+
* case so scripts notice). CI scripts use --format json to capture the
|
|
52
|
+
* `{ previous, version }` payload on stdout.
|
|
53
|
+
*
|
|
54
|
+
* @param {object} argv
|
|
55
|
+
*/
|
|
56
|
+
function versionCmd(argv) {
|
|
57
|
+
const manifestPath = path.resolve(argv.manifest);
|
|
58
|
+
const jsonOut = argv.format === 'json';
|
|
59
|
+
if (jsonOut) log.setJsonMode(true);
|
|
60
|
+
|
|
61
|
+
const positional = argv.newVersion;
|
|
62
|
+
|
|
63
|
+
// Component flags arrive on argv as `argv.major` / `argv.minor` / etc.
|
|
64
|
+
// because yargs lowercases option names. Keep insertion order so error
|
|
65
|
+
// messages list flags in the same order as the CLI surface.
|
|
66
|
+
const partial = {};
|
|
67
|
+
for (const key of Object.keys(COMPONENT_INDEX)) {
|
|
68
|
+
if (argv[key] !== undefined) partial[key] = argv[key];
|
|
69
|
+
}
|
|
70
|
+
const partialKeys = Object.keys(partial);
|
|
71
|
+
|
|
72
|
+
if (positional && partialKeys.length > 0) {
|
|
73
|
+
log.error(
|
|
74
|
+
'cannot combine a positional version with --major/--minor/--patch/--build. ' +
|
|
75
|
+
'Use one or the other.',
|
|
76
|
+
);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Read-only mode: no positional + no component flags. Prints the current
|
|
81
|
+
// version and returns without touching disk. Matches `npm version` behaviour
|
|
82
|
+
// when called bare.
|
|
83
|
+
const readOnly = !positional && partialKeys.length === 0;
|
|
84
|
+
|
|
85
|
+
let manifest;
|
|
86
|
+
try {
|
|
87
|
+
manifest = readManifest(manifestPath);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
log.error(err.message);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const previous =
|
|
94
|
+
typeof manifest.version === 'string' && manifest.version.length > 0
|
|
95
|
+
? manifest.version
|
|
96
|
+
: null;
|
|
97
|
+
|
|
98
|
+
if (readOnly) {
|
|
99
|
+
const wyvrnpmVersion = require('../../package.json').version;
|
|
100
|
+
if (jsonOut) {
|
|
101
|
+
process.stdout.write(JSON.stringify({
|
|
102
|
+
command: 'version',
|
|
103
|
+
wyvrnpmVersion,
|
|
104
|
+
path: manifestPath,
|
|
105
|
+
version: previous,
|
|
106
|
+
written: false,
|
|
107
|
+
}) + '\n');
|
|
108
|
+
} else if (previous) {
|
|
109
|
+
// Bare stdout for `VER=$(wyvrnpm version)` — no `[wyvrn]` prefix here.
|
|
110
|
+
process.stdout.write(previous + '\n');
|
|
111
|
+
} else {
|
|
112
|
+
log.error(`no \`version\` field in ${manifestPath}`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let newVersion;
|
|
119
|
+
if (positional) {
|
|
120
|
+
try {
|
|
121
|
+
parseVersion(positional);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
log.error(err.message);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
newVersion = positional;
|
|
127
|
+
} else {
|
|
128
|
+
let parts;
|
|
129
|
+
try {
|
|
130
|
+
parts = parseVersion(previous ?? '');
|
|
131
|
+
} catch (err) {
|
|
132
|
+
log.error(
|
|
133
|
+
`cannot tweak components — current manifest version is invalid or missing: ${err.message}\n` +
|
|
134
|
+
' Set a full version first: wyvrnpm version <x.y.z.b>',
|
|
135
|
+
);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
for (const key of partialKeys) {
|
|
139
|
+
const n = partial[key];
|
|
140
|
+
if (!isNonNegInt(n)) {
|
|
141
|
+
log.error(`--${key} must be a non-negative integer (got ${JSON.stringify(n)})`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
parts[COMPONENT_INDEX[key]] = n;
|
|
145
|
+
}
|
|
146
|
+
newVersion = parts.join('.');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const wyvrnpmVersion = require('../../package.json').version;
|
|
150
|
+
|
|
151
|
+
if (argv.dryRun) {
|
|
152
|
+
if (jsonOut) {
|
|
153
|
+
process.stdout.write(JSON.stringify({
|
|
154
|
+
command: 'version',
|
|
155
|
+
wyvrnpmVersion,
|
|
156
|
+
path: manifestPath,
|
|
157
|
+
previous,
|
|
158
|
+
version: newVersion,
|
|
159
|
+
dryRun: true,
|
|
160
|
+
written: false,
|
|
161
|
+
}) + '\n');
|
|
162
|
+
} else {
|
|
163
|
+
log.info(`would set version: ${previous ?? '(unset)'} → ${newVersion} (dry-run)`);
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
manifest.version = newVersion;
|
|
169
|
+
writeManifest(manifestPath, manifest);
|
|
170
|
+
|
|
171
|
+
if (jsonOut) {
|
|
172
|
+
process.stdout.write(JSON.stringify({
|
|
173
|
+
command: 'version',
|
|
174
|
+
wyvrnpmVersion,
|
|
175
|
+
path: manifestPath,
|
|
176
|
+
previous,
|
|
177
|
+
version: newVersion,
|
|
178
|
+
dryRun: false,
|
|
179
|
+
written: true,
|
|
180
|
+
}) + '\n');
|
|
181
|
+
} else {
|
|
182
|
+
log.success(`${manifestPath}: ${previous ?? '(unset)'} → ${newVersion}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = versionCmd;
|
|
187
|
+
module.exports.parseVersion = parseVersion;
|