-
Notifications
You must be signed in to change notification settings - Fork 6
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
Add uninstall subcommand #112
base: main
Are you sure you want to change the base?
Conversation
…the same as prefix
@@ -110,7 +116,7 @@ def __call__(self, parser, namespace, values, option_string=None): | |||
f"Defaults to {DEFAULT_NUM_PROCESSORS}.", | |||
) | |||
|
|||
g = p.add_mutually_exclusive_group(required=True) | |||
g = p.add_mutually_exclusive_group() |
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.
We'd need to add checks to make this required manually if uninstall
is not in the ARGV. Maybe an else
clause in _constructor_main()
(line 602, I think?).
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 added that into the same function and created tests to check.
src/entry_point.py
Outdated
# See: https://github.com/conda/conda/blob/475e6acbdc98122fcbef4733eb8cb8689324c1c8/conda/gateways/disk/create.py#L482-L488 # noqa | ||
ENVS_DIR_MAGIC_FILE = ".conda_envs_dir_test" | ||
|
||
uninstall_prefix = Path(uninstall_dir).expanduser().resolve() |
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.
Do we need to os.path.expandvars
too or is that not expected?
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.
Good call. I added it to where we parse args.prefix
.
_remove_file_directory(parent) | ||
parent = parent.parent | ||
|
||
def _remove_config_file_and_parents(file: Path): |
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.
There's precedent for users not liking this default behaviour, see conda/conda#13113. Not sure if we should delete empty parents recursively 🤔 Maybe just the config-containing one? Happy to have my mind changed here, though, so let me know.
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.
That's what I was intending with rootdir
, but it was still a little too aggressive. I changed the selection of which directories to remove. The only thing that might be controversial is the ~/.config
directory. I think it's okay to delete if empty. I would at least argue that we should delete it on Windows where configs are stored in different places.
run_plan(plan) | ||
run_plan_elevated(plan) |
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.
Hm, these functions do not raise errors? They just write to plan
?
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.
As far as I know, that's correct. There doesn't seem to be a "failed" state in the Results
enum either.
I am printing the plan results at least so that users can check if they don't see the results they expect.
src/entry_point.py
Outdated
continue | ||
expected_files = [pkgs_dir / "urls", pkgs_dir / "urls.txt"] | ||
if all(file in expected_files for file in pkgs_dir.iterdir()): | ||
_remove_file_and_parents(pkgs_dir) |
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.
Same here about parents (and elsewhere), I don't think we need to recursively remove all parents.
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 can get on board for the package cache, but for the binstar directory and the notices cache, we should remove the parents:
- The anaconda-client token is either in a
$BINSTAR_TOKEN_DIR/data
or abinstar/ContinuumIO
(Windows) orbinstart
directory and is created byconda
viaanaconda-client
. I think we can remove the binstar directories at least. To be honest, the best solution would be to makeanaconda-client
a plug-in handle that directory when runningconda remove
. Right now, however,conda
is the API that's creating that directory, so I decided to handle it here. - The notices cache is written into a specific conda cache directory, so we will not remove a user-created directory. Unfortunately, the subdirectories are different for Windows and Unix, but they are all called
conda
. So, I changed it to_remove_config_file_and_parents
here.
README.md
Outdated
> This can cause files to be left behind. | ||
|
||
> [!WARNING] | ||
> Support for softlinks is still limited. |
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.
Can you elaborate? Are errors expected, or undefined behavior?
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.
We only unlink softlinks and do not do anything with the underlying files. I made that more explicit.
src/entry_point.py
Outdated
), | ||
) | ||
uninstall_subcommand.add_argument( | ||
"--remove-caches", |
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.
"--remove-caches", | |
"--remove-additional-caches", |
The term is a bit convoluted with pkgs/
and pkgs/cache
being taken care of in conda-clean
, but this argument targets ~/.conda
and anaconda-client
stuff.
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.
Yes, unfortunately, conda
overloads the term "cache". I changed it to --remove-conda-caches
because it's more descriptive than --remove-additional-caches
.
Description
Uninstalling distributions for
conda
currently only has incomplete solutions with files and broken auto-run/initializer scripts left behind (see conda/constructor#642, conda/constructor#588, and conda/constructor#572). A clean uninstallation is currently not available without manually running a series of commands that are still not 100% safe - e.g.,conda init --reverse --all
will remove initialization ofconda
, no matter what installation it is pointed to.This PR introduces a cross-platform uninstallation subcommand that uses conda-internal features to perform the following tasks:
envs_dirs
). This uninstalls all shortcuts, performs pre-unlink actions, and de-registers the environments from theenvironments.txt
file.conda init reverse
if a configuration file points to any of the environments that are removed..condarc
files, either for the user, system-wide, or all.conda clean --all
to remove package caches outside the installation directory..conda
file.This is implemented in
conda-standalone
so that a single binary can be used on macOS and Linux. It also allows decoupling of the installer "front end" (built byconstructor
) from the installer "back end" (such asconda-standalone
ormicromamba
), as outlined in the following issue: conda/constructor#549Known gap: Soft links are currently not well supported: files are unlinked, but no efforts are made to ensure that this doesn't leave unused files behind.
Checklist - did you ...
news
directory (using the template) for the next release's release notes?