|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import sys |
| 4 | +from pathlib import Path |
3 | 5 | from textwrap import dedent |
4 | 6 | from typing import TYPE_CHECKING |
5 | 7 |
|
6 | 8 | if TYPE_CHECKING: |
7 | 9 | import argparse |
| 10 | + from typing import TypedDict |
| 11 | + |
| 12 | + class SnapshotData(TypedDict): |
| 13 | + file_path: Path |
| 14 | + snapshot_name: str |
| 15 | + |
8 | 16 |
|
9 | 17 | HELP = "Reset 'base' environment to essential packages only." |
| 18 | +SNAPSHOT_HELP = dedent( |
| 19 | + """ |
| 20 | + Snapshot to reset the `base` environment to. |
| 21 | + `snapshot` removes all packages except for `conda`, its plugins, |
| 22 | + and their dependencies. |
| 23 | + `installer` resets the `base` environment to the snapshot provided |
| 24 | + by the installer. |
| 25 | + `migrate` resets the `base` environment to the snapshot after the last |
| 26 | + `conda migrate` command run. |
| 27 | +
|
| 28 | + If not set, `conda self` will try to reset to the post-migration snapshot first, |
| 29 | + then to the installer-provided, and finally to the current snapshot. |
| 30 | + """ |
| 31 | +).lstrip() |
10 | 32 |
|
11 | 33 | WHAT_TO_EXPECT = dedent( |
12 | 34 | """ |
|
20 | 42 | Reset the `base` environment to only the essential packages and plugins. |
21 | 43 | """ |
22 | 44 | ).lstrip() |
| 45 | +SUCCESS_SNAPSHOT = dedent( |
| 46 | + """ |
| 47 | + SUCCESS! |
| 48 | + Reset the `base` environment to {snapshot_name} snapshot. |
| 49 | + """ |
| 50 | +).lstrip() |
23 | 51 |
|
24 | 52 |
|
25 | 53 | def configure_parser(parser: argparse.ArgumentParser) -> None: |
26 | 54 | from conda.cli.helpers import add_output_and_prompt_options |
27 | 55 |
|
28 | 56 | parser.description = HELP |
29 | 57 | add_output_and_prompt_options(parser) |
| 58 | + parser.add_argument( |
| 59 | + "--snapshot", |
| 60 | + choices=("current", "installer", "migrate"), |
| 61 | + help=SNAPSHOT_HELP, |
| 62 | + ) |
30 | 63 | parser.set_defaults(func=execute) |
31 | 64 |
|
32 | 65 |
|
33 | 66 | def execute(args: argparse.Namespace) -> int: |
34 | 67 | from conda.base.context import context |
35 | 68 | from conda.reporters import confirm_yn |
36 | 69 |
|
| 70 | + from ..constants import RESET_FILE_INSTALLER, RESET_FILE_MIGRATE |
37 | 71 | from ..query import permanent_dependencies |
38 | 72 | from ..reset import reset |
39 | 73 |
|
40 | 74 | if not context.quiet: |
41 | 75 | print(WHAT_TO_EXPECT) |
42 | 76 |
|
43 | | - confirm_yn( |
44 | | - "Proceed with resetting your 'base' environment?[y/n]:\n", |
45 | | - default="no", |
46 | | - dry_run=context.dry_run, |
47 | | - ) |
| 77 | + reset_data: dict[str, SnapshotData] = { |
| 78 | + "installer": { |
| 79 | + "file_path": Path(sys.prefix, "conda-meta", RESET_FILE_INSTALLER), |
| 80 | + "snapshot_name": "installer-provided", |
| 81 | + }, |
| 82 | + "migrate": { |
| 83 | + "file_path": Path(sys.prefix, "conda-meta", RESET_FILE_MIGRATE), |
| 84 | + "snapshot_name": "post-migration", |
| 85 | + }, |
| 86 | + } |
| 87 | + |
| 88 | + reset_file: Path | None = None |
| 89 | + snapshot_name = "" |
| 90 | + if not args.snapshot: |
| 91 | + for snapshot in ("migrate", "installer"): |
| 92 | + snapshot_data = reset_data[snapshot] |
| 93 | + if not snapshot_data["file_path"].exists(): |
| 94 | + continue |
| 95 | + reset_file = snapshot_data["file_path"] |
| 96 | + snapshot_name = snapshot_data["snapshot_name"] |
| 97 | + break |
| 98 | + elif args.snapshot in reset_data: |
| 99 | + reset_file = reset_data[args.snapshot]["file_path"] |
| 100 | + snapshot_name = reset_data[args.snapshot]["snapshot_name"] |
| 101 | + |
| 102 | + if reset_file and not reset_file.exists(): |
| 103 | + raise FileNotFoundError( |
| 104 | + f"Failed to reset to `{args.snapshot}`.\n" |
| 105 | + f"Required file {reset_file} not found." |
| 106 | + ) |
| 107 | + |
| 108 | + prompt = "Proceed with resetting your 'base' environment" |
| 109 | + if snapshot_name: |
| 110 | + prompt += f" to the {snapshot_name} snapshot" |
| 111 | + confirm_yn(f"{prompt}?[y/n]:\n", default="no", dry_run=context.dry_run) |
48 | 112 |
|
49 | 113 | if not context.quiet: |
50 | 114 | print("Resetting 'base' environment...") |
51 | | - uninstallable_packages = permanent_dependencies() |
52 | | - reset(uninstallable_packages=uninstallable_packages) |
| 115 | + uninstallable_packages = permanent_dependencies() if not reset_file else set() |
| 116 | + reset(uninstallable_packages=uninstallable_packages, snapshot=reset_file) |
53 | 117 |
|
54 | 118 | if not context.quiet: |
55 | | - print(SUCCESS) |
| 119 | + if snapshot_name: |
| 120 | + print(SUCCESS_SNAPSHOT.format(snapshot_name=snapshot_name)) |
| 121 | + else: |
| 122 | + print(SUCCESS) |
56 | 123 |
|
57 | 124 | return 0 |
0 commit comments