wyvrnpm 2.15.0 → 2.16.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/bin/wyvrnpm.js +33 -1
- package/package.json +1 -1
- package/src/commands/clean.js +184 -26
- package/src/toolchain/presets.js +78 -0
package/bin/wyvrnpm.js
CHANGED
|
@@ -506,9 +506,34 @@ yargs
|
|
|
506
506
|
// ── clean ─────────────────────────────────────────────────────────────────
|
|
507
507
|
.command(
|
|
508
508
|
'clean',
|
|
509
|
-
'Remove downloaded packages and lock file. --build-
|
|
509
|
+
'Remove downloaded packages and lock file. --build-dir adds the active profile\'s CMake build dir; --all wipes the entire wyvrnpm workspace footprint.',
|
|
510
510
|
(y) => {
|
|
511
511
|
y
|
|
512
|
+
.option('profile', {
|
|
513
|
+
alias: 'p',
|
|
514
|
+
type: 'string',
|
|
515
|
+
description: 'Active profile name — determines which wyvrn-<profile>/ build dir --build-dir targets',
|
|
516
|
+
})
|
|
517
|
+
.option('build-dir', {
|
|
518
|
+
type: 'boolean',
|
|
519
|
+
default: false,
|
|
520
|
+
description:
|
|
521
|
+
'Also remove the active profile\'s CMake build directory ' +
|
|
522
|
+
'(<binaryDirRoot>/wyvrn-<profile>/). Pairs well with CI cleanup. ' +
|
|
523
|
+
'binaryDirRoot resolves from wyvrn.local.json; non-default roots ' +
|
|
524
|
+
'set only via the install-time --build-dir <path> flag will not ' +
|
|
525
|
+
'be found here.',
|
|
526
|
+
})
|
|
527
|
+
.option('all', {
|
|
528
|
+
type: 'boolean',
|
|
529
|
+
default: false,
|
|
530
|
+
description:
|
|
531
|
+
'Wipe every wyvrnpm-managed file in the workspace: wyvrn_internal/, ' +
|
|
532
|
+
'wyvrn.lock, every <binaryDirRoot>/wyvrn-*/ build dir, and wyvrn-* ' +
|
|
533
|
+
'entries from CMakePresets.json / CMakeUserPresets.json. Does NOT ' +
|
|
534
|
+
'touch the machine-wide build cache — combine with --build-cache ' +
|
|
535
|
+
'for that.',
|
|
536
|
+
})
|
|
512
537
|
.option('build-cache', {
|
|
513
538
|
type: 'boolean',
|
|
514
539
|
default: false,
|
|
@@ -521,12 +546,19 @@ yargs
|
|
|
521
546
|
'Wipe only the local record of artefacts uploaded via --upload-built ' +
|
|
522
547
|
'(the .uploaded-to.json sidecars in the build cache). Does not touch ' +
|
|
523
548
|
'the registry, the artefact zips, or wyvrn_internal/.',
|
|
549
|
+
})
|
|
550
|
+
.option('dry-run', {
|
|
551
|
+
type: 'boolean',
|
|
552
|
+
default: false,
|
|
553
|
+
description: 'Print what would be removed without modifying anything.',
|
|
524
554
|
});
|
|
525
555
|
},
|
|
526
556
|
(argv) => clean({
|
|
527
557
|
...argv,
|
|
558
|
+
buildDir: argv['build-dir'],
|
|
528
559
|
buildCache: argv['build-cache'],
|
|
529
560
|
uploadedBuilt: argv['uploaded-built'],
|
|
561
|
+
dryRun: argv['dry-run'],
|
|
530
562
|
}),
|
|
531
563
|
)
|
|
532
564
|
|
package/package.json
CHANGED
package/src/commands/clean.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const fs
|
|
3
|
+
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
6
|
const {
|
|
@@ -8,72 +8,230 @@ const {
|
|
|
8
8
|
getBuildCacheRoot,
|
|
9
9
|
clearUploadSidecars,
|
|
10
10
|
} = require('../build/cache');
|
|
11
|
+
const { readConfig } = require('../config');
|
|
12
|
+
const { readLocalOverlay } = require('../conf');
|
|
13
|
+
const { resolveBinaryDirRoot } = require('../binary-dir');
|
|
14
|
+
const { stripWyvrnPresets, safeReadJson } = require('../toolchain/presets');
|
|
11
15
|
const log = require('../logger');
|
|
12
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the active profile name using the same precedence install/build
|
|
19
|
+
* use, but without pulling the full buildContext (which would require a
|
|
20
|
+
* manifest, parse --conf, etc. — overkill for a cleanup command).
|
|
21
|
+
*
|
|
22
|
+
* Precedence: --profile > config.defaultProfile > "default".
|
|
23
|
+
*/
|
|
24
|
+
function resolveProfileName(argv) {
|
|
25
|
+
if (typeof argv.profile === 'string' && argv.profile.length > 0) {
|
|
26
|
+
return argv.profile;
|
|
27
|
+
}
|
|
28
|
+
const config = readConfig();
|
|
29
|
+
if (typeof config.defaultProfile === 'string' && config.defaultProfile.length > 0) {
|
|
30
|
+
return config.defaultProfile;
|
|
31
|
+
}
|
|
32
|
+
return 'default';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolve binaryDirRoot the same way install/build do — overlay-only on
|
|
37
|
+
* clean (no `--build-dir <path>` setter; clean's `--build-dir` is a
|
|
38
|
+
* boolean selector). If the user installed against a non-default root
|
|
39
|
+
* via the install-time CLI flag, they need the overlay set for cleanup
|
|
40
|
+
* to find the same tree.
|
|
41
|
+
*/
|
|
42
|
+
function resolveBinaryRoot(rootDir) {
|
|
43
|
+
const overlay = readLocalOverlay(rootDir);
|
|
44
|
+
return resolveBinaryDirRoot({
|
|
45
|
+
localOverlayBinaryDirRoot: overlay.binaryDirRoot,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* List every `wyvrn-*` subdirectory directly under `<rootDir>/<binaryRoot>/`.
|
|
51
|
+
* Returns absolute paths. Empty list if the parent dir doesn't exist.
|
|
52
|
+
*/
|
|
53
|
+
function listWyvrnBuildDirs(rootDir, binaryRoot) {
|
|
54
|
+
const parent = path.join(rootDir, binaryRoot);
|
|
55
|
+
if (!fs.existsSync(parent)) return [];
|
|
56
|
+
let entries;
|
|
57
|
+
try {
|
|
58
|
+
entries = fs.readdirSync(parent, { withFileTypes: true });
|
|
59
|
+
} catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
return entries
|
|
63
|
+
.filter((e) => e.isDirectory() && e.name.startsWith('wyvrn-'))
|
|
64
|
+
.map((e) => path.join(parent, e.name));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Remove a path (file or dir). Honours dry-run by logging instead. Returns
|
|
69
|
+
* true if the path existed and we either removed it or would have.
|
|
70
|
+
*/
|
|
71
|
+
function removePath(p, { dryRun, label }) {
|
|
72
|
+
if (!fs.existsSync(p)) return false;
|
|
73
|
+
if (dryRun) {
|
|
74
|
+
log.info(`Would remove ${label ?? p}`);
|
|
75
|
+
} else {
|
|
76
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
77
|
+
log.info(`Removed ${label ?? p}`);
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Strip wyvrn-generated entries from `CMakePresets.json` /
|
|
84
|
+
* `CMakeUserPresets.json`. User-owned files (no wyvrnpm vendor marker)
|
|
85
|
+
* are left untouched. Files whose only meaningful content was wyvrnpm's
|
|
86
|
+
* are deleted; files with surviving foreign content are rewritten.
|
|
87
|
+
*
|
|
88
|
+
* Returns the number of files touched (incl. dry-run "would touch").
|
|
89
|
+
*/
|
|
90
|
+
function stripPresetsFromWorkspace(rootDir, { dryRun }) {
|
|
91
|
+
const candidates = [
|
|
92
|
+
path.join(rootDir, 'CMakePresets.json'),
|
|
93
|
+
path.join(rootDir, 'CMakeUserPresets.json'),
|
|
94
|
+
];
|
|
95
|
+
let touched = 0;
|
|
96
|
+
for (const filePath of candidates) {
|
|
97
|
+
if (!fs.existsSync(filePath)) continue;
|
|
98
|
+
const existing = safeReadJson(filePath);
|
|
99
|
+
if (!existing) continue;
|
|
100
|
+
// Only act on files we generated. A user-owned file (no wyvrnpm
|
|
101
|
+
// vendor marker AND no wyvrn-* presets) is left alone — defence in
|
|
102
|
+
// depth so we never strip presets from a hand-authored file even
|
|
103
|
+
// if a stray `wyvrn-x` name slipped in.
|
|
104
|
+
const hasOurVendor = !!(existing.vendor && existing.vendor['wyvrnpm/generated']);
|
|
105
|
+
const hasOurPresets =
|
|
106
|
+
(existing.configurePresets ?? []).some((p) => p && typeof p.name === 'string' && p.name.startsWith('wyvrn-')) ||
|
|
107
|
+
(existing.buildPresets ?? []).some((p) => p && typeof p.name === 'string' && p.name.startsWith('wyvrn-'));
|
|
108
|
+
if (!hasOurVendor && !hasOurPresets) continue;
|
|
109
|
+
|
|
110
|
+
const { result, removed } = stripWyvrnPresets(existing);
|
|
111
|
+
if (removed.length === 0 && hasOurVendor && !result) continue;
|
|
112
|
+
|
|
113
|
+
if (result === null) {
|
|
114
|
+
if (dryRun) {
|
|
115
|
+
log.info(`Would remove ${filePath} (only wyvrnpm content remained)`);
|
|
116
|
+
} else {
|
|
117
|
+
fs.unlinkSync(filePath);
|
|
118
|
+
log.info(`Removed ${filePath} (only wyvrnpm content remained)`);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
if (dryRun) {
|
|
122
|
+
log.info(`Would strip ${removed.length} wyvrn-* preset(s) from ${filePath}`);
|
|
123
|
+
} else {
|
|
124
|
+
fs.writeFileSync(filePath, JSON.stringify(result, null, 2) + '\n', 'utf8');
|
|
125
|
+
log.info(`Stripped ${removed.length} wyvrn-* preset(s) from ${filePath}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
touched += 1;
|
|
129
|
+
}
|
|
130
|
+
return touched;
|
|
131
|
+
}
|
|
132
|
+
|
|
13
133
|
/**
|
|
14
134
|
* Removes the project's `wyvrn_internal/` package cache and `wyvrn.lock` file.
|
|
15
|
-
* With `--build-cache`, also clears the machine-wide source-build cache
|
|
16
|
-
* under `%LOCALAPPDATA%\wyvrnpm\bc\` (shared across projects).
|
|
17
135
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
136
|
+
* Flag matrix:
|
|
137
|
+
* - (default) wyvrn_internal/ + wyvrn.lock
|
|
138
|
+
* - --build-dir also the active profile's wyvrn-<profile> build dir
|
|
139
|
+
* - --all default set + every wyvrn-prefixed build dir + wyvrn-prefixed preset entries
|
|
140
|
+
* - --build-cache also the machine-wide source-build cache
|
|
141
|
+
* - --uploaded-built wipe just the upload sidecars (targeted-only when used
|
|
142
|
+
* alone — does not also do the default scrub)
|
|
143
|
+
* - --dry-run print what would be removed; write nothing
|
|
23
144
|
*
|
|
24
145
|
* @param {object} argv
|
|
25
146
|
* @param {string} argv.root Project root directory.
|
|
26
147
|
* @param {string} argv.manifest Path to the manifest file (used to locate wyvrn.lock).
|
|
148
|
+
* @param {string} [argv.profile] Active profile name (overrides config.defaultProfile).
|
|
149
|
+
* @param {boolean} [argv.buildDir] When true, also wipes <binaryDirRoot>/wyvrn-<profile>/.
|
|
150
|
+
* @param {boolean} [argv.all] When true, scorched-earth workspace cleanup.
|
|
27
151
|
* @param {boolean} [argv.buildCache] When true, also nukes the source-build cache.
|
|
28
152
|
* @param {boolean} [argv.uploadedBuilt] When true, wipes upload sidecars only.
|
|
153
|
+
* @param {boolean} [argv.dryRun] When true, log intentions but write nothing.
|
|
29
154
|
* @returns {Promise<void>}
|
|
30
155
|
*/
|
|
31
156
|
async function clean(argv) {
|
|
32
157
|
const rootDir = path.resolve(argv.root);
|
|
33
158
|
const razerDir = path.join(rootDir, 'wyvrn_internal');
|
|
34
159
|
const lockPath = path.join(path.dirname(path.resolve(argv.manifest)), 'wyvrn.lock');
|
|
160
|
+
const dryRun = !!argv.dryRun;
|
|
35
161
|
|
|
36
162
|
// --uploaded-built is a targeted cleanup mode. When passed on its own,
|
|
37
163
|
// skip the default wyvrn_internal/ + wyvrn.lock scrub so a user can
|
|
38
164
|
// "forget uploads" without also wiping their project-local install.
|
|
39
|
-
|
|
165
|
+
// --all forces the default scrub regardless (scorched-earth wins).
|
|
166
|
+
const targetedOnly = argv.uploadedBuilt && !argv.buildCache && !argv.buildDir && !argv.all;
|
|
40
167
|
|
|
41
168
|
let removedAnything = false;
|
|
42
169
|
|
|
43
170
|
if (!targetedOnly) {
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
171
|
+
if (removePath(razerDir, { dryRun, label: razerDir })) removedAnything = true;
|
|
172
|
+
else log.info(`Nothing to clean at ${razerDir}`);
|
|
173
|
+
|
|
174
|
+
if (removePath(lockPath, { dryRun, label: lockPath })) removedAnything = true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// --build-dir / --all: wipe CMake build trees.
|
|
178
|
+
if (argv.buildDir || argv.all) {
|
|
179
|
+
const binaryRoot = resolveBinaryRoot(rootDir);
|
|
180
|
+
|
|
181
|
+
if (argv.all) {
|
|
182
|
+
// Every wyvrn-* subdir under binaryDirRoot — covers all profiles
|
|
183
|
+
// any past install/build wrote to.
|
|
184
|
+
const dirs = listWyvrnBuildDirs(rootDir, binaryRoot);
|
|
185
|
+
if (dirs.length === 0) {
|
|
186
|
+
log.info(`No wyvrn-* build directories under ${path.join(rootDir, binaryRoot)}`);
|
|
187
|
+
}
|
|
188
|
+
for (const d of dirs) {
|
|
189
|
+
if (removePath(d, { dryRun, label: d })) removedAnything = true;
|
|
190
|
+
}
|
|
48
191
|
} else {
|
|
49
|
-
|
|
192
|
+
// Active profile only — symmetric with `wyvrnpm build --clean`.
|
|
193
|
+
const profileName = resolveProfileName(argv);
|
|
194
|
+
const buildDir = path.join(rootDir, binaryRoot, `wyvrn-${profileName}`);
|
|
195
|
+
if (removePath(buildDir, { dryRun, label: buildDir })) removedAnything = true;
|
|
196
|
+
else log.info(`Nothing to clean at ${buildDir}`);
|
|
50
197
|
}
|
|
198
|
+
}
|
|
51
199
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
200
|
+
// --all: also strip wyvrn-* entries from CMakePresets.json / CMakeUserPresets.json.
|
|
201
|
+
if (argv.all) {
|
|
202
|
+
const touched = stripPresetsFromWorkspace(rootDir, { dryRun });
|
|
203
|
+
if (touched > 0) removedAnything = true;
|
|
57
204
|
}
|
|
58
205
|
|
|
59
206
|
if (argv.buildCache) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
log.info(`Removed source-build cache ${removed}`);
|
|
207
|
+
if (dryRun) {
|
|
208
|
+
log.info(`Would remove source-build cache ${getBuildCacheRoot()}`);
|
|
63
209
|
removedAnything = true;
|
|
64
210
|
} else {
|
|
65
|
-
|
|
211
|
+
const removed = clearBuildCache();
|
|
212
|
+
if (removed) {
|
|
213
|
+
log.info(`Removed source-build cache ${removed}`);
|
|
214
|
+
removedAnything = true;
|
|
215
|
+
} else {
|
|
216
|
+
log.info(`No source-build cache at ${getBuildCacheRoot()}`);
|
|
217
|
+
}
|
|
66
218
|
}
|
|
67
219
|
} else if (argv.uploadedBuilt) {
|
|
68
220
|
// clearBuildCache() would have nuked the sidecars as a side-effect, so
|
|
69
221
|
// only bother with the sidecar sweep when the cache itself is surviving.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
222
|
+
if (dryRun) {
|
|
223
|
+
log.info('Would remove upload sidecars from the build cache');
|
|
224
|
+
} else {
|
|
225
|
+
const removed = clearUploadSidecars();
|
|
226
|
+
log.info(`Removed ${removed} upload record${removed === 1 ? '' : 's'} from the build cache`);
|
|
227
|
+
if (removed > 0) removedAnything = true;
|
|
228
|
+
}
|
|
73
229
|
}
|
|
74
230
|
|
|
75
231
|
if (!removedAnything) {
|
|
76
232
|
log.info('Nothing to clean.');
|
|
233
|
+
} else if (dryRun) {
|
|
234
|
+
log.info('Dry run — no files were modified.');
|
|
77
235
|
}
|
|
78
236
|
}
|
|
79
237
|
|
package/src/toolchain/presets.js
CHANGED
|
@@ -343,8 +343,86 @@ function generateCMakePresets({
|
|
|
343
343
|
return { path: null, action: 'skipped', isUser: false };
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Strip every wyvrnpm-generated entry from a parsed presets object.
|
|
348
|
+
* Mirror of `mergeIntoExisting` — used by `wyvrnpm clean --all` to
|
|
349
|
+
* leave no trace of our injections in the consumer's CMakePresets.json.
|
|
350
|
+
*
|
|
351
|
+
* Both `configurePresets` and `buildPresets` are filtered for names
|
|
352
|
+
* matching `/^wyvrn-/`. The wyvrnpm `vendor` entry is always removed.
|
|
353
|
+
*
|
|
354
|
+
* Returns:
|
|
355
|
+
* - `result: null` → the file is now empty of meaningful content
|
|
356
|
+
* (no presets of either kind, no other vendor
|
|
357
|
+
* entries, no other top-level keys beyond the
|
|
358
|
+
* schema `version`). Caller should delete the
|
|
359
|
+
* file rather than write a husk.
|
|
360
|
+
* - `result: object` → the trimmed object the caller should write
|
|
361
|
+
* back. Foreign vendor blocks, user-named
|
|
362
|
+
* presets, and any other top-level keys are
|
|
363
|
+
* preserved.
|
|
364
|
+
* - `removed: string[]` → names of every preset (configure + build)
|
|
365
|
+
* that was stripped. Useful for dry-run
|
|
366
|
+
* reporting.
|
|
367
|
+
*
|
|
368
|
+
* @param {object|null} existing
|
|
369
|
+
* @returns {{ result: object|null, removed: string[] }}
|
|
370
|
+
*/
|
|
371
|
+
function stripWyvrnPresets(existing) {
|
|
372
|
+
if (!existing || typeof existing !== 'object') {
|
|
373
|
+
return { result: null, removed: [] };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const isWyvrnName = (n) => typeof n === 'string' && n.startsWith('wyvrn-');
|
|
377
|
+
|
|
378
|
+
const removed = [];
|
|
379
|
+
const configurePresets = (existing.configurePresets ?? []).filter((p) => {
|
|
380
|
+
if (p && isWyvrnName(p.name)) { removed.push(p.name); return false; }
|
|
381
|
+
return true;
|
|
382
|
+
});
|
|
383
|
+
const buildPresets = (existing.buildPresets ?? []).filter((p) => {
|
|
384
|
+
if (p && (isWyvrnName(p.name) || isWyvrnName(p.configurePreset))) {
|
|
385
|
+
removed.push(p.name);
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
return true;
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Drop our vendor marker; preserve any foreign vendor entries.
|
|
392
|
+
let vendor = existing.vendor;
|
|
393
|
+
if (vendor && typeof vendor === 'object') {
|
|
394
|
+
const { [VENDOR_KEY]: _ours, ...rest } = vendor;
|
|
395
|
+
vendor = Object.keys(rest).length > 0 ? rest : undefined;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Decide if anything meaningful survives. The schema `version` field
|
|
399
|
+
// is metadata, not content — a file with only `{ version: 3 }` is a
|
|
400
|
+
// husk we should delete.
|
|
401
|
+
const hasSurvivingPresets = configurePresets.length > 0 || buildPresets.length > 0;
|
|
402
|
+
const hasForeignVendor = vendor !== undefined;
|
|
403
|
+
const otherTopLevelKeys = Object.keys(existing).filter(
|
|
404
|
+
(k) => k !== 'version' && k !== 'vendor' && k !== 'configurePresets' && k !== 'buildPresets',
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
if (!hasSurvivingPresets && !hasForeignVendor && otherTopLevelKeys.length === 0) {
|
|
408
|
+
return { result: null, removed };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const result = { ...existing };
|
|
412
|
+
if (configurePresets.length > 0) result.configurePresets = configurePresets;
|
|
413
|
+
else delete result.configurePresets;
|
|
414
|
+
if (buildPresets.length > 0) result.buildPresets = buildPresets;
|
|
415
|
+
else delete result.buildPresets;
|
|
416
|
+
if (vendor !== undefined) result.vendor = vendor;
|
|
417
|
+
else delete result.vendor;
|
|
418
|
+
|
|
419
|
+
return { result, removed };
|
|
420
|
+
}
|
|
421
|
+
|
|
346
422
|
module.exports = {
|
|
347
423
|
generateCMakePresets,
|
|
424
|
+
stripWyvrnPresets,
|
|
425
|
+
safeReadJson,
|
|
348
426
|
// Exported for tests
|
|
349
427
|
isWyvrnGenerated,
|
|
350
428
|
buildConfigurePreset,
|