rman 2.21.0 → 2.21.1
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.
|
@@ -119,7 +119,9 @@ async function remove(file, dryRun) {
|
|
|
119
119
|
function pruneEmptyDirs(dir, includeSelf, dryRun) {
|
|
120
120
|
if (dryRun || !fs.existsSync(dir))
|
|
121
121
|
return;
|
|
122
|
-
|
|
122
|
+
/** Not through a link: a link to a directory is not a directory of this one to prune - removing
|
|
123
|
+
* it as one is `ENOTDIR`, and walking into it reaches somewhere else entirely. */
|
|
124
|
+
const entries = fg.sync('**', { cwd: dir, onlyDirectories: true, dot: true, followSymbolicLinks: false });
|
|
123
125
|
entries.sort((a, b) => b.split(path.sep).length - a.split(path.sep).length);
|
|
124
126
|
for (const rel of entries) {
|
|
125
127
|
const abs = path.join(dir, rel);
|
|
@@ -140,23 +142,48 @@ function pruneEmptyDirs(dir, includeSelf, dryRun) {
|
|
|
140
142
|
* matching files *inside* an otherwise-deleted directory, leaving the rest of it gone and the
|
|
141
143
|
* protected files (and their now non-empty parent) in place.
|
|
142
144
|
*/
|
|
145
|
+
/* **A symbolic link is removed as a link, and never followed.** A build directory routinely holds
|
|
146
|
+
* one - `build/node_modules/@scope/sibling` pointing at another package's own build - and walking
|
|
147
|
+
* through it deleted *that* package's output, then failed `rmdir` on the link with `ENOTDIR`.
|
|
148
|
+
* Measured on `panates/syncbridge`: cleaning `syncbuild` emptied `common/build`, and the run
|
|
149
|
+
* reported only `0 succeeded, 1 failed`. fast-glob follows links by default, so every glob here
|
|
150
|
+
* says `followSymbolicLinks: false`. */
|
|
143
151
|
async function cleanGlobs(dirname, include, exclude, dryRun) {
|
|
144
152
|
if (!include.length)
|
|
145
153
|
return [];
|
|
146
|
-
const matches = await fg(include, {
|
|
154
|
+
const matches = await fg(include, {
|
|
155
|
+
cwd: dirname,
|
|
156
|
+
ignore: exclude,
|
|
157
|
+
onlyFiles: false,
|
|
158
|
+
dot: true,
|
|
159
|
+
absolute: true,
|
|
160
|
+
followSymbolicLinks: false,
|
|
161
|
+
});
|
|
147
162
|
const protectedFiles = exclude.length
|
|
148
|
-
? new Set(await fg(exclude, { cwd: dirname, onlyFiles: true, dot: true, absolute: true }))
|
|
163
|
+
? new Set(await fg(exclude, { cwd: dirname, onlyFiles: true, dot: true, absolute: true, followSymbolicLinks: false }))
|
|
149
164
|
: new Set();
|
|
150
165
|
const removed = [];
|
|
151
166
|
for (const m of matches) {
|
|
152
|
-
|
|
167
|
+
/** `lstat`, not `stat`: a link is removed as the link it is, and never read as the thing it
|
|
168
|
+
* points at. */
|
|
169
|
+
const stat = await fs.promises.lstat(m).catch(() => undefined);
|
|
153
170
|
if (!stat)
|
|
154
171
|
continue;
|
|
155
172
|
if (stat.isDirectory()) {
|
|
156
|
-
|
|
157
|
-
|
|
173
|
+
/** Files *and* links inside, and a link never entered - see `cleanGlobs`' own note. */
|
|
174
|
+
const inside = await fg('**', {
|
|
175
|
+
cwd: m,
|
|
176
|
+
onlyFiles: false,
|
|
177
|
+
dot: true,
|
|
178
|
+
absolute: true,
|
|
179
|
+
followSymbolicLinks: false,
|
|
180
|
+
});
|
|
181
|
+
for (const f of inside) {
|
|
158
182
|
if (protectedFiles.has(f))
|
|
159
183
|
continue;
|
|
184
|
+
const entry = await fs.promises.lstat(f).catch(() => undefined);
|
|
185
|
+
if (!entry || entry.isDirectory())
|
|
186
|
+
continue;
|
|
160
187
|
await remove(f, dryRun);
|
|
161
188
|
removed.push(path.relative(dirname, f));
|
|
162
189
|
}
|
|
@@ -208,6 +235,8 @@ async function cleanTsArtifacts(pkg, dryRun) {
|
|
|
208
235
|
onlyFiles: true,
|
|
209
236
|
dot: true,
|
|
210
237
|
absolute: true,
|
|
238
|
+
/** A link to a directory elsewhere is not this package's source to sweep. */
|
|
239
|
+
followSymbolicLinks: false,
|
|
211
240
|
});
|
|
212
241
|
const removed = [];
|
|
213
242
|
for (const f of files) {
|
|
@@ -277,6 +306,7 @@ async function cleanTsBuildInfo(dirname, dryRun) {
|
|
|
277
306
|
onlyFiles: true,
|
|
278
307
|
dot: true,
|
|
279
308
|
absolute: true,
|
|
309
|
+
followSymbolicLinks: false,
|
|
280
310
|
});
|
|
281
311
|
for (const f of files)
|
|
282
312
|
await remove(f, dryRun);
|
|
@@ -310,7 +340,12 @@ async function cleanPackage(pkg, item, panelEnabled, dryRun, logger) {
|
|
|
310
340
|
}
|
|
311
341
|
catch (e) {
|
|
312
342
|
item.status = 'failed';
|
|
313
|
-
|
|
343
|
+
const message = e?.message ?? String(e);
|
|
344
|
+
item.log.push(message);
|
|
345
|
+
/** The panel replays `log` in its recap; with no panel nothing does, and the run said only
|
|
346
|
+
* `0 succeeded, 1 failed` - which is how the link bug above went unexplained. */
|
|
347
|
+
if (!panelEnabled)
|
|
348
|
+
logger.error(colors.red('clean'), colors.cyan(item.name), message);
|
|
314
349
|
throw e;
|
|
315
350
|
}
|
|
316
351
|
finally {
|
package/constants.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '2.21.
|
|
1
|
+
export const version = '2.21.1';
|