Skip to content

feat(clean): deno clean --except <paths>, remove all cache data except what's needed to run paths #28424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 46 commits into from
Apr 29, 2025

Conversation

nathanwhit
Copy link
Member

@nathanwhit nathanwhit commented Mar 7, 2025

Closes #27229.

TODO:

  • Tests
  • Make some changes to deno_cache_dir so we can get the paths for the local http cache
  • Right now this leaves the node modules setup cache in an incorrect state (removes the symlinks, but doesn't update the setup cache)
  • Handle code cache and other sqlite caches?

@bartlomieju bartlomieju added this to the 2.3.0 milestone Apr 22, 2025
@nathanwhit nathanwhit requested review from dsherret and Copilot April 22, 2025 16:07
@nathanwhit nathanwhit requested a review from bartlomieju April 22, 2025 16:07
Copy link
Contributor

@Copilot Copilot AI left a comment

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 adds support for an entrypoint-based clean subcommand in Deno by extending the clean functionality to accept new clean flags. Key changes include:

  • Modifying the Clean subcommand in cli/main.rs to accept a CleanFlags argument and await the clean operation.
  • Introducing a new CleanFlags struct and updating the clean_parse function in cli/args/flags.rs to extract flags for the clean command.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
cli/main.rs Updates the Clean subcommand to forward clean_flags and await the clean function.
cli/args/flags.rs Adds the CleanFlags struct and updates the clean_parse function to extract and handle new flags.

@nathanwhit nathanwhit marked this pull request as ready for review April 24, 2025 21:41
@nathanwhit nathanwhit force-pushed the clean-entrypoint-take2 branch from 4cabc70 to 903fe38 Compare April 28, 2025 23:12
@nathanwhit nathanwhit force-pushed the clean-entrypoint-take2 branch from 2af5b12 to eedebb9 Compare April 29, 2025 00:19
@nathanwhit nathanwhit force-pushed the clean-entrypoint-take2 branch from 697096e to 2dd71a8 Compare April 29, 2025 15:45
@nathanwhit nathanwhit force-pushed the clean-entrypoint-take2 branch from 2dd71a8 to 7d66ed3 Compare April 29, 2025 15:53

let npm_cache = factory.npm_cache()?;
let snap = npm_resolver.as_managed().unwrap().resolution().snapshot();
// TODO(nathanwhit): remove once we don't need packuments for creating the snapshot from lockfile
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this the case with lockfile v5?

Copy link
Member Author

@nathanwhit nathanwhit Apr 29, 2025

Choose a reason for hiding this comment

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

yes, but we don't know if it's actually lockfile v5. they could still be using v4

we could maybe add an API to deno_lockfile to get the version of the original lockfile text, but there isn't one atm

Copy link
Member

Choose a reason for hiding this comment

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

Ah, good point 👍 let's keep it for now

Copy link
Member

Choose a reason for hiding this comment

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

Oh, would be sweet to add this. I think it would help netlify too.

.map(deno_resolver::npm::get_package_folder_id_folder_name)
.collect::<HashSet<_>>();

// TODO(nathanwhit): this probably shouldn't reach directly into this code
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand what's the problem here - is this touching some internal implementation details?

Copy link
Member Author

Choose a reason for hiding this comment

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

sort of. it's just everywhere else in the code we expose more generic methods on e.g. NpmInstaller. This just uses some of the code from the local installer directly. It's not incorrect, but it feels sort of wrong to me.

Copy link
Member

Choose a reason for hiding this comment

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

We could expose it as a public API down the road?

Copy link
Member Author

@nathanwhit nathanwhit Apr 29, 2025

Choose a reason for hiding this comment

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

yeah that's pretty much what the todo is, just to make/use a more public api

@nathanwhit nathanwhit force-pushed the clean-entrypoint-take2 branch from 5c8e5d9 to 696f457 Compare April 29, 2025 21:03
@@ -12,6 +12,7 @@ use deno_npm::NpmSystemInfo;
use deno_resolver::npm::managed::NpmResolutionCell;
use deno_runtime::colors;
use deno_semver::package::PackageReq;
pub(crate) use local::SetupCache;
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick because this is the cli crate:

Suggested change
pub(crate) use local::SetupCache;
pub use local::SetupCache;

Comment on lines 107 to 111
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Found {
Match,
Prefix,
}
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: Maybe move this about PathTree so that the PathTrie struct w/ impl is beside each other?

.build_graph_with_npm_resolution(
graph,
CreateGraphOptions {
// loader: Some(&mut NoLoader),
Copy link
Member

Choose a reason for hiding this comment

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

Remove?


let npm_cache = factory.npm_cache()?;
let snap = npm_resolver.as_managed().unwrap().resolution().snapshot();
// TODO(nathanwhit): remove once we don't need packuments for creating the snapshot from lockfile
Copy link
Member

Choose a reason for hiding this comment

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

Oh, would be sweet to add this. I think it would help netlify too.

continue;
}
if !entry.path().starts_with(base) {
panic!("VERY BAD");
Copy link
Member

Choose a reason for hiding this comment

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

Better message? 😄

if dry_run {
#[allow(clippy::print_stderr)]
{
eprintln!("would remove dir: {}", entry.path().display());
Copy link
Member

Choose a reason for hiding this comment

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

Should we use a tree view here similar to deno compile/info's output? This prefix text for each entry seems a little repetitive btw. Maybe we should just list the paths?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll just list the paths for now

Comment on lines +625 to +655
if meta.is_symlink() {
std::fs::remove_dir(path).with_context(|| {
format!("Failed to remove symlink: {}", path.display())
})?;
return Ok(());
}
Copy link
Member

Choose a reason for hiding this comment

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

Does remove_dir work for file symlinks on windows?

Copy link
Member Author

Choose a reason for hiding this comment

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

nope, only directory symlinks AFAICT. that's why it tries remove_file, and falls back to remove_dir if that failed

dry_run: bool,
) -> Result<(), AnyError> {
if !dir.ends_with("node_modules") || !dir.is_dir() {
bail!("not a node_modules directory");
Copy link
Member

Choose a reason for hiding this comment

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

Include the path in the error message?

Comment on lines +490 to +504
if !base.exists() {
return Ok(());
}
Copy link
Member

Choose a reason for hiding this comment

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

Not like it will ever happen, but perhaps we should handle this in the std::fs::read_dir(&base)? error case and also handle when ErrorKind::NotADirectory.

Comment on lines 514 to 552
} else if dry_run {
#[allow(clippy::print_stderr)]
{
eprintln!(
"would remove dir from node modules: {}",
entry.path().display()
);
}
} else {
rm_rf(state, &entry.path())?;
}
Copy link
Member

Choose a reason for hiding this comment

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

Could use strategy pattern here in the future (reporting strategy and remove strategy).

@nathanwhit nathanwhit force-pushed the clean-entrypoint-take2 branch from 8c030e3 to 6f5433f Compare April 29, 2025 21:57
Copy link
Member

@dsherret dsherret left a comment

Choose a reason for hiding this comment

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

LGTM!

@nathanwhit nathanwhit enabled auto-merge (squash) April 29, 2025 22:09
@nathanwhit nathanwhit merged commit 6d00354 into denoland:main Apr 29, 2025
18 checks passed
@nathanwhit nathanwhit deleted the clean-entrypoint-take2 branch April 29, 2025 22:50
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.

deno clean -e / deno clean --prod
3 participants