ruyi 0.44.0b20251219__py3-none-any.whl → 0.45.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ruyi/__main__.py +16 -4
- ruyi/cli/cmd.py +6 -5
- ruyi/cli/config_cli.py +14 -11
- ruyi/cli/main.py +14 -4
- ruyi/cli/oobe.py +7 -3
- ruyi/cli/self_cli.py +48 -34
- ruyi/cli/user_input.py +42 -12
- ruyi/cli/version_cli.py +11 -5
- ruyi/config/__init__.py +26 -2
- ruyi/config/errors.py +19 -7
- ruyi/device/provision.py +116 -55
- ruyi/device/provision_cli.py +6 -3
- ruyi/i18n/__init__.py +129 -0
- ruyi/log/__init__.py +6 -5
- ruyi/mux/runtime.py +19 -6
- ruyi/mux/venv/maker.py +93 -35
- ruyi/mux/venv/venv_cli.py +13 -10
- ruyi/pluginhost/plugin_cli.py +4 -3
- ruyi/resource_bundle/__init__.py +22 -8
- ruyi/resource_bundle/__main__.py +6 -5
- ruyi/resource_bundle/data.py +13 -9
- ruyi/ruyipkg/admin_checksum.py +4 -1
- ruyi/ruyipkg/admin_cli.py +9 -6
- ruyi/ruyipkg/augmented_pkg.py +15 -14
- ruyi/ruyipkg/checksum.py +8 -2
- ruyi/ruyipkg/distfile.py +33 -9
- ruyi/ruyipkg/entity.py +12 -2
- ruyi/ruyipkg/entity_cli.py +20 -12
- ruyi/ruyipkg/entity_provider.py +11 -2
- ruyi/ruyipkg/fetcher.py +38 -9
- ruyi/ruyipkg/install.py +143 -42
- ruyi/ruyipkg/install_cli.py +18 -15
- ruyi/ruyipkg/list.py +27 -20
- ruyi/ruyipkg/list_cli.py +12 -7
- ruyi/ruyipkg/news.py +23 -11
- ruyi/ruyipkg/news_cli.py +10 -7
- ruyi/ruyipkg/profile_cli.py +8 -2
- ruyi/ruyipkg/repo.py +22 -8
- ruyi/ruyipkg/unpack.py +42 -8
- ruyi/ruyipkg/unpack_method.py +5 -1
- ruyi/ruyipkg/update_cli.py +8 -3
- ruyi/telemetry/provider.py +74 -29
- ruyi/telemetry/telemetry_cli.py +9 -8
- ruyi/utils/git.py +18 -11
- ruyi/utils/prereqs.py +10 -5
- ruyi/utils/ssl_patch.py +2 -1
- ruyi/version.py +9 -3
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/METADATA +2 -1
- ruyi-0.45.0.dist-info/RECORD +103 -0
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/WHEEL +1 -1
- ruyi-0.44.0b20251219.dist-info/RECORD +0 -102
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/entry_points.txt +0 -0
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/licenses/LICENSE-Apache.txt +0 -0
ruyi/__main__.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import pathlib
|
|
4
5
|
import sys
|
|
5
6
|
|
|
6
7
|
import ruyi
|
|
8
|
+
from ruyi.i18n import _, ADAPTER
|
|
7
9
|
from ruyi.utils.ci import is_running_in_ci
|
|
8
10
|
from ruyi.utils.global_mode import (
|
|
9
11
|
EnvGlobalModeProvider,
|
|
@@ -41,6 +43,8 @@ def _is_allowed_to_run_as_root() -> bool:
|
|
|
41
43
|
|
|
42
44
|
def entrypoint() -> None:
|
|
43
45
|
gm = EnvGlobalModeProvider(os.environ, sys.argv)
|
|
46
|
+
ADAPTER.init_from_env(os.environ)
|
|
47
|
+
ADAPTER.hook()
|
|
44
48
|
|
|
45
49
|
# NOTE: import of `ruyi.log` takes ~90ms on my machine, so initialization
|
|
46
50
|
# of logging is deferred as late as possible
|
|
@@ -50,11 +54,16 @@ def entrypoint() -> None:
|
|
|
50
54
|
|
|
51
55
|
logger = RuyiConsoleLogger(gm)
|
|
52
56
|
|
|
53
|
-
logger.F("refusing to run as super user outside CI without explicit consent")
|
|
57
|
+
logger.F(_("refusing to run as super user outside CI without explicit consent"))
|
|
54
58
|
|
|
55
59
|
choices = ", ".join(f"'{x}'" for x in TRUTHY_ENV_VAR_VALUES)
|
|
56
60
|
logger.I(
|
|
57
|
-
|
|
61
|
+
_(
|
|
62
|
+
"re-run with environment variable [yellow]{env_var}[/] set to one of [yellow]{choices}[/] to signify consent"
|
|
63
|
+
).format(
|
|
64
|
+
env_var=ENV_FORCE_ALLOW_ROOT,
|
|
65
|
+
choices=choices,
|
|
66
|
+
)
|
|
58
67
|
)
|
|
59
68
|
sys.exit(1)
|
|
60
69
|
|
|
@@ -63,7 +72,7 @@ def entrypoint() -> None:
|
|
|
63
72
|
|
|
64
73
|
logger = RuyiConsoleLogger(gm)
|
|
65
74
|
|
|
66
|
-
logger.F("no argv?")
|
|
75
|
+
logger.F(_("no argv?"))
|
|
67
76
|
sys.exit(1)
|
|
68
77
|
|
|
69
78
|
if gm.is_packaged and ruyi.__compiled__.standalone:
|
|
@@ -85,8 +94,11 @@ def entrypoint() -> None:
|
|
|
85
94
|
#
|
|
86
95
|
# we assume the one-file build if Nuitka is detected; sys.argv[0] does NOT
|
|
87
96
|
# work if it's just `ruyi` so we have to check our parent process in that case
|
|
88
|
-
self_exe = get_nuitka_self_exe() if gm.is_packaged else __file__
|
|
89
97
|
sys.argv[0] = get_argv0()
|
|
98
|
+
if gm.is_packaged:
|
|
99
|
+
self_exe = get_nuitka_self_exe()
|
|
100
|
+
else:
|
|
101
|
+
self_exe = str(pathlib.Path(sys.argv[0]).resolve())
|
|
90
102
|
gm.record_self_exe(sys.argv[0], __file__, self_exe)
|
|
91
103
|
|
|
92
104
|
from ruyi.config import GlobalConfig
|
ruyi/cli/cmd.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
from typing import Callable, IO, Protocol, TYPE_CHECKING
|
|
3
3
|
|
|
4
|
+
from ..i18n import _
|
|
4
5
|
from . import RUYI_ENTRYPOINT_NAME
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
@@ -114,7 +115,7 @@ class BaseCommand:
|
|
|
114
115
|
return
|
|
115
116
|
|
|
116
117
|
sp = p.add_subparsers(
|
|
117
|
-
title="subcommands",
|
|
118
|
+
title=_("subcommands"),
|
|
118
119
|
required=cls.is_subcommand_required,
|
|
119
120
|
)
|
|
120
121
|
for subcmd_cls in cls.parsers:
|
|
@@ -158,7 +159,7 @@ class RootCommand(
|
|
|
158
159
|
has_subcommands=True,
|
|
159
160
|
has_main=True,
|
|
160
161
|
prog=RUYI_ENTRYPOINT_NAME,
|
|
161
|
-
description="RuyiSDK Package Manager",
|
|
162
|
+
description=_("RuyiSDK Package Manager"),
|
|
162
163
|
):
|
|
163
164
|
@classmethod
|
|
164
165
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
@@ -170,12 +171,12 @@ class RootCommand(
|
|
|
170
171
|
action="store_const",
|
|
171
172
|
dest="func",
|
|
172
173
|
const=cli_version,
|
|
173
|
-
help="Print version information",
|
|
174
|
+
help=_("Print version information"),
|
|
174
175
|
)
|
|
175
176
|
p.add_argument(
|
|
176
177
|
"--porcelain",
|
|
177
178
|
action="store_true",
|
|
178
|
-
help="Give the output in a machine-friendly format if applicable",
|
|
179
|
+
help=_("Give the output in a machine-friendly format if applicable"),
|
|
179
180
|
)
|
|
180
181
|
|
|
181
182
|
# https://github.com/python/cpython/issues/67037 prevents the registration
|
|
@@ -219,7 +220,7 @@ class AdminCommand(
|
|
|
219
220
|
has_subcommands=True,
|
|
220
221
|
# https://github.com/python/cpython/issues/67037
|
|
221
222
|
# help=argparse.SUPPRESS,
|
|
222
|
-
help="(NOT FOR REGULAR USERS) Subcommands for managing Ruyi repos",
|
|
223
|
+
help=_("(NOT FOR REGULAR USERS) Subcommands for managing Ruyi repos"),
|
|
223
224
|
):
|
|
224
225
|
@classmethod
|
|
225
226
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
ruyi/cli/config_cli.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
|
+
from ..i18n import _
|
|
4
5
|
from .cmd import RootCommand
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
@@ -13,7 +14,7 @@ class ConfigCommand(
|
|
|
13
14
|
RootCommand,
|
|
14
15
|
cmd="config",
|
|
15
16
|
has_subcommands=True,
|
|
16
|
-
help="Manage Ruyi's config options",
|
|
17
|
+
help=_("Manage Ruyi's config options"),
|
|
17
18
|
):
|
|
18
19
|
@classmethod
|
|
19
20
|
def configure_args(
|
|
@@ -27,7 +28,7 @@ class ConfigCommand(
|
|
|
27
28
|
class ConfigGetCommand(
|
|
28
29
|
ConfigCommand,
|
|
29
30
|
cmd="get",
|
|
30
|
-
help="Query the value of a Ruyi config option",
|
|
31
|
+
help=_("Query the value of a Ruyi config option"),
|
|
31
32
|
):
|
|
32
33
|
@classmethod
|
|
33
34
|
def configure_args(
|
|
@@ -38,7 +39,7 @@ class ConfigGetCommand(
|
|
|
38
39
|
p.add_argument(
|
|
39
40
|
"key",
|
|
40
41
|
type=str,
|
|
41
|
-
help="The Ruyi config option to query",
|
|
42
|
+
help=_("The Ruyi config option to query"),
|
|
42
43
|
)
|
|
43
44
|
|
|
44
45
|
@classmethod
|
|
@@ -66,7 +67,7 @@ class ConfigGetCommand(
|
|
|
66
67
|
class ConfigSetCommand(
|
|
67
68
|
ConfigCommand,
|
|
68
69
|
cmd="set",
|
|
69
|
-
help="Set the value of a Ruyi config option",
|
|
70
|
+
help=_("Set the value of a Ruyi config option"),
|
|
70
71
|
):
|
|
71
72
|
@classmethod
|
|
72
73
|
def configure_args(
|
|
@@ -77,12 +78,12 @@ class ConfigSetCommand(
|
|
|
77
78
|
p.add_argument(
|
|
78
79
|
"key",
|
|
79
80
|
type=str,
|
|
80
|
-
help="The Ruyi config option to set",
|
|
81
|
+
help=_("The Ruyi config option to set"),
|
|
81
82
|
)
|
|
82
83
|
p.add_argument(
|
|
83
84
|
"value",
|
|
84
85
|
type=str,
|
|
85
|
-
help="The value to set the option to",
|
|
86
|
+
help=_("The value to set the option to"),
|
|
86
87
|
)
|
|
87
88
|
|
|
88
89
|
@classmethod
|
|
@@ -100,7 +101,9 @@ class ConfigSetCommand(
|
|
|
100
101
|
ed.set_value(key, pyval)
|
|
101
102
|
except ProtectedGlobalConfigError:
|
|
102
103
|
cfg.logger.F(
|
|
103
|
-
|
|
104
|
+
_(
|
|
105
|
+
"the config [yellow]{key}[/] is protected and not meant to be overridden by users"
|
|
106
|
+
).format(key=key)
|
|
104
107
|
)
|
|
105
108
|
return 2
|
|
106
109
|
|
|
@@ -112,7 +115,7 @@ class ConfigSetCommand(
|
|
|
112
115
|
class ConfigUnsetCommand(
|
|
113
116
|
ConfigCommand,
|
|
114
117
|
cmd="unset",
|
|
115
|
-
help="Unset a Ruyi config option",
|
|
118
|
+
help=_("Unset a Ruyi config option"),
|
|
116
119
|
):
|
|
117
120
|
@classmethod
|
|
118
121
|
def configure_args(
|
|
@@ -123,7 +126,7 @@ class ConfigUnsetCommand(
|
|
|
123
126
|
p.add_argument(
|
|
124
127
|
"key",
|
|
125
128
|
type=str,
|
|
126
|
-
help="The Ruyi config option to unset",
|
|
129
|
+
help=_("The Ruyi config option to unset"),
|
|
127
130
|
)
|
|
128
131
|
|
|
129
132
|
@classmethod
|
|
@@ -142,7 +145,7 @@ class ConfigUnsetCommand(
|
|
|
142
145
|
class ConfigRemoveSectionCommand(
|
|
143
146
|
ConfigCommand,
|
|
144
147
|
cmd="remove-section",
|
|
145
|
-
help="Remove a section from the Ruyi config",
|
|
148
|
+
help=_("Remove a section from the Ruyi config"),
|
|
146
149
|
):
|
|
147
150
|
@classmethod
|
|
148
151
|
def configure_args(
|
|
@@ -153,7 +156,7 @@ class ConfigRemoveSectionCommand(
|
|
|
153
156
|
p.add_argument(
|
|
154
157
|
"section",
|
|
155
158
|
type=str,
|
|
156
|
-
help="The section to remove",
|
|
159
|
+
help=_("The section to remove"),
|
|
157
160
|
)
|
|
158
161
|
|
|
159
162
|
@classmethod
|
ruyi/cli/main.py
CHANGED
|
@@ -4,6 +4,7 @@ import sys
|
|
|
4
4
|
from typing import Final, TYPE_CHECKING
|
|
5
5
|
|
|
6
6
|
from ..config import GlobalConfig
|
|
7
|
+
from ..i18n import _
|
|
7
8
|
from ..telemetry.scope import TelemetryScope
|
|
8
9
|
from ..utils.global_mode import GlobalModeProvider
|
|
9
10
|
from ..version import RUYI_SEMVER
|
|
@@ -50,10 +51,19 @@ def main(gm: GlobalModeProvider, gc: GlobalConfig, argv: list[str]) -> int:
|
|
|
50
51
|
if not is_called_as_ruyi(gm.argv0):
|
|
51
52
|
if should_prompt_for_renaming(gm.argv0):
|
|
52
53
|
logger.F(
|
|
53
|
-
|
|
54
|
+
_(
|
|
55
|
+
"the {ruyi_exe} executable must be named [green]'{expected_name}'[/] to work"
|
|
56
|
+
).format(
|
|
57
|
+
ruyi_exe=RUYI_ENTRYPOINT_NAME,
|
|
58
|
+
expected_name=RUYI_ENTRYPOINT_NAME,
|
|
59
|
+
)
|
|
54
60
|
)
|
|
55
|
-
logger.I(
|
|
56
|
-
|
|
61
|
+
logger.I(
|
|
62
|
+
_("it is now [yellow]'{current_name}'[/]").format(
|
|
63
|
+
current_name=gm.argv0,
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
logger.I(_("please rename the command file and retry"))
|
|
57
67
|
return 1
|
|
58
68
|
|
|
59
69
|
from ..mux.runtime import mux_main
|
|
@@ -115,7 +125,7 @@ def main(gm: GlobalModeProvider, gc: GlobalConfig, argv: list[str]) -> int:
|
|
|
115
125
|
try:
|
|
116
126
|
telemetry_key: str = args.tele_key
|
|
117
127
|
except AttributeError:
|
|
118
|
-
logger.F("internal error: CLI entrypoint was added without a telemetry key")
|
|
128
|
+
logger.F(_("internal error: CLI entrypoint was added without a telemetry key"))
|
|
119
129
|
return 1
|
|
120
130
|
|
|
121
131
|
# Special-case the `--output-completion-script` argument; treat it as if
|
ruyi/cli/oobe.py
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
|
-
from typing import Callable, TYPE_CHECKING
|
|
5
|
+
from typing import Callable, Final, TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from ..i18n import _, d_
|
|
6
8
|
|
|
7
9
|
if TYPE_CHECKING:
|
|
8
10
|
from ..config import GlobalConfig
|
|
9
11
|
|
|
10
12
|
|
|
11
|
-
SHELL_AUTO_COMPLETION_TIP =
|
|
13
|
+
SHELL_AUTO_COMPLETION_TIP: Final = d_(
|
|
14
|
+
"""
|
|
12
15
|
[bold green]tip[/]: you can enable shell auto-completion for [yellow]ruyi[/] by adding the
|
|
13
16
|
following line to your [green]{shrc}[/], if you have not done so already:
|
|
14
17
|
|
|
@@ -18,6 +21,7 @@ You can do so by running the following command later:
|
|
|
18
21
|
|
|
19
22
|
[green]echo 'eval "$(ruyi --output-completion-script={shell})"' >> {shrc}[/]
|
|
20
23
|
"""
|
|
24
|
+
)
|
|
21
25
|
|
|
22
26
|
|
|
23
27
|
class OOBE:
|
|
@@ -74,7 +78,7 @@ class OOBE:
|
|
|
74
78
|
return
|
|
75
79
|
|
|
76
80
|
self._gc.logger.stdout(
|
|
77
|
-
SHELL_AUTO_COMPLETION_TIP.format(
|
|
81
|
+
_(SHELL_AUTO_COMPLETION_TIP).format(
|
|
78
82
|
shell=shell,
|
|
79
83
|
shrc=f"~/.{shell}rc",
|
|
80
84
|
)
|
ruyi/cli/self_cli.py
CHANGED
|
@@ -4,13 +4,16 @@ import pathlib
|
|
|
4
4
|
import shutil
|
|
5
5
|
from typing import Final, TYPE_CHECKING
|
|
6
6
|
|
|
7
|
+
from ..i18n import _, d_
|
|
7
8
|
from .cmd import RootCommand
|
|
8
9
|
|
|
9
10
|
if TYPE_CHECKING:
|
|
10
11
|
from .completion import ArgumentParser
|
|
11
12
|
from .. import config
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
UNINSTALL_NOTICE: Final = d_(
|
|
16
|
+
"""
|
|
14
17
|
[bold]Thanks for hacking with [yellow]Ruyi[/]![/]
|
|
15
18
|
|
|
16
19
|
This will uninstall [yellow]Ruyi[/] from your system, and optionally remove
|
|
@@ -21,6 +24,7 @@ Note that your [yellow]Ruyi[/] virtual environments [bold]will become unusable[/
|
|
|
21
24
|
[yellow]Ruyi[/] is uninstalled. You should take care of migrating or cleaning
|
|
22
25
|
them yourselves afterwards.
|
|
23
26
|
"""
|
|
27
|
+
)
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
# Self-management commands
|
|
@@ -28,7 +32,7 @@ class SelfCommand(
|
|
|
28
32
|
RootCommand,
|
|
29
33
|
cmd="self",
|
|
30
34
|
has_subcommands=True,
|
|
31
|
-
help="Manage this Ruyi installation",
|
|
35
|
+
help=_("Manage this Ruyi installation"),
|
|
32
36
|
):
|
|
33
37
|
@classmethod
|
|
34
38
|
def configure_args(
|
|
@@ -42,7 +46,7 @@ class SelfCommand(
|
|
|
42
46
|
class SelfCleanCommand(
|
|
43
47
|
SelfCommand,
|
|
44
48
|
cmd="clean",
|
|
45
|
-
help="Remove various Ruyi-managed data to reclaim storage",
|
|
49
|
+
help=_("Remove various Ruyi-managed data to reclaim storage"),
|
|
46
50
|
):
|
|
47
51
|
@classmethod
|
|
48
52
|
def configure_args(
|
|
@@ -54,42 +58,42 @@ class SelfCleanCommand(
|
|
|
54
58
|
"--quiet",
|
|
55
59
|
"-q",
|
|
56
60
|
action="store_true",
|
|
57
|
-
help="Do not print out the actions being performed",
|
|
61
|
+
help=_("Do not print out the actions being performed"),
|
|
58
62
|
)
|
|
59
63
|
p.add_argument(
|
|
60
64
|
"--all",
|
|
61
65
|
action="store_true",
|
|
62
|
-
help="Remove all covered data",
|
|
66
|
+
help=_("Remove all covered data"),
|
|
63
67
|
)
|
|
64
68
|
p.add_argument(
|
|
65
69
|
"--distfiles",
|
|
66
70
|
action="store_true",
|
|
67
|
-
help="Remove all downloaded distfiles if any",
|
|
71
|
+
help=_("Remove all downloaded distfiles if any"),
|
|
68
72
|
)
|
|
69
73
|
p.add_argument(
|
|
70
74
|
"--installed-pkgs",
|
|
71
75
|
action="store_true",
|
|
72
|
-
help="Remove all installed packages if any",
|
|
76
|
+
help=_("Remove all installed packages if any"),
|
|
73
77
|
)
|
|
74
78
|
p.add_argument(
|
|
75
79
|
"--news-read-status",
|
|
76
80
|
action="store_true",
|
|
77
|
-
help="Mark all news items as unread",
|
|
81
|
+
help=_("Mark all news items as unread"),
|
|
78
82
|
)
|
|
79
83
|
p.add_argument(
|
|
80
84
|
"--progcache",
|
|
81
85
|
action="store_true",
|
|
82
|
-
help="Clear the Ruyi program cache",
|
|
86
|
+
help=_("Clear the Ruyi program cache"),
|
|
83
87
|
)
|
|
84
88
|
p.add_argument(
|
|
85
89
|
"--repo",
|
|
86
90
|
action="store_true",
|
|
87
|
-
help="Remove the Ruyi repo if located in Ruyi-managed cache directory",
|
|
91
|
+
help=_("Remove the Ruyi repo if located in Ruyi-managed cache directory"),
|
|
88
92
|
)
|
|
89
93
|
p.add_argument(
|
|
90
94
|
"--telemetry",
|
|
91
95
|
action="store_true",
|
|
92
|
-
help="Remove all telemetry data recorded if any",
|
|
96
|
+
help=_("Remove all telemetry data recorded if any"),
|
|
93
97
|
)
|
|
94
98
|
|
|
95
99
|
@classmethod
|
|
@@ -122,9 +126,11 @@ class SelfCleanCommand(
|
|
|
122
126
|
telemetry,
|
|
123
127
|
]
|
|
124
128
|
):
|
|
125
|
-
logger.F("no data specified for cleaning")
|
|
129
|
+
logger.F(_("no data specified for cleaning"))
|
|
126
130
|
logger.I(
|
|
127
|
-
|
|
131
|
+
_(
|
|
132
|
+
"please check [yellow]ruyi self clean --help[/] for a list of cleanable data"
|
|
133
|
+
)
|
|
128
134
|
)
|
|
129
135
|
return 1
|
|
130
136
|
|
|
@@ -149,7 +155,7 @@ class SelfCleanCommand(
|
|
|
149
155
|
class SelfUninstallCommand(
|
|
150
156
|
SelfCommand,
|
|
151
157
|
cmd="uninstall",
|
|
152
|
-
help="Uninstall Ruyi",
|
|
158
|
+
help=_("Uninstall Ruyi"),
|
|
153
159
|
):
|
|
154
160
|
@classmethod
|
|
155
161
|
def configure_args(
|
|
@@ -160,13 +166,15 @@ class SelfUninstallCommand(
|
|
|
160
166
|
p.add_argument(
|
|
161
167
|
"--purge",
|
|
162
168
|
action="store_true",
|
|
163
|
-
help="Remove all installed packages and Ruyi-managed remote repo data",
|
|
169
|
+
help=_("Remove all installed packages and Ruyi-managed remote repo data"),
|
|
164
170
|
)
|
|
165
171
|
p.add_argument(
|
|
166
172
|
"-y",
|
|
167
173
|
action="store_true",
|
|
168
174
|
dest="consent",
|
|
169
|
-
help=
|
|
175
|
+
help=_(
|
|
176
|
+
"Give consent for uninstallation on CLI; do not ask for confirmation"
|
|
177
|
+
),
|
|
170
178
|
)
|
|
171
179
|
|
|
172
180
|
@classmethod
|
|
@@ -180,24 +188,28 @@ class SelfUninstallCommand(
|
|
|
180
188
|
|
|
181
189
|
if cfg.is_installation_externally_managed:
|
|
182
190
|
logger.F(
|
|
183
|
-
|
|
191
|
+
_(
|
|
192
|
+
"this [yellow]ruyi[/] is externally managed, for example, by the system package manager, and cannot be uninstalled this way"
|
|
193
|
+
)
|
|
184
194
|
)
|
|
185
|
-
logger.I("please uninstall via the external manager instead")
|
|
195
|
+
logger.I(_("please uninstall via the external manager instead"))
|
|
186
196
|
return 1
|
|
187
197
|
|
|
188
198
|
if not cfg.is_packaged:
|
|
189
199
|
logger.F(
|
|
190
|
-
|
|
200
|
+
_(
|
|
201
|
+
"this [yellow]ruyi[/] is not in standalone form, and cannot be uninstalled this way"
|
|
202
|
+
)
|
|
191
203
|
)
|
|
192
204
|
return 1
|
|
193
205
|
|
|
194
206
|
if not consent:
|
|
195
|
-
logger.stdout(UNINSTALL_NOTICE)
|
|
196
|
-
if not user_input.ask_for_yesno_confirmation(logger, "Continue?"):
|
|
197
|
-
logger.I("aborting uninstallation")
|
|
207
|
+
logger.stdout(_(UNINSTALL_NOTICE))
|
|
208
|
+
if not user_input.ask_for_yesno_confirmation(logger, _("Continue?")):
|
|
209
|
+
logger.I(_("aborting uninstallation"))
|
|
198
210
|
return 0
|
|
199
211
|
else:
|
|
200
|
-
logger.I("uninstallation consent given over CLI, proceeding")
|
|
212
|
+
logger.I(_("uninstallation consent given over CLI, proceeding"))
|
|
201
213
|
|
|
202
214
|
_do_reset(
|
|
203
215
|
cfg,
|
|
@@ -208,7 +220,7 @@ class SelfUninstallCommand(
|
|
|
208
220
|
self_binary=True,
|
|
209
221
|
)
|
|
210
222
|
|
|
211
|
-
logger.I("[yellow]ruyi[/] is uninstalled")
|
|
223
|
+
logger.I(_("[yellow]ruyi[/] is uninstalled"))
|
|
212
224
|
|
|
213
225
|
return 0
|
|
214
226
|
|
|
@@ -235,7 +247,7 @@ def _do_reset(
|
|
|
235
247
|
logger.I(s)
|
|
236
248
|
|
|
237
249
|
if installed_pkgs:
|
|
238
|
-
status("removing installed packages")
|
|
250
|
+
status(_("removing installed packages"))
|
|
239
251
|
shutil.rmtree(cfg.data_root, True)
|
|
240
252
|
cfg.ruyipkg_global_state.purge_installation_info()
|
|
241
253
|
|
|
@@ -244,28 +256,28 @@ def _do_reset(
|
|
|
244
256
|
cfg.telemetry.discard_events(True)
|
|
245
257
|
|
|
246
258
|
if all_state:
|
|
247
|
-
status("removing state data")
|
|
259
|
+
status(_("removing state data"))
|
|
248
260
|
shutil.rmtree(cfg.state_root, True)
|
|
249
261
|
else:
|
|
250
262
|
if news_read_status:
|
|
251
|
-
status("removing read status of news items")
|
|
263
|
+
status(_("removing read status of news items"))
|
|
252
264
|
cfg.news_read_status.remove()
|
|
253
265
|
|
|
254
266
|
if telemetry:
|
|
255
|
-
status("removing all telemetry data")
|
|
267
|
+
status(_("removing all telemetry data"))
|
|
256
268
|
shutil.rmtree(cfg.telemetry_root, True)
|
|
257
269
|
|
|
258
270
|
if all_cache:
|
|
259
|
-
status("removing cached data")
|
|
271
|
+
status(_("removing cached data"))
|
|
260
272
|
shutil.rmtree(cfg.cache_root, True)
|
|
261
273
|
else:
|
|
262
274
|
if distfiles:
|
|
263
|
-
status("removing downloaded distfiles")
|
|
275
|
+
status(_("removing downloaded distfiles"))
|
|
264
276
|
# TODO: deduplicate the path derivation
|
|
265
277
|
shutil.rmtree(os.path.join(cfg.cache_root, "distfiles"), True)
|
|
266
278
|
|
|
267
279
|
if progcache:
|
|
268
|
-
status("clearing the Ruyi program cache")
|
|
280
|
+
status(_("clearing the Ruyi program cache"))
|
|
269
281
|
# TODO: deduplicate the path derivation
|
|
270
282
|
shutil.rmtree(os.path.join(cfg.cache_root, "progcache"), True)
|
|
271
283
|
|
|
@@ -283,14 +295,16 @@ def _do_reset(
|
|
|
283
295
|
|
|
284
296
|
if not repo_is_below_cache_root:
|
|
285
297
|
logger.W(
|
|
286
|
-
|
|
298
|
+
_(
|
|
299
|
+
"not removing the Ruyi repo: it is outside of the Ruyi cache directory"
|
|
300
|
+
)
|
|
287
301
|
)
|
|
288
302
|
else:
|
|
289
|
-
status("removing the Ruyi repo")
|
|
303
|
+
status(_("removing the Ruyi repo"))
|
|
290
304
|
shutil.rmtree(repo_dir, True)
|
|
291
305
|
|
|
292
306
|
if self_binary:
|
|
293
|
-
status("removing the ruyi binary")
|
|
307
|
+
status(_("removing the ruyi binary"))
|
|
294
308
|
try:
|
|
295
309
|
os.unlink(cfg.self_exe)
|
|
296
310
|
except FileNotFoundError:
|
ruyi/cli/user_input.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os.path
|
|
2
2
|
|
|
3
|
+
from ..i18n import _
|
|
3
4
|
from ..log import RuyiLogger
|
|
4
5
|
|
|
5
6
|
|
|
@@ -10,7 +11,7 @@ def pause_before_continuing(
|
|
|
10
11
|
|
|
11
12
|
EOFError should be handled by the caller."""
|
|
12
13
|
|
|
13
|
-
logger.stdout("Press [green]<ENTER>[/] to continue: ", end="")
|
|
14
|
+
logger.stdout(_("Press [green]<ENTER>[/] to continue: "), end="")
|
|
14
15
|
input()
|
|
15
16
|
|
|
16
17
|
|
|
@@ -26,9 +27,11 @@ def ask_for_yesno_confirmation(
|
|
|
26
27
|
logger.stdout(f"{prompt} {choices_help} ", end="")
|
|
27
28
|
user_input = input()
|
|
28
29
|
except EOFError:
|
|
29
|
-
yesno = "YES" if default else "NO"
|
|
30
|
+
yesno = _("YES") if default else _("NO")
|
|
30
31
|
logger.W(
|
|
31
|
-
|
|
32
|
+
_(
|
|
33
|
+
"EOF while reading user input, assuming the default choice {yesno}"
|
|
34
|
+
).format(yesno=yesno)
|
|
32
35
|
)
|
|
33
36
|
return default
|
|
34
37
|
|
|
@@ -39,8 +42,12 @@ def ask_for_yesno_confirmation(
|
|
|
39
42
|
if user_input in {"N", "n", "no"}:
|
|
40
43
|
return False
|
|
41
44
|
else:
|
|
42
|
-
logger.stdout(
|
|
43
|
-
|
|
45
|
+
logger.stdout(
|
|
46
|
+
_("Unrecognized input [yellow]'{user_input}'[/].").format(
|
|
47
|
+
user_input=user_input
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
logger.stdout(_("Accepted choices: Y/y/yes for YES, N/n/no for NO."))
|
|
44
51
|
|
|
45
52
|
|
|
46
53
|
def ask_for_kv_choice(
|
|
@@ -81,12 +88,19 @@ def ask_for_choice(
|
|
|
81
88
|
if default_idx is not None:
|
|
82
89
|
if not (0 <= default_idx < nr_choices):
|
|
83
90
|
raise ValueError(f"Default choice index {default_idx} out of range")
|
|
84
|
-
choices_help =
|
|
91
|
+
choices_help = _("(1-{nr_choices}, default {default})").format(
|
|
92
|
+
nr_choices=nr_choices,
|
|
93
|
+
default=default_idx + 1,
|
|
94
|
+
)
|
|
85
95
|
else:
|
|
86
|
-
choices_help =
|
|
96
|
+
choices_help = _("(1-{nr_choices})").format(nr_choices=nr_choices)
|
|
87
97
|
while True:
|
|
88
98
|
try:
|
|
89
|
-
user_input = input(
|
|
99
|
+
user_input = input(
|
|
100
|
+
_("Choice? {choices_help} ").format(
|
|
101
|
+
choices_help=choices_help,
|
|
102
|
+
)
|
|
103
|
+
)
|
|
90
104
|
except EOFError:
|
|
91
105
|
raise ValueError("EOF while reading user choice")
|
|
92
106
|
|
|
@@ -96,18 +110,34 @@ def ask_for_choice(
|
|
|
96
110
|
try:
|
|
97
111
|
choice_int = int(user_input)
|
|
98
112
|
except ValueError:
|
|
99
|
-
logger.stdout(f"Unrecognized input [yellow]'{user_input}'[/].")
|
|
100
113
|
logger.stdout(
|
|
101
|
-
|
|
114
|
+
_("Unrecognized input [yellow]'{user_input}'[/].").format(
|
|
115
|
+
user_input=user_input,
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
logger.stdout(
|
|
119
|
+
_(
|
|
120
|
+
"Accepted choices: an integer number from 1 to {nr_choices} inclusive."
|
|
121
|
+
).format(
|
|
122
|
+
nr_choices=nr_choices,
|
|
123
|
+
)
|
|
102
124
|
)
|
|
103
125
|
continue
|
|
104
126
|
|
|
105
127
|
if 1 <= choice_int <= nr_choices:
|
|
106
128
|
return choice_int - 1
|
|
107
129
|
|
|
108
|
-
logger.stdout(f"Out-of-range input [yellow]'{user_input}'[/].")
|
|
109
130
|
logger.stdout(
|
|
110
|
-
|
|
131
|
+
_("Out-of-range input [yellow]'{user_input}'[/].").format(
|
|
132
|
+
user_input=user_input,
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
logger.stdout(
|
|
136
|
+
_(
|
|
137
|
+
"Accepted choices: an integer number from 1 to {nr_choices} inclusive."
|
|
138
|
+
).format(
|
|
139
|
+
nr_choices=nr_choices,
|
|
140
|
+
)
|
|
111
141
|
)
|
|
112
142
|
|
|
113
143
|
|
ruyi/cli/version_cli.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
|
+
from ..i18n import _
|
|
4
5
|
from .cmd import RootCommand
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
@@ -11,7 +12,7 @@ if TYPE_CHECKING:
|
|
|
11
12
|
class VersionCommand(
|
|
12
13
|
RootCommand,
|
|
13
14
|
cmd="version",
|
|
14
|
-
help="Print version information",
|
|
15
|
+
help=_("Print version information"),
|
|
15
16
|
):
|
|
16
17
|
@classmethod
|
|
17
18
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
@@ -27,19 +28,24 @@ def cli_version(cfg: "GlobalConfig", args: argparse.Namespace) -> int:
|
|
|
27
28
|
from ..ruyipkg.host import get_native_host
|
|
28
29
|
from ..version import COPYRIGHT_NOTICE, MPL_REDIST_NOTICE, RUYI_SEMVER
|
|
29
30
|
|
|
30
|
-
print(
|
|
31
|
+
print(
|
|
32
|
+
_("Ruyi {version}\n\nRunning on {host}.").format(
|
|
33
|
+
version=RUYI_SEMVER,
|
|
34
|
+
host=get_native_host(),
|
|
35
|
+
)
|
|
36
|
+
)
|
|
31
37
|
|
|
32
38
|
if cfg.is_installation_externally_managed:
|
|
33
|
-
print("This Ruyi installation is externally managed.")
|
|
39
|
+
print(_("This Ruyi installation is externally managed."))
|
|
34
40
|
|
|
35
41
|
print()
|
|
36
42
|
|
|
37
|
-
cfg.logger.stdout(COPYRIGHT_NOTICE)
|
|
43
|
+
cfg.logger.stdout(_(COPYRIGHT_NOTICE))
|
|
38
44
|
|
|
39
45
|
# Output the MPL notice only when we actually bundle and depend on the
|
|
40
46
|
# MPL component(s), which right now is only certifi. Keep the condition
|
|
41
47
|
# synced with __main__.py.
|
|
42
48
|
if hasattr(ruyi, "__compiled__") and ruyi.__compiled__.standalone:
|
|
43
|
-
cfg.logger.stdout(MPL_REDIST_NOTICE)
|
|
49
|
+
cfg.logger.stdout(_(MPL_REDIST_NOTICE))
|
|
44
50
|
|
|
45
51
|
return 0
|