Skip to content

chore: replace rimraf with native fs.rm() - #2631

Open
Roli Bosch (roli-lpci) wants to merge 1 commit into
Azure:mainfrom
roli-lpci:chore/replace-rimraf-with-native-fs-rm
Open

chore: replace rimraf with native fs.rm()#2631
Roli Bosch (roli-lpci) wants to merge 1 commit into
Azure:mainfrom
roli-lpci:chore/replace-rimraf-with-native-fs-rm

Conversation

@roli-lpci

@roli-lpci Roli Bosch (roli-lpci) commented Feb 25, 2026

Copy link
Copy Markdown

Summary

  • Replace the rimraf dependency with Node.js built-in fs.rm() / fs.rmSync() with { recursive: true, force: true }
  • Remove rimraf and @types/rimraf from package.json (and the rimraf entry from tsconfig.json's types list)
  • Add scripts/clean.js to replace the rimraf CLI usage in the clean / clean:deep npm scripts
  • Add a ChangeLog.md entry under Upcoming Release

Impact

Dependency hygiene. rimraf@3.0.2 pins a deprecated dependency chain that npm warns about on every install: rimraf ("Rimraf versions prior to v4 are no longer supported"), glob@7 ("Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version"), and inflight ("This module is not supported, and leaks memory"). Removing rimraf eliminates that entire chain — 12 entries drop out of package-lock.json, and rimraf/glob@7 no longer appear anywhere in the tree, including transitively.

Runtime behavior parity. rimrafAsync in src/common/utils/utils.ts keeps its exported name and signature, so no call sites change (persistence cleanup in the blob/queue/table Loki stores and FSExtentStore). Like rimraf, fs.rm with force: true succeeds when the target is already missing, and maxRetries: 10 on Windows replaces rimraf's built-in retry behavior for transient EPERM/EBUSY on locked files. Error semantics are unchanged: failures still reject, as the promisified rimraf did.

Test helper parity. rmRecursive in tests/testutils.ts previously swallowed every cleanup error; the earlier revision of this PR made it reject on non-ENOENT errors — the likely cause of the four Windows CI job failures (Blob/Queue teardown hitting transient EPERM/EBUSY on locked files). It now retries on Windows and then swallows any remaining error, restoring the original non-flaky teardown behavior.

Clean script parity. scripts/clean.js removes exactly the targets the old rimraf CLI invocation listed (dist typings *.log coverage __testspersistence__ temp __testsstorage__ .nyc_output debug.log *.vsix *.tgz, plus __* for clean:deep), using fs.rmSync with maxRetries/retryDelay so locked files on Windows are retried rather than left behind.

Node compatibility. fs.rm requires Node ≥ 14.14.0; main's engines.node is now >= 22.0.0, so it is guaranteed on every supported version. This resolves the two review comments about the old >= 10.0.0 engines constraint.

Changes since the previous revision

  • Rebased onto current main (resolves the merge conflict)
  • Addressed the Windows CI failures: test cleanup no longer rejects on transient errors (see above; a CI rerun will confirm)
  • Added maxRetries/retryDelay to scripts/clean.js
  • Removed the now-stale rimraf entry from tsconfig.json's types array (new on main since the original revision; the build fails without this)

Test plan

  • npm run build, npm run lint — pass
  • npm run test:blob, npm run test:queue, npm run test:package-scripts — pass locally (macOS); these exercise rmRecursive teardown on every suite
  • npm run clean / npm run clean:deep verified to delete every target of the old rimraf commands
  • grep -rn rimraf src/ tests/ scripts/ package.json package-lock.json → no dependency or import references remain

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes direct usage of the rimraf package and replaces recursive deletions with Node’s built-in fs.rm() / fs.rmSync() to support dependency cleanup and simplify maintenance.

Changes:

  • Replaced rimraf usage in test utilities and common runtime utilities with fs.promises.rm(...).
  • Added a custom scripts/clean.js to replace the rimraf CLI usage in npm scripts.
  • Removed rimraf and @types/rimraf from package.json.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/testutils.ts Switched test cleanup helper from rimraf to fs.promises.rm.
src/common/utils/utils.ts Replaced rimrafAsync implementation with fs.promises.rm.
scripts/clean.js Introduced a Node-based clean script to replace the rimraf CLI.
package.json Dropped rimraf deps and updated clean scripts to use scripts/clean.js.
package-lock.json Lockfile updated to reflect dependency removal and transitive changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/testutils.ts Outdated
Comment on lines 51 to 55
await fsPromises.rm(path, {
recursive: true,
force: true,
maxRetries: process.platform === "win32" ? 10 : 0
});

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — rmRecursive now wraps fs.promises.rm in try/catch and resolves regardless, with maxRetries: 10 on Windows first. This restores the previous non-flaky teardown behavior and should resolve the four Windows CI failures.

Comment thread src/common/utils/utils.ts
Comment on lines +12 to +16
await fsPromises.rm(path, {
recursive: true,
force: true,
maxRetries: process.platform === "win32" ? 10 : 0
});

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved by main's engines bump: engines.node is now >= 22.0.0, so fs.promises.rm is guaranteed on every supported version. No fallback needed.

Comment thread scripts/clean.js
Comment on lines +19 to +26
function rm(target) {
try {
fs.rmSync(target, { recursive: true, force: true });
} catch (err) {
if (err.code !== "ENOENT") {
console.error(`Warning: could not remove ${target}: ${err.message}`);
}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — the fs.rmSync call now passes maxRetries: 10, retryDelay: 100, matching the retry behavior of the other fs.rm usages in this PR.

Comment thread package.json
Comment on lines +324 to +325
"clean": "node scripts/clean.js",
"clean:deep": "node scripts/clean.js --deep",

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved — main has since moved engines.node to >= 22.0.0, which this rebase picks up, so the constraint already guarantees fs.rm support.

@md84419

Copy link
Copy Markdown

Roli Bosch (@roli-lpci)

  • 4 checks are failing on Windows platforms - maybe related to the copilot findings above?
  • please can this be tagged as 3.37.0 so it can be considered for inclusion in the next build?

@jainakanksha-msft

Copy link
Copy Markdown
Member

Michael Davey (@md84419), let's address all the issues in the Pull request first.
Please also add a detailed explanation of the impact this change will have in the PR description.

@md84419

Copy link
Copy Markdown

Akanksha Jain (@jainakanksha-msft) rimraf 3.0.2 is a security vulnerability.

  • it depends on vulnerable glob@7.2.3
  • which depends on vulnerable minimatch@3.1.5
  • which depends on vulnerable brace-expansion ≤ 5.0.7 (HIGH, GHSA-mh99-v99m-4gvg)

Eliminating the rimraf dependency removes that whole depdency chain and I assume the original author decided it was simpler and more maintainable than getting the code working with the latest rimraf.

I guess the bigger question is, what would it take to release a version of azurite with 0 vulnerabilities?

  • Eliminating rimraf and bumping @opentelementry/core and application insights (Bump @opentelemetry/core and applicationinsights #2660) gets one a long way there. The project also needs moment ^2.30.1 and needs a verison of sequelize and @azure/ms-rest-js with uuid ^11.1.1. Then its just a case of bumping the minor and patch versions on everything else.

@jainakanksha-msft

Copy link
Copy Markdown
Member

Michael Davey (@md84419)\ Roli Bosch (@roli-lpci), could you please refresh the PR with main, and address all the issues.

Replaces the rimraf dependency with Node.js built-in fs.rm()/fs.rmSync():

- src/common/utils/utils.ts: rimrafAsync now wraps fs.promises.rm with
  recursive/force and Windows retry handling
- tests/testutils.ts: rmRecursive uses fs.promises.rm, swallowing cleanup
  errors to keep test teardown non-flaky (matches prior rimraf behavior)
- scripts/clean.js: Node-based clean script replacing the rimraf CLI,
  with maxRetries/retryDelay for locked files on Windows
- package.json / tsconfig.json: drop rimraf and @types/rimraf
- package-lock.json: regenerated; rimraf's deprecated dependency chain
  (glob@7 -> minimatch@3 -> inflight) is fully removed
- ChangeLog.md: upcoming-release entry

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 7, 2026 19:07
@roli-lpci
Roli Bosch (roli-lpci) force-pushed the chore/replace-rimraf-with-native-fs-rm branch from d988db2 to 4e76c48 Compare August 7, 2026 19:07
@roli-lpci

Copy link
Copy Markdown
Author

Akanksha Jain (@jainakanksha-msft) Done — the branch is refreshed on current main and all four review findings are addressed: rmRecursive now retries and then swallows transient cleanup errors, matching the old rimraf behavior (this should resolve the four Windows CI failures once CI reruns), and scripts/clean.js now retries locked files via maxRetries/retryDelay. The two comments about the Node >= 10.0.0 engines constraint are resolved by main itself — engines.node is now >= 22.0.0, so fs.rm is available on every supported version. The PR description has been updated with a detailed impact section (motivation, behavior parity, Node compatibility).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 7 changed files in this pull request and generated no new comments.

Suppressed comments (2)

scripts/clean.js:56

  • rmGlob("*.ext") currently deletes dotfiles like .foo.log because it matches by endsWith(ext). rimraf/glob-style *.log patterns typically do not match dotfiles by default, so this can delete more than the old command (and conflicts with the stated “parity”). Consider skipping entries starting with . to better match glob semantics.
  for (const entry of entries) {
    if (entry.endsWith(ext)) {
      rm(path.join(rootDir, entry));
    }
  }

scripts/clean.js:32

  • If fs.rmSync fails after retries, the script currently only logs a warning and still exits with status 0. That can mask failures in CI or local workflows that rely on npm run clean to guarantee a pristine workspace. Consider setting a non-zero exit code when a removal fails (while still continuing to attempt other targets).
  } catch (err) {
    if (err.code !== "ENOENT") {
      console.error(`Warning: could not remove ${target}: ${err.message}`);
    }
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants