ruyi 0.45.0__py3-none-any.whl → 0.45.0a20251230__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 +4 -16
- ruyi/cli/cmd.py +5 -6
- ruyi/cli/config_cli.py +11 -14
- ruyi/cli/main.py +4 -14
- ruyi/cli/oobe.py +3 -7
- ruyi/cli/self_cli.py +34 -48
- ruyi/cli/user_input.py +12 -42
- ruyi/cli/version_cli.py +5 -11
- ruyi/config/__init__.py +2 -26
- ruyi/config/errors.py +7 -19
- ruyi/device/provision.py +55 -116
- ruyi/device/provision_cli.py +3 -6
- ruyi/log/__init__.py +5 -6
- ruyi/mux/runtime.py +6 -19
- ruyi/mux/venv/maker.py +35 -93
- ruyi/mux/venv/venv_cli.py +10 -13
- ruyi/pluginhost/plugin_cli.py +3 -4
- ruyi/resource_bundle/__init__.py +8 -22
- ruyi/resource_bundle/__main__.py +5 -6
- ruyi/resource_bundle/data.py +9 -13
- ruyi/ruyipkg/admin_checksum.py +1 -4
- ruyi/ruyipkg/admin_cli.py +6 -9
- ruyi/ruyipkg/augmented_pkg.py +14 -15
- ruyi/ruyipkg/checksum.py +2 -8
- ruyi/ruyipkg/distfile.py +9 -33
- ruyi/ruyipkg/entity.py +2 -12
- ruyi/ruyipkg/entity_cli.py +12 -20
- ruyi/ruyipkg/entity_provider.py +2 -11
- ruyi/ruyipkg/fetcher.py +9 -38
- ruyi/ruyipkg/install.py +42 -143
- ruyi/ruyipkg/install_cli.py +15 -18
- ruyi/ruyipkg/list.py +20 -27
- ruyi/ruyipkg/list_cli.py +7 -12
- ruyi/ruyipkg/news.py +11 -23
- ruyi/ruyipkg/news_cli.py +7 -10
- ruyi/ruyipkg/profile_cli.py +2 -8
- ruyi/ruyipkg/repo.py +8 -22
- ruyi/ruyipkg/unpack.py +8 -42
- ruyi/ruyipkg/unpack_method.py +1 -5
- ruyi/ruyipkg/update_cli.py +3 -8
- ruyi/telemetry/provider.py +29 -74
- ruyi/telemetry/telemetry_cli.py +8 -9
- ruyi/utils/git.py +11 -18
- ruyi/utils/prereqs.py +5 -10
- ruyi/utils/ssl_patch.py +1 -2
- ruyi/version.py +3 -9
- {ruyi-0.45.0.dist-info → ruyi-0.45.0a20251230.dist-info}/METADATA +1 -2
- ruyi-0.45.0a20251230.dist-info/RECORD +102 -0
- {ruyi-0.45.0.dist-info → ruyi-0.45.0a20251230.dist-info}/WHEEL +1 -1
- ruyi/i18n/__init__.py +0 -129
- ruyi-0.45.0.dist-info/RECORD +0 -103
- {ruyi-0.45.0.dist-info → ruyi-0.45.0a20251230.dist-info}/entry_points.txt +0 -0
- {ruyi-0.45.0.dist-info → ruyi-0.45.0a20251230.dist-info}/licenses/LICENSE-Apache.txt +0 -0
ruyi/config/__init__.py
CHANGED
|
@@ -18,13 +18,6 @@ if TYPE_CHECKING:
|
|
|
18
18
|
from ..utils.xdg_basedir import XDGPathEntry
|
|
19
19
|
from .news import NewsReadStatusStore
|
|
20
20
|
|
|
21
|
-
import babel
|
|
22
|
-
# not sure why Pyright insists on individual imports
|
|
23
|
-
# otherwise, at the use site (`except babel.core.UnknownLocaleError`):
|
|
24
|
-
# error: "core" is not a known attribute of module "babel" (reportAttributeAccessIssue)
|
|
25
|
-
from babel.core import UnknownLocaleError
|
|
26
|
-
|
|
27
|
-
from ..i18n import _
|
|
28
21
|
from . import errors
|
|
29
22
|
from . import schema
|
|
30
23
|
|
|
@@ -122,11 +115,7 @@ class GlobalConfig:
|
|
|
122
115
|
if iem is not None and not is_global_scope:
|
|
123
116
|
iem_cfg_key = f"{schema.SECTION_INSTALLATION}.{schema.KEY_INSTALLATION_EXTERNALLY_MANAGED}"
|
|
124
117
|
self.logger.W(
|
|
125
|
-
|
|
126
|
-
"the config key [yellow]{key}[/] cannot be set from user config; ignoring"
|
|
127
|
-
).format(
|
|
128
|
-
key=iem_cfg_key,
|
|
129
|
-
),
|
|
118
|
+
f"the config key [yellow]{iem_cfg_key}[/] cannot be set from user config; ignoring",
|
|
130
119
|
)
|
|
131
120
|
else:
|
|
132
121
|
self.is_installation_externally_managed = bool(iem)
|
|
@@ -144,11 +133,7 @@ class GlobalConfig:
|
|
|
144
133
|
if self.override_repo_dir:
|
|
145
134
|
if not pathlib.Path(self.override_repo_dir).is_absolute():
|
|
146
135
|
self.logger.W(
|
|
147
|
-
|
|
148
|
-
"the local repo path '{path}' is not absolute; ignoring"
|
|
149
|
-
).format(
|
|
150
|
-
path=self.override_repo_dir,
|
|
151
|
-
)
|
|
136
|
+
f"the local repo path '{self.override_repo_dir}' is not absolute; ignoring"
|
|
152
137
|
)
|
|
153
138
|
self.override_repo_dir = None
|
|
154
139
|
|
|
@@ -289,15 +274,6 @@ class GlobalConfig:
|
|
|
289
274
|
def lang_code(self) -> str:
|
|
290
275
|
return self._lang_code
|
|
291
276
|
|
|
292
|
-
@cached_property
|
|
293
|
-
def babel_locale(self) -> babel.Locale:
|
|
294
|
-
try:
|
|
295
|
-
return babel.Locale.parse(self.lang_code)
|
|
296
|
-
except UnknownLocaleError:
|
|
297
|
-
# this can happen in case of unrecognized locale names, which
|
|
298
|
-
# apparently falls back to "C"
|
|
299
|
-
return babel.Locale.parse("en_US")
|
|
300
|
-
|
|
301
277
|
@property
|
|
302
278
|
def cache_root(self) -> os.PathLike[Any]:
|
|
303
279
|
return self._dirs.app_cache
|
ruyi/config/errors.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from os import PathLike
|
|
2
2
|
from typing import Any, Sequence
|
|
3
3
|
|
|
4
|
-
from ..i18n import _
|
|
5
|
-
|
|
6
4
|
|
|
7
5
|
class InvalidConfigSectionError(Exception):
|
|
8
6
|
def __init__(self, section: str) -> None:
|
|
@@ -10,7 +8,7 @@ class InvalidConfigSectionError(Exception):
|
|
|
10
8
|
self._section = section
|
|
11
9
|
|
|
12
10
|
def __str__(self) -> str:
|
|
13
|
-
return
|
|
11
|
+
return f"invalid config section: {self._section}"
|
|
14
12
|
|
|
15
13
|
def __repr__(self) -> str:
|
|
16
14
|
return f"InvalidConfigSectionError({self._section!r})"
|
|
@@ -22,7 +20,7 @@ class InvalidConfigKeyError(Exception):
|
|
|
22
20
|
self._key = key
|
|
23
21
|
|
|
24
22
|
def __str__(self) -> str:
|
|
25
|
-
return
|
|
23
|
+
return f"invalid config key: {self._key}"
|
|
26
24
|
|
|
27
25
|
def __repr__(self) -> str:
|
|
28
26
|
return f"InvalidConfigKeyError({self._key:!r})"
|
|
@@ -41,13 +39,7 @@ class InvalidConfigValueTypeError(TypeError):
|
|
|
41
39
|
self._expected = expected
|
|
42
40
|
|
|
43
41
|
def __str__(self) -> str:
|
|
44
|
-
return
|
|
45
|
-
"invalid value type for config key {key}: {actual_type}, expected {expected_type}"
|
|
46
|
-
).format(
|
|
47
|
-
key=self._key,
|
|
48
|
-
actual_type=type(self._val),
|
|
49
|
-
expected_type=self._expected,
|
|
50
|
-
)
|
|
42
|
+
return f"invalid value type for config key {self._key}: {type(self._val)}, expected {self._expected}"
|
|
51
43
|
|
|
52
44
|
def __repr__(self) -> str:
|
|
53
45
|
return f"InvalidConfigValueTypeError({self._key!r}, {self._val!r}, {self._expected:!r})"
|
|
@@ -66,10 +58,8 @@ class InvalidConfigValueError(ValueError):
|
|
|
66
58
|
self._typ = typ
|
|
67
59
|
|
|
68
60
|
def __str__(self) -> str:
|
|
69
|
-
return
|
|
70
|
-
key
|
|
71
|
-
typ=self._typ,
|
|
72
|
-
val=self._val,
|
|
61
|
+
return (
|
|
62
|
+
f"invalid config value for key {self._key} (type {self._typ}): {self._val}"
|
|
73
63
|
)
|
|
74
64
|
|
|
75
65
|
def __repr__(self) -> str:
|
|
@@ -84,7 +74,7 @@ class MalformedConfigFileError(Exception):
|
|
|
84
74
|
self._path = path
|
|
85
75
|
|
|
86
76
|
def __str__(self) -> str:
|
|
87
|
-
return
|
|
77
|
+
return f"malformed config file: {self._path}"
|
|
88
78
|
|
|
89
79
|
def __repr__(self) -> str:
|
|
90
80
|
return f"MalformedConfigFileError({self._path:!r})"
|
|
@@ -96,9 +86,7 @@ class ProtectedGlobalConfigError(Exception):
|
|
|
96
86
|
self._key = key
|
|
97
87
|
|
|
98
88
|
def __str__(self) -> str:
|
|
99
|
-
return
|
|
100
|
-
key=self._key,
|
|
101
|
-
)
|
|
89
|
+
return f"attempt to modify protected global config key: {self._key}"
|
|
102
90
|
|
|
103
91
|
def __repr__(self) -> str:
|
|
104
92
|
return f"ProtectedGlobalConfigError({self._key!r})"
|
ruyi/device/provision.py
CHANGED
|
@@ -4,7 +4,6 @@ from typing import TYPE_CHECKING, TypedDict, TypeGuard, cast
|
|
|
4
4
|
|
|
5
5
|
from ..cli import user_input
|
|
6
6
|
from ..config import GlobalConfig
|
|
7
|
-
from ..i18n import _
|
|
8
7
|
from ..log import RuyiLogger
|
|
9
8
|
from ..ruyipkg.atom import Atom, ExprAtom, SlugAtom
|
|
10
9
|
from ..ruyipkg.entity_provider import BaseEntity
|
|
@@ -37,8 +36,7 @@ def do_provision_interactive(config: GlobalConfig) -> int:
|
|
|
37
36
|
mr.ensure_git_repo()
|
|
38
37
|
|
|
39
38
|
log.stdout(
|
|
40
|
-
|
|
41
|
-
"""
|
|
39
|
+
"""
|
|
42
40
|
[bold green]RuyiSDK Device Provisioning Wizard[/]
|
|
43
41
|
|
|
44
42
|
This is a wizard intended to help you install a system on your device for your
|
|
@@ -54,12 +52,11 @@ required to flash images, you should arrange to allow your user account [yellow]
|
|
|
54
52
|
access to necessary commands such as [yellow]dd[/]. Flashing will fail if the [yellow]sudo[/]
|
|
55
53
|
configuration does not allow so.
|
|
56
54
|
"""
|
|
57
|
-
)
|
|
58
55
|
)
|
|
59
56
|
|
|
60
|
-
if not user_input.ask_for_yesno_confirmation(log,
|
|
57
|
+
if not user_input.ask_for_yesno_confirmation(log, "Continue?"):
|
|
61
58
|
log.stdout(
|
|
62
|
-
|
|
59
|
+
"\nExiting. You can restart the wizard whenever prepared.",
|
|
63
60
|
end="\n\n",
|
|
64
61
|
)
|
|
65
62
|
return 1
|
|
@@ -71,9 +68,7 @@ configuration does not allow so.
|
|
|
71
68
|
dev_choices = {k: v.display_name or "" for k, v in devices_by_id.items()}
|
|
72
69
|
dev_id = user_input.ask_for_kv_choice(
|
|
73
70
|
log,
|
|
74
|
-
|
|
75
|
-
"\nThe following devices are currently supported by the wizard. Please pick your device:"
|
|
76
|
-
),
|
|
71
|
+
"\nThe following devices are currently supported by the wizard. Please pick your device:",
|
|
77
72
|
dev_choices,
|
|
78
73
|
)
|
|
79
74
|
dev = devices_by_id[dev_id]
|
|
@@ -89,9 +84,7 @@ configuration does not allow so.
|
|
|
89
84
|
variant_choices = [get_variant_display_name(dev, i) for i in variants]
|
|
90
85
|
variant_idx = user_input.ask_for_choice(
|
|
91
86
|
log,
|
|
92
|
-
|
|
93
|
-
"\nThe device has the following variants. Please choose the one corresponding to your hardware at hand:"
|
|
94
|
-
),
|
|
87
|
+
"\nThe device has the following variants. Please choose the one corresponding to your hardware at hand:",
|
|
95
88
|
variant_choices,
|
|
96
89
|
)
|
|
97
90
|
variant = variants[variant_idx]
|
|
@@ -108,9 +101,7 @@ configuration does not allow so.
|
|
|
108
101
|
combo_choices = [combo.display_name or "" for combo in supported_combos]
|
|
109
102
|
combo_idx = user_input.ask_for_choice(
|
|
110
103
|
log,
|
|
111
|
-
|
|
112
|
-
"\nThe following system configurations are supported by the device variant you have chosen. Please pick the one you want to put on the device:"
|
|
113
|
-
),
|
|
104
|
+
"\nThe following system configurations are supported by the device variant you have chosen. Please pick the one you want to put on the device:",
|
|
114
105
|
combo_choices,
|
|
115
106
|
)
|
|
116
107
|
combo = supported_combos[combo_idx]
|
|
@@ -141,8 +132,7 @@ def do_provision_combo_interactive(
|
|
|
141
132
|
combo: BaseEntity,
|
|
142
133
|
) -> int:
|
|
143
134
|
logger = config.logger
|
|
144
|
-
|
|
145
|
-
logger.D(f"provisioning device variant '{devid}'")
|
|
135
|
+
logger.D(f"provisioning device variant '{dev_decl.id}@{variant_decl.id}'")
|
|
146
136
|
|
|
147
137
|
# download packages
|
|
148
138
|
pkg_atoms = combo.data.get("package_atoms", [])
|
|
@@ -151,36 +141,28 @@ def do_provision_combo_interactive(
|
|
|
151
141
|
return 0
|
|
152
142
|
|
|
153
143
|
logger.F(
|
|
154
|
-
|
|
155
|
-
"malformed config: device variant '{devid}' asks for no packages but provides no messages either"
|
|
156
|
-
).format(
|
|
157
|
-
devid=devid,
|
|
158
|
-
)
|
|
144
|
+
f"malformed config: device variant '{dev_decl.id}@{variant_decl.id}' asks for no packages but provides no messages either"
|
|
159
145
|
)
|
|
160
146
|
return 1
|
|
161
147
|
|
|
162
148
|
new_pkg_atoms = customize_package_versions(config, mr, pkg_atoms)
|
|
163
149
|
if new_pkg_atoms is None:
|
|
164
|
-
logger.stdout(
|
|
165
|
-
_("\nExiting. You may restart the wizard at any time."), end="\n\n"
|
|
166
|
-
)
|
|
150
|
+
logger.stdout("\nExiting. You may restart the wizard at any time.", end="\n\n")
|
|
167
151
|
return 1
|
|
168
152
|
else:
|
|
169
153
|
pkg_atoms = new_pkg_atoms
|
|
170
154
|
|
|
171
155
|
pkg_names_for_display = "\n".join(f" * [green]{i}[/]" for i in pkg_atoms)
|
|
172
156
|
logger.stdout(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
157
|
+
f"""
|
|
158
|
+
We are about to download and install the following packages for your device:
|
|
159
|
+
|
|
160
|
+
{pkg_names_for_display}
|
|
161
|
+
"""
|
|
177
162
|
)
|
|
178
|
-
logger.stdout(pkg_names_for_display, end="\n\n")
|
|
179
163
|
|
|
180
|
-
if not user_input.ask_for_yesno_confirmation(logger,
|
|
181
|
-
logger.stdout(
|
|
182
|
-
_("\nExiting. You may restart the wizard at any time."), end="\n\n"
|
|
183
|
-
)
|
|
164
|
+
if not user_input.ask_for_yesno_confirmation(logger, "Proceed?"):
|
|
165
|
+
logger.stdout("\nExiting. You may restart the wizard at any time.", end="\n\n")
|
|
184
166
|
return 1
|
|
185
167
|
|
|
186
168
|
ret = do_install_atoms(
|
|
@@ -192,8 +174,8 @@ def do_provision_combo_interactive(
|
|
|
192
174
|
reinstall=False,
|
|
193
175
|
)
|
|
194
176
|
if ret != 0:
|
|
195
|
-
logger.F(
|
|
196
|
-
logger.I(
|
|
177
|
+
logger.F("failed to download and install packages")
|
|
178
|
+
logger.I("your device was not touched")
|
|
197
179
|
return 2
|
|
198
180
|
|
|
199
181
|
strat_provider = ProvisionStrategyProvider(mr)
|
|
@@ -216,8 +198,7 @@ def do_provision_combo_interactive(
|
|
|
216
198
|
host_blkdev_map: PartitionMapDecl = {}
|
|
217
199
|
if requested_host_blkdevs:
|
|
218
200
|
logger.stdout(
|
|
219
|
-
|
|
220
|
-
"""
|
|
201
|
+
"""
|
|
221
202
|
For initializing this target device, you should plug into this host system the
|
|
222
203
|
device's storage (e.g. SD card or NVMe SSD), or a removable disk to be
|
|
223
204
|
reformatted as a live medium, and note down the corresponding device file
|
|
@@ -225,7 +206,6 @@ path(s), e.g. /dev/sdX, /dev/nvmeXnY for whole disks; /dev/sdXY, /dev/nvmeXnYpZ
|
|
|
225
206
|
for partitions. You may consult e.g. [yellow]sudo blkid[/] output for the
|
|
226
207
|
information you will need later.
|
|
227
208
|
"""
|
|
228
|
-
)
|
|
229
209
|
)
|
|
230
210
|
for part in requested_host_blkdevs:
|
|
231
211
|
part_desc = get_part_desc(part)
|
|
@@ -233,9 +213,7 @@ information you will need later.
|
|
|
233
213
|
while True:
|
|
234
214
|
path = user_input.ask_for_file(
|
|
235
215
|
logger,
|
|
236
|
-
|
|
237
|
-
part_desc=part_desc,
|
|
238
|
-
),
|
|
216
|
+
f"Please give the path for the {part_desc}:",
|
|
239
217
|
)
|
|
240
218
|
|
|
241
219
|
# Retrieve the latest mount info in case the user un-mounts
|
|
@@ -246,17 +224,10 @@ information you will need later.
|
|
|
246
224
|
for m in blkdev_mounts:
|
|
247
225
|
if m.source_path.samefile(path):
|
|
248
226
|
logger.W(
|
|
249
|
-
|
|
250
|
-
"path [cyan]'{path}'[/] is currently mounted at [yellow]'{target}'[/]"
|
|
251
|
-
).format(
|
|
252
|
-
path=path,
|
|
253
|
-
target=m.target,
|
|
254
|
-
)
|
|
227
|
+
f"path [cyan]'{path}'[/] is currently mounted at [yellow]'{m.target}'[/]"
|
|
255
228
|
)
|
|
256
229
|
logger.I(
|
|
257
|
-
|
|
258
|
-
"rejecting the path for safety; please double-check and retry"
|
|
259
|
-
)
|
|
230
|
+
"rejecting the path for safety; please double-check and retry"
|
|
260
231
|
)
|
|
261
232
|
path_valid = False
|
|
262
233
|
break
|
|
@@ -267,14 +238,12 @@ information you will need later.
|
|
|
267
238
|
|
|
268
239
|
# final confirmation
|
|
269
240
|
logger.stdout(
|
|
270
|
-
|
|
271
|
-
"""
|
|
241
|
+
"""
|
|
272
242
|
We have collected enough information for the actual flashing. Now is the last
|
|
273
243
|
chance to re-check and confirm everything is fine.
|
|
274
244
|
|
|
275
245
|
We are about to:
|
|
276
246
|
"""
|
|
277
|
-
)
|
|
278
247
|
)
|
|
279
248
|
|
|
280
249
|
pretend_steps = "\n".join(
|
|
@@ -288,11 +257,9 @@ We are about to:
|
|
|
288
257
|
)
|
|
289
258
|
logger.stdout(pretend_steps, end="\n\n")
|
|
290
259
|
|
|
291
|
-
if not user_input.ask_for_yesno_confirmation(logger,
|
|
260
|
+
if not user_input.ask_for_yesno_confirmation(logger, "Proceed with flashing?"):
|
|
292
261
|
logger.stdout(
|
|
293
|
-
|
|
294
|
-
"\nExiting. The device is not touched and you may re-start the wizard at will."
|
|
295
|
-
),
|
|
262
|
+
"\nExiting. The device is not touched and you may re-start the wizard at will.",
|
|
296
263
|
end="\n\n",
|
|
297
264
|
)
|
|
298
265
|
return 1
|
|
@@ -306,20 +273,18 @@ We are about to:
|
|
|
306
273
|
# ask the user to ensure the device shows up
|
|
307
274
|
# TODO: automate doing so
|
|
308
275
|
logger.stdout(
|
|
309
|
-
|
|
276
|
+
"""
|
|
310
277
|
Some flashing steps require the use of fastboot, in which case you should
|
|
311
278
|
ensure the target device is showing up in [yellow]fastboot devices[/] output.
|
|
312
279
|
Please [bold red]confirm it yourself before continuing[/].
|
|
313
|
-
"""
|
|
280
|
+
"""
|
|
314
281
|
)
|
|
315
282
|
if not user_input.ask_for_yesno_confirmation(
|
|
316
283
|
logger,
|
|
317
|
-
|
|
284
|
+
"Is the device identified by fastboot now?",
|
|
318
285
|
):
|
|
319
286
|
logger.stdout(
|
|
320
|
-
|
|
321
|
-
"\nAborting. The device is not touched. You may re-start the wizard after [yellow]fastboot[/] is fixed for the device."
|
|
322
|
-
),
|
|
287
|
+
"\nAborting. The device is not touched. You may re-start the wizard after [yellow]fastboot[/] is fixed for the device.",
|
|
323
288
|
end="\n\n",
|
|
324
289
|
)
|
|
325
290
|
return 1
|
|
@@ -329,16 +294,16 @@ Please [bold red]confirm it yourself before continuing[/].
|
|
|
329
294
|
logger.D(f"flashing {pkg} with strategy {strat}")
|
|
330
295
|
ret = strat.flash(pkg_part_maps[pkg], host_blkdev_map)
|
|
331
296
|
if ret != 0:
|
|
332
|
-
logger.F(
|
|
297
|
+
logger.F("flashing failed, check your device right now")
|
|
333
298
|
return ret
|
|
334
299
|
|
|
335
300
|
# parting words
|
|
336
301
|
logger.stdout(
|
|
337
|
-
|
|
302
|
+
"""
|
|
338
303
|
It seems the flashing has finished without errors.
|
|
339
304
|
|
|
340
305
|
[bold green]Happy hacking![/]
|
|
341
|
-
"""
|
|
306
|
+
"""
|
|
342
307
|
)
|
|
343
308
|
|
|
344
309
|
maybe_render_postinst_msg(logger, mr, combo, config.lang_code)
|
|
@@ -349,11 +314,11 @@ It seems the flashing has finished without errors.
|
|
|
349
314
|
def get_part_desc(part: PartitionKind) -> str:
|
|
350
315
|
match part:
|
|
351
316
|
case "disk":
|
|
352
|
-
return
|
|
317
|
+
return "target's whole disk"
|
|
353
318
|
case "live":
|
|
354
|
-
return
|
|
319
|
+
return "removable disk to use as live medium"
|
|
355
320
|
case _:
|
|
356
|
-
return
|
|
321
|
+
return f"target's '{part}' partition"
|
|
357
322
|
|
|
358
323
|
|
|
359
324
|
class PackageProvisionStrategyDecl(TypedDict):
|
|
@@ -524,19 +489,17 @@ def customize_package_versions(
|
|
|
524
489
|
|
|
525
490
|
# Ask if the user wants to customize package versions
|
|
526
491
|
logger.stdout(
|
|
527
|
-
|
|
528
|
-
"By default, we'll install the latest version of each package, but in this case, other choices are possible."
|
|
529
|
-
)
|
|
492
|
+
"By default, we'll install the latest version of each package, but in this case, other choices are possible."
|
|
530
493
|
)
|
|
531
494
|
if not user_input.ask_for_yesno_confirmation(
|
|
532
495
|
logger,
|
|
533
|
-
|
|
496
|
+
"Would you like to customize package versions?",
|
|
534
497
|
):
|
|
535
498
|
return pkg_atoms
|
|
536
499
|
|
|
537
500
|
while True: # Loop to allow restarting the selection process
|
|
538
501
|
result: list[str] = []
|
|
539
|
-
logger.stdout(
|
|
502
|
+
logger.stdout("\n[bold]Package Version Selection[/]")
|
|
540
503
|
|
|
541
504
|
for atom_str in pkg_atoms:
|
|
542
505
|
# Parse the atom to get package name
|
|
@@ -544,26 +507,18 @@ def customize_package_versions(
|
|
|
544
507
|
if isinstance(a, ExprAtom):
|
|
545
508
|
# If it's already an expression with version constraints, show the constraints
|
|
546
509
|
logger.stdout(
|
|
547
|
-
|
|
548
|
-
"\nPackage [green]{atom}[/] already has version constraints."
|
|
549
|
-
).format(
|
|
550
|
-
atom=atom_str,
|
|
551
|
-
)
|
|
510
|
+
f"\nPackage [green]{atom_str}[/] already has version constraints."
|
|
552
511
|
)
|
|
553
512
|
if not user_input.ask_for_yesno_confirmation(
|
|
554
513
|
logger,
|
|
555
|
-
|
|
514
|
+
"Would you like to change them?",
|
|
556
515
|
):
|
|
557
516
|
result.append(atom_str)
|
|
558
517
|
continue
|
|
559
518
|
elif isinstance(a, SlugAtom):
|
|
560
519
|
# Slugs already fix the version, so we can't change them
|
|
561
520
|
logger.W(
|
|
562
|
-
|
|
563
|
-
"version cannot be overridden for slug atom [green]{atom}[/]"
|
|
564
|
-
).format(
|
|
565
|
-
atom=atom_str,
|
|
566
|
-
)
|
|
521
|
+
f"version cannot be overridden for slug atom [green]{atom_str}[/]"
|
|
567
522
|
)
|
|
568
523
|
result.append(atom_str)
|
|
569
524
|
continue
|
|
@@ -571,24 +526,19 @@ def customize_package_versions(
|
|
|
571
526
|
# Get all available versions for this package
|
|
572
527
|
package_name = a.name
|
|
573
528
|
category = a.category
|
|
574
|
-
pkg_fullname = f"{category}/{package_name}" if category else package_name
|
|
575
529
|
|
|
576
530
|
available_versions: "list[BoundPackageManifest]" = []
|
|
577
531
|
try:
|
|
578
532
|
available_versions = list(mr.iter_pkg_vers(package_name, category))
|
|
579
533
|
except KeyError:
|
|
580
534
|
logger.W(
|
|
581
|
-
|
|
582
|
-
pkg=pkg_fullname
|
|
583
|
-
)
|
|
535
|
+
f"could not find package [yellow]{category}/{package_name}[/] in repository"
|
|
584
536
|
)
|
|
585
537
|
result.append(atom_str)
|
|
586
538
|
|
|
587
539
|
if not available_versions:
|
|
588
540
|
logger.W(
|
|
589
|
-
|
|
590
|
-
pkg=pkg_fullname
|
|
591
|
-
)
|
|
541
|
+
f"no versions found for package [yellow]{category}/{package_name}[/]"
|
|
592
542
|
)
|
|
593
543
|
result.append(atom_str)
|
|
594
544
|
continue
|
|
@@ -597,12 +547,7 @@ def customize_package_versions(
|
|
|
597
547
|
# If there's only one version available, use it
|
|
598
548
|
selected_version = available_versions[0]
|
|
599
549
|
logger.stdout(
|
|
600
|
-
|
|
601
|
-
"Only one version available for [green]{pkg}[/]: [blue]{ver}[/], using it."
|
|
602
|
-
).format(
|
|
603
|
-
pkg=pkg_fullname,
|
|
604
|
-
ver=selected_version.ver,
|
|
605
|
-
)
|
|
550
|
+
f"Only one version available for [green]{category}/{package_name}[/]: [blue]{selected_version.ver}[/], using it."
|
|
606
551
|
)
|
|
607
552
|
result.append(atom_str)
|
|
608
553
|
continue
|
|
@@ -617,15 +562,11 @@ def customize_package_versions(
|
|
|
617
562
|
remarks = []
|
|
618
563
|
|
|
619
564
|
if pm.is_prerelease:
|
|
620
|
-
remarks.append(
|
|
565
|
+
remarks.append("prerelease")
|
|
621
566
|
if pm.service_level.has_known_issues:
|
|
622
|
-
remarks.append(
|
|
567
|
+
remarks.append("has known issues")
|
|
623
568
|
if pm.upstream_version:
|
|
624
|
-
remarks.append(
|
|
625
|
-
_("upstream: {upstream_ver}").format(
|
|
626
|
-
upstream_ver=pm.upstream_version
|
|
627
|
-
)
|
|
628
|
-
)
|
|
569
|
+
remarks.append(f"upstream: {pm.upstream_version}")
|
|
629
570
|
|
|
630
571
|
remark_str = f" ({', '.join(remarks)})" if remarks else ""
|
|
631
572
|
version_choices.append(f"{version_str}{remark_str}")
|
|
@@ -633,9 +574,7 @@ def customize_package_versions(
|
|
|
633
574
|
# Ask the user to select a version
|
|
634
575
|
version_idx = user_input.ask_for_choice(
|
|
635
576
|
logger,
|
|
636
|
-
|
|
637
|
-
pkg=pkg_fullname,
|
|
638
|
-
),
|
|
577
|
+
f"\nSelect a version for package [green]{category or ''}{('/' + package_name) if category else package_name}[/]:",
|
|
639
578
|
version_choices,
|
|
640
579
|
)
|
|
641
580
|
|
|
@@ -647,27 +586,27 @@ def customize_package_versions(
|
|
|
647
586
|
else:
|
|
648
587
|
new_atom = f"{package_name}(=={selected_version.ver})"
|
|
649
588
|
|
|
650
|
-
logger.stdout(
|
|
589
|
+
logger.stdout(f"Selected: [blue]{new_atom}[/]")
|
|
651
590
|
result.append(new_atom)
|
|
652
591
|
|
|
653
|
-
logger.stdout(
|
|
592
|
+
logger.stdout("\nPackage versions to be installed:")
|
|
654
593
|
for atom in result:
|
|
655
594
|
logger.stdout(f" * [green]{atom}[/]")
|
|
656
595
|
|
|
657
596
|
confirmation = user_input.ask_for_choice(
|
|
658
597
|
logger,
|
|
659
|
-
|
|
598
|
+
"\nHow would you like to proceed?",
|
|
660
599
|
[
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
600
|
+
"Continue with these versions",
|
|
601
|
+
"Restart version selection",
|
|
602
|
+
"Abort device provisioning",
|
|
664
603
|
],
|
|
665
604
|
)
|
|
666
605
|
|
|
667
606
|
if confirmation == 0: # Continue with these versions
|
|
668
607
|
return result
|
|
669
608
|
elif confirmation == 1: # Restart version selection
|
|
670
|
-
logger.stdout(
|
|
609
|
+
logger.stdout("\nRestarting package version selection...")
|
|
671
610
|
continue
|
|
672
611
|
else: # Abort installation
|
|
673
612
|
return None
|
ruyi/device/provision_cli.py
CHANGED
|
@@ -2,7 +2,6 @@ import argparse
|
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from ..cli.cmd import RootCommand
|
|
5
|
-
from ..i18n import _
|
|
6
5
|
|
|
7
6
|
if TYPE_CHECKING:
|
|
8
7
|
from ..cli.completion import ArgumentParser
|
|
@@ -13,7 +12,7 @@ class DeviceCommand(
|
|
|
13
12
|
RootCommand,
|
|
14
13
|
cmd="device",
|
|
15
14
|
has_subcommands=True,
|
|
16
|
-
help=
|
|
15
|
+
help="Manage devices",
|
|
17
16
|
):
|
|
18
17
|
@classmethod
|
|
19
18
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
@@ -24,7 +23,7 @@ class DeviceProvisionCommand(
|
|
|
24
23
|
DeviceCommand,
|
|
25
24
|
cmd="provision",
|
|
26
25
|
aliases=["flash"],
|
|
27
|
-
help=
|
|
26
|
+
help="Interactively initialize a device for development",
|
|
28
27
|
):
|
|
29
28
|
@classmethod
|
|
30
29
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
@@ -37,7 +36,5 @@ class DeviceProvisionCommand(
|
|
|
37
36
|
try:
|
|
38
37
|
return do_provision_interactive(cfg)
|
|
39
38
|
except KeyboardInterrupt:
|
|
40
|
-
cfg.logger.stdout(
|
|
41
|
-
_("\n\nKeyboard interrupt received, exiting."), end="\n\n"
|
|
42
|
-
)
|
|
39
|
+
cfg.logger.stdout("\n\nKeyboard interrupt received, exiting.", end="\n\n")
|
|
43
40
|
return 1
|
ruyi/log/__init__.py
CHANGED
|
@@ -11,7 +11,6 @@ if TYPE_CHECKING:
|
|
|
11
11
|
from rich.console import Console, RenderableType
|
|
12
12
|
from rich.text import Text
|
|
13
13
|
|
|
14
|
-
from ..i18n import _
|
|
15
14
|
from ..utils.global_mode import ProvidesGlobalMode
|
|
16
15
|
from ..utils.porcelain import PorcelainEntity, PorcelainEntityType, PorcelainOutput
|
|
17
16
|
|
|
@@ -218,7 +217,7 @@ class RuyiConsoleLogger(RuyiLogger):
|
|
|
218
217
|
return self._emit_porcelain_log("F", message, sep, *objects)
|
|
219
218
|
|
|
220
219
|
return self.log_console.print(
|
|
221
|
-
|
|
220
|
+
f"[bold red]fatal error:[/] {message}",
|
|
222
221
|
*objects,
|
|
223
222
|
sep=sep,
|
|
224
223
|
end=end,
|
|
@@ -235,7 +234,7 @@ class RuyiConsoleLogger(RuyiLogger):
|
|
|
235
234
|
return self._emit_porcelain_log("I", message, sep, *objects)
|
|
236
235
|
|
|
237
236
|
return self.log_console.print(
|
|
238
|
-
|
|
237
|
+
f"[bold green]info:[/] {message}",
|
|
239
238
|
*objects,
|
|
240
239
|
sep=sep,
|
|
241
240
|
end=end,
|
|
@@ -252,7 +251,7 @@ class RuyiConsoleLogger(RuyiLogger):
|
|
|
252
251
|
return self._emit_porcelain_log("W", message, sep, *objects)
|
|
253
252
|
|
|
254
253
|
return self.log_console.print(
|
|
255
|
-
|
|
254
|
+
f"[bold yellow]warn:[/] {message}",
|
|
256
255
|
*objects,
|
|
257
256
|
sep=sep,
|
|
258
257
|
end=end,
|
|
@@ -264,10 +263,10 @@ def humanize_list(
|
|
|
264
263
|
*,
|
|
265
264
|
sep: str = ", ",
|
|
266
265
|
item_color: str | None = None,
|
|
267
|
-
empty_prompt: str
|
|
266
|
+
empty_prompt: str = "(none)",
|
|
268
267
|
) -> str:
|
|
269
268
|
if not obj:
|
|
270
|
-
return empty_prompt
|
|
269
|
+
return empty_prompt
|
|
271
270
|
if item_color is None:
|
|
272
271
|
return sep.join(obj)
|
|
273
272
|
return sep.join(f"[{item_color}]{x}[/]" for x in obj)
|