-
-
Notifications
You must be signed in to change notification settings - Fork 86
feat: add --keep-one flag to nh clean for direnv gcroots #511
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
base: master
Are you sure you want to change the base?
Conversation
The `nh clean` command now supports a `--keep-one` flag. When specified, this flag ensures the gcroot for each `direnv` project is preserved. This overrides the `--keep-since` flag for direnv roots. nix-community#469
WalkthroughThe changes introduce a new Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
CHANGELOG.md (1)
21-21: Align--keep-onechangelog description with actual behaviorThe entry says “keeps one gcroot per direnv project”, but the current implementation in
src/clean.rseffectively keeps all direnv gcroots whenever--keep-oneis set (no “at most one per project” enforcement). Either:
- change the implementation to actually enforce “one per project”, or
- reword this line (and other docs) to describe the real behavior (e.g. that direnv gcroots are exempt from time-based culling when
--keep-oneis used).src/interface.rs (1)
440-443: Clarifykeep_onehelp text to match implemented semanticsThe doc says “Keep at least one gcroot per direnv project”, but the logic in
clean.rscurrently:
- skips time-based culling entirely for all direnv gcroots when
keep_oneis true, rather than enforcing “one per project”, and- effectively makes
keep_sincenot apply to direnv roots.Once you settle on the intended behavior, please update this comment to be explicit, e.g.:
- If you really want “one per project”: mention that older direnv gcroots for the same project may be deleted, but one (typically the newest) is always kept and
--keep-sinceis ignored for those.- If you actually want “never delete direnv gcroots when this is set”: rephrase to something like “Do not delete direnv gcroots (ignores
--keep-sincefor direnv roots)”.This keeps
--helpoutput honest and avoids surprising users.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
CHANGELOG.md(1 hunks)src/clean.rs(2 hunks)src/interface.rs(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/interface.rs (1)
src/commands.rs (1)
arg(248-251)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test NH on Linux
- GitHub Check: Test NH on Darwin
- GitHub Check: treewide-checks
- GitHub Check: Build NH on Linux
- GitHub Check: Build NH on Darwin
| let is_direnv_root = DIRENV_REGEX.is_match(&dst.to_string_lossy()); | ||
| if args.keep_one && is_direnv_root { | ||
| // If --keep-one is specified AND this is a direnv gcroot, always | ||
| // keep it. | ||
| gcroots_tagged.insert(dst, false); | ||
| } else { | ||
| // For other gcroots (non-direnv, or if --keep-one is not | ||
| // specified), apply time-based culling. | ||
| let dur = now.duration_since( | ||
| dst | ||
| .symlink_metadata() | ||
| .wrap_err("Reading gcroot metadata")? | ||
| .modified()?, | ||
| ); | ||
| debug!(?dur); | ||
| match dur { | ||
| Err(err) => { | ||
| warn!(?err, ?now, "Failed to compare time!"); | ||
| }, | ||
| Ok(val) if val <= args.keep_since.into() => { | ||
| gcroots_tagged.insert(dst, false); | ||
| }, | ||
| Ok(_) => { | ||
| gcroots_tagged.insert(dst, true); | ||
| }, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--keep-one currently keeps all direnv gcroots, not “one per project”
Right now the logic is:
let is_direnv_root = DIRENV_REGEX.is_match(&dst.to_string_lossy());
if args.keep_one && is_direnv_root {
gcroots_tagged.insert(dst, false); // always keep
} else {
// time-based culling using args.keep_since
}Implications:
- For any direnv gcroot, once
--keep-oneis set, it is never marked for deletion, regardless of age. - There is no grouping by “direnv project”, and no pass that ensures “at most one gcroot per project” survives. This conflicts with:
- the field/changelog wording (“keeps one gcroot per direnv project”), and
- the PR description about matching Nix’s behavior of keeping only the latest per project.
If the intended behavior is indeed “keep exactly (or at least) one gcroot per direnv project while ignoring --keep-since for those roots”, you’ll need something like:
- First pass over
/nix/var/nix/gcroots/auto:- collect direnv gcroots into a structure keyed by some project identifier (e.g. path portion up to
/.direnv/or another stable grouping criterion), - collect non-direnv gcroots as you do today.
- collect direnv gcroots into a structure keyed by some project identifier (e.g. path portion up to
- Second pass:
- for each direnv project, keep the newest (by
symlink_metadata().modified()) and mark older onestrue(to be removed), - for non-direnv roots, keep using the existing
keep_sincelogic.
- for each direnv project, keep the newest (by
As-is, users enabling --keep-one will get behavior closer to “never prune direnv gcroots”, which may lead to unbounded accumulation and doesn’t match the documented semantics.
Related UX point:
- The output
println!("Keeping one gcroot per direnv project");
println!("Keeping paths newer than {}", args.keep_since);is also misleading under current behavior:
- direnv gcroots older than
keep_sinceare still kept with--keep-one, - and more than one gcroot per project is kept.
Once semantics are settled, consider rephrasing these lines to describe exactly what is happening (e.g. “Direnv gcroots are exempt from time-based cleanup” or “Keeping only the newest direnv gcroot per project, regardless of age”).
Finally, tiny optional nit: you call dst.to_string_lossy() twice (once for the regexes filter above and once here). Caching it in a let dst_str = dst.to_string_lossy(); and reusing it would avoid repeated allocations, though this is a micro-optimization.
Also applies to: 285-287
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe only the latest gcroot is kept for direnv? My understand of direnv is that each project keeps a since "latest" gcroot, so what is mentioned above would not apply. I could be wrong, so I guess, if someone knowledgeable (I guess, human) can weigh in for a definitive answer.
I feel meh about the other suggestions, but happy to make them if asked.
|
Hi, sorry for the late response. Just wanted to let you know that I have not forgotten about this PR, but have not been in the right state of mind to provide you with a proper review---especially given that I'm not that up to date with Direnv's specifics. Once I get my remote-build-stuff PR done, I'll investigate Direnv and hopefully give you a non-AI review. For the record, AI is there mostly to catch those very rare "oh shoot, I forgot" issues and you are free to disregard it's (usually hallucinated) comments if you are confident. |
The
nh cleancommand now supports a--keep-oneflag. When specified, this flag ensures the gcroot for eachdirenvproject is preserved. This overrides the--keep-sinceflag for direnv roots.I believe this should address #469 too, though I did make the change according to my needs (which is keeping the latest per project, which if I understand correctly, is what is tracked in
/nix/var/nix/gcroots/autofor direnv gcroots).Also, first time writing rust and first time contributor. Went for it as the change did seem straightforward enough (if I understood things correctly). Happy to make any changes as directed.
Sanity Checking
nix fmtto format my Nix codecargo fmtto format my Rust codecargo clippyand fixed any new linter warnings.logic
description.
x86_64-linuxaarch64-linuxx86_64-darwinaarch64-darwinAdd a 👍 reaction to pull requests you find important.
Summary by CodeRabbit
--keep-oneflag tonh cleancommand to preserve one garbage collection root per direnv project.✏️ Tip: You can customize this high-level summary in your review settings.