ruyi 0.41.0b20250924__py3-none-any.whl → 0.42.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/cli/config_cli.py +20 -4
- ruyi/cli/main.py +30 -8
- ruyi/cli/oobe.py +7 -1
- ruyi/cli/self_cli.py +7 -2
- ruyi/config/__init__.py +121 -38
- ruyi/config/editor.py +34 -3
- ruyi/config/errors.py +22 -6
- ruyi/config/schema.py +72 -17
- ruyi/device/provision.py +1 -1
- ruyi/pluginhost/api.py +1 -1
- ruyi/ruyipkg/distfile.py +12 -4
- ruyi/ruyipkg/install.py +99 -32
- ruyi/ruyipkg/install_cli.py +29 -1
- ruyi/ruyipkg/profile.py +148 -1
- ruyi/ruyipkg/repo.py +6 -3
- ruyi/ruyipkg/state.py +10 -0
- ruyi/ruyipkg/unpack.py +14 -16
- ruyi/telemetry/provider.py +65 -22
- ruyi/telemetry/store.py +1 -1
- ruyi/utils/git.py +4 -2
- ruyi/utils/global_mode.py +19 -1
- ruyi/utils/toml.py +11 -0
- ruyi/utils/xdg_basedir.py +20 -13
- ruyi/version.py +2 -49
- {ruyi-0.41.0b20250924.dist-info → ruyi-0.42.0.dist-info}/METADATA +18 -11
- {ruyi-0.41.0b20250924.dist-info → ruyi-0.42.0.dist-info}/RECORD +30 -31
- ruyi/mux/.gitignore +0 -1
- /ruyi/ruyipkg/{fetch.py → fetcher.py} +0 -0
- {ruyi-0.41.0b20250924.dist-info → ruyi-0.42.0.dist-info}/WHEEL +0 -0
- {ruyi-0.41.0b20250924.dist-info → ruyi-0.42.0.dist-info}/entry_points.txt +0 -0
- {ruyi-0.41.0b20250924.dist-info → ruyi-0.42.0.dist-info}/licenses/LICENSE-Apache.txt +0 -0
ruyi/ruyipkg/unpack.py
CHANGED
|
@@ -2,7 +2,7 @@ import mmap
|
|
|
2
2
|
import os
|
|
3
3
|
import shutil
|
|
4
4
|
import subprocess
|
|
5
|
-
from typing import BinaryIO, NoReturn, Protocol
|
|
5
|
+
from typing import Any, BinaryIO, NoReturn, Protocol
|
|
6
6
|
|
|
7
7
|
from ..log import RuyiLogger
|
|
8
8
|
from ..utils import ar, prereqs
|
|
@@ -20,7 +20,7 @@ class SupportsRead(Protocol):
|
|
|
20
20
|
def do_unpack(
|
|
21
21
|
logger: RuyiLogger,
|
|
22
22
|
filename: str,
|
|
23
|
-
dest: str | None,
|
|
23
|
+
dest: str | os.PathLike[Any] | None,
|
|
24
24
|
strip_components: int,
|
|
25
25
|
unpack_method: UnpackMethod,
|
|
26
26
|
stream: BinaryIO | SupportsRead | None = None,
|
|
@@ -77,7 +77,7 @@ def do_unpack(
|
|
|
77
77
|
def do_unpack_or_symlink(
|
|
78
78
|
logger: RuyiLogger,
|
|
79
79
|
filename: str,
|
|
80
|
-
dest: str | None,
|
|
80
|
+
dest: str | os.PathLike[Any] | None,
|
|
81
81
|
strip_components: int,
|
|
82
82
|
unpack_method: UnpackMethod,
|
|
83
83
|
stream: BinaryIO | SupportsRead | None = None,
|
|
@@ -100,7 +100,7 @@ def do_unpack_or_symlink(
|
|
|
100
100
|
|
|
101
101
|
def _do_copy_raw(
|
|
102
102
|
src_path: str,
|
|
103
|
-
destdir: str | None,
|
|
103
|
+
destdir: str | os.PathLike[Any] | None,
|
|
104
104
|
) -> None:
|
|
105
105
|
src_filename = os.path.basename(src_path)
|
|
106
106
|
if destdir is None:
|
|
@@ -114,7 +114,7 @@ def _do_copy_raw(
|
|
|
114
114
|
|
|
115
115
|
def do_symlink(
|
|
116
116
|
src_path: str,
|
|
117
|
-
destdir: str | None,
|
|
117
|
+
destdir: str | os.PathLike[Any] | None,
|
|
118
118
|
) -> None:
|
|
119
119
|
src_filename = os.path.basename(src_path)
|
|
120
120
|
if destdir is None:
|
|
@@ -132,7 +132,7 @@ def do_symlink(
|
|
|
132
132
|
def _do_unpack_tar(
|
|
133
133
|
logger: RuyiLogger,
|
|
134
134
|
filename: str,
|
|
135
|
-
dest: str | None,
|
|
135
|
+
dest: str | os.PathLike[Any] | None,
|
|
136
136
|
strip_components: int,
|
|
137
137
|
unpack_method: UnpackMethod,
|
|
138
138
|
stream: SupportsRead | None,
|
|
@@ -164,8 +164,6 @@ def _do_unpack_tar(
|
|
|
164
164
|
stdin = subprocess.PIPE
|
|
165
165
|
|
|
166
166
|
argv.extend(("-f", filename, f"--strip-components={strip_components}"))
|
|
167
|
-
if dest is not None:
|
|
168
|
-
argv.extend(("-C", dest))
|
|
169
167
|
if prefixes_to_unpack:
|
|
170
168
|
if any(p.startswith("-") for p in prefixes_to_unpack):
|
|
171
169
|
raise ValueError(
|
|
@@ -200,11 +198,11 @@ def _do_unpack_tar(
|
|
|
200
198
|
def _do_unpack_zip(
|
|
201
199
|
logger: RuyiLogger,
|
|
202
200
|
filename: str,
|
|
203
|
-
dest: str | None,
|
|
201
|
+
dest: str | os.PathLike[Any] | None,
|
|
204
202
|
) -> None:
|
|
205
203
|
argv = ["unzip", filename]
|
|
206
204
|
if dest is not None:
|
|
207
|
-
argv.extend(("-d", dest))
|
|
205
|
+
argv.extend(("-d", str(dest)))
|
|
208
206
|
logger.D(f"about to call unzip: argv={argv}")
|
|
209
207
|
retcode = subprocess.call(argv, cwd=dest)
|
|
210
208
|
if retcode != 0:
|
|
@@ -214,7 +212,7 @@ def _do_unpack_zip(
|
|
|
214
212
|
def _do_unpack_bare_gz(
|
|
215
213
|
logger: RuyiLogger,
|
|
216
214
|
filename: str,
|
|
217
|
-
destdir: str | None,
|
|
215
|
+
destdir: str | os.PathLike[Any] | None,
|
|
218
216
|
) -> None:
|
|
219
217
|
# the suffix may not be ".gz" so do this generically
|
|
220
218
|
dest_filename = os.path.splitext(os.path.basename(filename))[0]
|
|
@@ -235,7 +233,7 @@ def _do_unpack_bare_gz(
|
|
|
235
233
|
def _do_unpack_bare_bzip2(
|
|
236
234
|
logger: RuyiLogger,
|
|
237
235
|
filename: str,
|
|
238
|
-
destdir: str | None,
|
|
236
|
+
destdir: str | os.PathLike[Any] | None,
|
|
239
237
|
) -> None:
|
|
240
238
|
# the suffix may not be ".bz2" so do this generically
|
|
241
239
|
dest_filename = os.path.splitext(os.path.basename(filename))[0]
|
|
@@ -256,7 +254,7 @@ def _do_unpack_bare_bzip2(
|
|
|
256
254
|
def _do_unpack_bare_lz4(
|
|
257
255
|
logger: RuyiLogger,
|
|
258
256
|
filename: str,
|
|
259
|
-
destdir: str | None,
|
|
257
|
+
destdir: str | os.PathLike[Any] | None,
|
|
260
258
|
) -> None:
|
|
261
259
|
# the suffix may not be ".lz4" so do this generically
|
|
262
260
|
dest_filename = os.path.splitext(os.path.basename(filename))[0]
|
|
@@ -271,7 +269,7 @@ def _do_unpack_bare_lz4(
|
|
|
271
269
|
def _do_unpack_bare_xz(
|
|
272
270
|
logger: RuyiLogger,
|
|
273
271
|
filename: str,
|
|
274
|
-
destdir: str | None,
|
|
272
|
+
destdir: str | os.PathLike[Any] | None,
|
|
275
273
|
) -> None:
|
|
276
274
|
# the suffix may not be ".xz" so do this generically
|
|
277
275
|
dest_filename = os.path.splitext(os.path.basename(filename))[0]
|
|
@@ -292,7 +290,7 @@ def _do_unpack_bare_xz(
|
|
|
292
290
|
def _do_unpack_bare_zstd(
|
|
293
291
|
logger: RuyiLogger,
|
|
294
292
|
filename: str,
|
|
295
|
-
destdir: str | None,
|
|
293
|
+
destdir: str | os.PathLike[Any] | None,
|
|
296
294
|
) -> None:
|
|
297
295
|
# the suffix may not be ".zst" so do this generically
|
|
298
296
|
dest_filename = os.path.splitext(os.path.basename(filename))[0]
|
|
@@ -307,7 +305,7 @@ def _do_unpack_bare_zstd(
|
|
|
307
305
|
def _do_unpack_deb(
|
|
308
306
|
logger: RuyiLogger,
|
|
309
307
|
filename: str,
|
|
310
|
-
destdir: str | None,
|
|
308
|
+
destdir: str | os.PathLike[Any] | None,
|
|
311
309
|
) -> None:
|
|
312
310
|
with ar.ArpyArchiveWrapper(filename) as a:
|
|
313
311
|
for f in a.infolist():
|
ruyi/telemetry/provider.py
CHANGED
|
@@ -18,19 +18,23 @@ if TYPE_CHECKING:
|
|
|
18
18
|
|
|
19
19
|
FALLBACK_PM_TELEMETRY_ENDPOINT = "https://api.ruyisdk.cn/telemetry/pm/"
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
TELEMETRY_CONSENT_AND_UPLOAD_DESC = """
|
|
22
|
+
RuyiSDK collects anonymized usage data locally to help us improve the product.
|
|
23
|
+
|
|
24
|
+
[green]By default, nothing leaves your machine[/], and you can also turn off usage data
|
|
25
|
+
collection completely. Only with your explicit permission can [yellow]ruyi[/] upload
|
|
26
|
+
collected telemetry, periodically to RuyiSDK team-managed servers located in
|
|
27
|
+
the Chinese mainland. You can change this setting at any time by running
|
|
28
|
+
[yellow]ruyi telemetry consent[/], [yellow]ruyi telemetry local[/], or [yellow]ruyi telemetry optout[/].
|
|
29
|
+
|
|
30
|
+
If you enable uploads now, we'll also send a one-time report from this [yellow]ruyi[/]
|
|
31
|
+
installation so the RuyiSDK team can better understand adoption. Thank you for
|
|
32
|
+
helping us build a better experience!
|
|
33
33
|
"""
|
|
34
|
+
TELEMETRY_CONSENT_AND_UPLOAD_PROMPT = (
|
|
35
|
+
"Enable telemetry uploads and send a one-time report now?"
|
|
36
|
+
)
|
|
37
|
+
TELEMETRY_OPTOUT_PROMPT = "\nDo you want to disable telemetry entirely?"
|
|
34
38
|
|
|
35
39
|
|
|
36
40
|
def next_utc_weekday(wday: int, now: float | None = None) -> int:
|
|
@@ -66,12 +70,17 @@ def set_telemetry_mode(
|
|
|
66
70
|
|
|
67
71
|
logger = gc.logger
|
|
68
72
|
|
|
73
|
+
if mode == "on":
|
|
74
|
+
if consent_time is None:
|
|
75
|
+
consent_time = datetime.datetime.now().astimezone()
|
|
76
|
+
else:
|
|
77
|
+
# clear any previously recorded consent time
|
|
78
|
+
consent_time = None
|
|
79
|
+
|
|
80
|
+
# First, persist the changes to user config
|
|
69
81
|
with ConfigEditor.work_on_user_local_config(gc) as ed:
|
|
70
82
|
ed.set_value((schema.SECTION_TELEMETRY, schema.KEY_TELEMETRY_MODE), mode)
|
|
71
|
-
|
|
72
|
-
if mode == "on":
|
|
73
|
-
if consent_time is None:
|
|
74
|
-
consent_time = datetime.datetime.now().astimezone()
|
|
83
|
+
if consent_time is not None:
|
|
75
84
|
ed.set_value(
|
|
76
85
|
(schema.SECTION_TELEMETRY, schema.KEY_TELEMETRY_UPLOAD_CONSENT),
|
|
77
86
|
consent_time,
|
|
@@ -83,6 +92,18 @@ def set_telemetry_mode(
|
|
|
83
92
|
|
|
84
93
|
ed.stage()
|
|
85
94
|
|
|
95
|
+
# Then, apply the changes to the running instance's GlobalConfig
|
|
96
|
+
# TelemetryProvider instance (if any) will pick them up automatically
|
|
97
|
+
# because the properties are backed by GlobalConfig.
|
|
98
|
+
gc.set_by_key(
|
|
99
|
+
(schema.SECTION_TELEMETRY, schema.KEY_TELEMETRY_MODE),
|
|
100
|
+
mode,
|
|
101
|
+
)
|
|
102
|
+
gc.set_by_key(
|
|
103
|
+
(schema.SECTION_TELEMETRY, schema.KEY_TELEMETRY_UPLOAD_CONSENT),
|
|
104
|
+
consent_time,
|
|
105
|
+
)
|
|
106
|
+
|
|
86
107
|
if not show_cli_feedback:
|
|
87
108
|
return
|
|
88
109
|
match mode:
|
|
@@ -111,8 +132,6 @@ def set_telemetry_mode(
|
|
|
111
132
|
class TelemetryProvider:
|
|
112
133
|
def __init__(self, gc: "GlobalConfig") -> None:
|
|
113
134
|
self.state_root = pathlib.Path(gc.telemetry_root)
|
|
114
|
-
self.local_mode = gc.telemetry_mode == "local"
|
|
115
|
-
self.upload_consent_time = gc.telemetry_upload_consent_time
|
|
116
135
|
|
|
117
136
|
self._discard_events = False
|
|
118
137
|
self._gc = gc
|
|
@@ -129,6 +148,14 @@ class TelemetryProvider:
|
|
|
129
148
|
def logger(self) -> RuyiLogger:
|
|
130
149
|
return self._gc.logger
|
|
131
150
|
|
|
151
|
+
@property
|
|
152
|
+
def local_mode(self) -> bool:
|
|
153
|
+
return self._gc.telemetry_mode == "local"
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def upload_consent_time(self) -> datetime.datetime | None:
|
|
157
|
+
return self._gc.telemetry_upload_consent_time
|
|
158
|
+
|
|
132
159
|
def store(self, scope: TelemetryScope) -> TelemetryStore | None:
|
|
133
160
|
return self._stores.get(scope)
|
|
134
161
|
|
|
@@ -386,16 +413,32 @@ class TelemetryProvider:
|
|
|
386
413
|
return store.upload_staged_payloads()
|
|
387
414
|
|
|
388
415
|
def oobe_prompt(self) -> None:
|
|
389
|
-
"""Ask whether the user consents to a first-run telemetry upload
|
|
416
|
+
"""Ask whether the user consents to a first-run telemetry upload, and
|
|
417
|
+
persist the user's exact telemetry choice."""
|
|
390
418
|
|
|
391
419
|
from ..cli import user_input
|
|
392
420
|
|
|
393
|
-
self.logger.stdout(
|
|
421
|
+
self.logger.stdout(TELEMETRY_CONSENT_AND_UPLOAD_DESC)
|
|
394
422
|
if not user_input.ask_for_yesno_confirmation(
|
|
395
423
|
self.logger,
|
|
396
|
-
|
|
397
|
-
|
|
424
|
+
TELEMETRY_CONSENT_AND_UPLOAD_PROMPT,
|
|
425
|
+
False,
|
|
398
426
|
):
|
|
427
|
+
# ask if the user wants to opt out entirely
|
|
428
|
+
if user_input.ask_for_yesno_confirmation(
|
|
429
|
+
self.logger,
|
|
430
|
+
TELEMETRY_OPTOUT_PROMPT,
|
|
431
|
+
False,
|
|
432
|
+
):
|
|
433
|
+
set_telemetry_mode(self._gc, "off")
|
|
434
|
+
return
|
|
435
|
+
|
|
436
|
+
# user wants to stay in local mode
|
|
437
|
+
# explicitly record the preference, so we don't have to worry about
|
|
438
|
+
# us potentially changing defaults yet another time
|
|
439
|
+
set_telemetry_mode(self._gc, "local")
|
|
399
440
|
return
|
|
400
441
|
|
|
442
|
+
consent_time = datetime.datetime.now().astimezone()
|
|
443
|
+
set_telemetry_mode(self._gc, "on", consent_time)
|
|
401
444
|
self._upload_on_exit = True
|
ruyi/telemetry/store.py
CHANGED
ruyi/utils/git.py
CHANGED
|
@@ -119,13 +119,15 @@ def pull_ff_or_die(
|
|
|
119
119
|
logger.I(f"repository: [yellow]{repo_path}[/]")
|
|
120
120
|
logger.I(f"expected remote URL: [yellow]{remote_url}[/]")
|
|
121
121
|
logger.I(f"actual remote URL: [yellow]{remote.url}[/]")
|
|
122
|
-
logger.I("please fix the repo settings manually")
|
|
122
|
+
logger.I("please [bold red]fix the repo settings manually[/]")
|
|
123
123
|
raise SystemExit(1)
|
|
124
124
|
|
|
125
125
|
logger.D(
|
|
126
126
|
f"updating url of remote {remote_name} from {remote.url} to {remote_url}"
|
|
127
127
|
)
|
|
128
|
-
repo.remotes.set_url(
|
|
128
|
+
repo.remotes.set_url(remote_name, remote_url)
|
|
129
|
+
# this needs manual refreshing
|
|
130
|
+
remote = repo.remotes[remote_name]
|
|
129
131
|
|
|
130
132
|
logger.D("fetching")
|
|
131
133
|
try:
|
ruyi/utils/global_mode.py
CHANGED
|
@@ -128,6 +128,24 @@ def _guess_porcelain_from_argv(argv: list[str]) -> bool:
|
|
|
128
128
|
return len(argv) > 1 and argv[1] == "--porcelain"
|
|
129
129
|
|
|
130
130
|
|
|
131
|
+
def _probe_cli_autocomplete(env: Mapping[str, str], argv: list[str]) -> bool:
|
|
132
|
+
"""
|
|
133
|
+
Probe if the current invocation is related to shell completion based on
|
|
134
|
+
the arguments passed, without requiring the ``argparse`` machinery to be
|
|
135
|
+
completely initialized, and the environment.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
# If `--output-completion-script` is present anywhere in the arguments,
|
|
139
|
+
# then this is related to shell completion even if _ARGCOMPLETE is not yet
|
|
140
|
+
# set (which is only set for invocations after the shell finished sourcing
|
|
141
|
+
# the completion script).
|
|
142
|
+
for arg in argv:
|
|
143
|
+
if arg.startswith("--output-completion-script"):
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
return "_ARGCOMPLETE" in os.environ
|
|
147
|
+
|
|
148
|
+
|
|
131
149
|
class EnvGlobalModeProvider(GlobalModeProvider):
|
|
132
150
|
def __init__(
|
|
133
151
|
self,
|
|
@@ -150,7 +168,7 @@ class EnvGlobalModeProvider(GlobalModeProvider):
|
|
|
150
168
|
|
|
151
169
|
# We have to lift this piece of implementation detail out of argcomplete,
|
|
152
170
|
# as the argcomplete import is very costly in terms of startup time.
|
|
153
|
-
self._is_cli_autocomplete =
|
|
171
|
+
self._is_cli_autocomplete = _probe_cli_autocomplete(env, argv)
|
|
154
172
|
|
|
155
173
|
self._venv_root = env.get(ENV_VENV_ROOT_KEY)
|
|
156
174
|
|
ruyi/utils/toml.py
CHANGED
|
@@ -7,6 +7,17 @@ from tomlkit.container import Container
|
|
|
7
7
|
from tomlkit.items import Array, Comment, InlineTable, Item, Table, Trivia, Whitespace
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
class NoneValue(Exception):
|
|
11
|
+
"""Used to indicate that a None value is to be dumped in TOML. Because TOML
|
|
12
|
+
does not support None natively, this means special handling is needed."""
|
|
13
|
+
|
|
14
|
+
def __str__(self) -> str:
|
|
15
|
+
return "NoneValue()"
|
|
16
|
+
|
|
17
|
+
def __repr__(self) -> str:
|
|
18
|
+
return "NoneValue()"
|
|
19
|
+
|
|
20
|
+
|
|
10
21
|
def with_indent(item: Item, spaces: int = 2) -> Item:
|
|
11
22
|
item.indent(spaces)
|
|
12
23
|
return item
|
ruyi/utils/xdg_basedir.py
CHANGED
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import pathlib
|
|
7
|
-
from typing import Iterable
|
|
7
|
+
from typing import Iterable, NamedTuple
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class XDGPathEntry(NamedTuple):
|
|
11
|
+
path: pathlib.Path
|
|
12
|
+
is_global: bool
|
|
8
13
|
|
|
9
14
|
|
|
10
15
|
def _paths_from_env(env: str, default: str) -> Iterable[pathlib.Path]:
|
|
@@ -38,14 +43,16 @@ class XDGBaseDir:
|
|
|
38
43
|
return pathlib.Path(v) if v else pathlib.Path.home() / ".local" / "state"
|
|
39
44
|
|
|
40
45
|
@property
|
|
41
|
-
def config_dirs(self) -> Iterable[
|
|
46
|
+
def config_dirs(self) -> Iterable[XDGPathEntry]:
|
|
42
47
|
# from highest precedence to lowest
|
|
43
|
-
|
|
48
|
+
for p in _paths_from_env("XDG_CONFIG_DIRS", "/etc/xdg"):
|
|
49
|
+
yield XDGPathEntry(p, True)
|
|
44
50
|
|
|
45
51
|
@property
|
|
46
|
-
def data_dirs(self) -> Iterable[
|
|
52
|
+
def data_dirs(self) -> Iterable[XDGPathEntry]:
|
|
47
53
|
# from highest precedence to lowest
|
|
48
|
-
|
|
54
|
+
for p in _paths_from_env("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/"):
|
|
55
|
+
yield XDGPathEntry(p, True)
|
|
49
56
|
|
|
50
57
|
# derived info
|
|
51
58
|
|
|
@@ -66,15 +73,15 @@ class XDGBaseDir:
|
|
|
66
73
|
return self.state_home / self.app_name
|
|
67
74
|
|
|
68
75
|
@property
|
|
69
|
-
def app_config_dirs(self) -> Iterable[
|
|
76
|
+
def app_config_dirs(self) -> Iterable[XDGPathEntry]:
|
|
70
77
|
# from highest precedence to lowest
|
|
71
|
-
yield self.app_config
|
|
72
|
-
for
|
|
73
|
-
yield
|
|
78
|
+
yield XDGPathEntry(self.app_config, False)
|
|
79
|
+
for e in self.config_dirs:
|
|
80
|
+
yield XDGPathEntry(e.path / self.app_name, e.is_global)
|
|
74
81
|
|
|
75
82
|
@property
|
|
76
|
-
def app_data_dirs(self) -> Iterable[
|
|
83
|
+
def app_data_dirs(self) -> Iterable[XDGPathEntry]:
|
|
77
84
|
# from highest precedence to lowest
|
|
78
|
-
yield self.app_data
|
|
79
|
-
for
|
|
80
|
-
yield
|
|
85
|
+
yield XDGPathEntry(self.app_data, False)
|
|
86
|
+
for e in self.data_dirs:
|
|
87
|
+
yield XDGPathEntry(e.path / self.app_name, e.is_global)
|
ruyi/version.py
CHANGED
|
@@ -1,53 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
from typing import Final, TYPE_CHECKING
|
|
1
|
+
from typing import Final
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if TYPE_CHECKING:
|
|
7
|
-
# pyright only works with semver 3.x
|
|
8
|
-
from semver.version import Version
|
|
9
|
-
else:
|
|
10
|
-
try:
|
|
11
|
-
from semver.version import Version # type: ignore[import-untyped,unused-ignore]
|
|
12
|
-
except ModuleNotFoundError:
|
|
13
|
-
# semver 2.x
|
|
14
|
-
from semver import VersionInfo as Version # type: ignore[import-untyped,unused-ignore]
|
|
15
|
-
|
|
16
|
-
# NOTE: one cannot print logs in the version helpers, because the version info
|
|
17
|
-
# is initialized so early (before argparse can look at argv because --version
|
|
18
|
-
# requires version info to be ready) that the porcelain status is not yet
|
|
19
|
-
# available.
|
|
20
|
-
|
|
21
|
-
_PYPI_PRERELEASE_KINDS_MAP: Final = {
|
|
22
|
-
"a": "alpha",
|
|
23
|
-
"b": "beta",
|
|
24
|
-
"rc": "rc",
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# based on https://python-semver.readthedocs.io/en/3.0.2/advanced/convert-pypi-to-semver.html
|
|
29
|
-
def _convert2semver(ver: packaging.version.Version) -> Version:
|
|
30
|
-
if ver.epoch:
|
|
31
|
-
raise ValueError("Can't convert an epoch to semver")
|
|
32
|
-
if ver.post:
|
|
33
|
-
raise ValueError("Can't convert a post part to semver")
|
|
34
|
-
|
|
35
|
-
pre: str | None = None
|
|
36
|
-
if ver.pre:
|
|
37
|
-
kind, val = ver.pre
|
|
38
|
-
pre = f"{_PYPI_PRERELEASE_KINDS_MAP.get(kind, kind)}.{val}"
|
|
39
|
-
|
|
40
|
-
maj, min, pat = ver.release[:3]
|
|
41
|
-
return Version(maj, min, pat, prerelease=pre, build=ver.dev)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def _init_pkg_semver() -> Version:
|
|
45
|
-
pkg_pypi_ver = packaging.version.Version(importlib.metadata.version("ruyi"))
|
|
46
|
-
# log.D(f"PyPI-style version of ruyi: {pkg_pypi_ver}")
|
|
47
|
-
return _convert2semver(pkg_pypi_ver)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
RUYI_SEMVER: Final = _init_pkg_semver()
|
|
3
|
+
RUYI_SEMVER: Final = "0.42.0"
|
|
51
4
|
RUYI_USER_AGENT: Final = f"ruyi/{RUYI_SEMVER}"
|
|
52
5
|
|
|
53
6
|
COPYRIGHT_NOTICE: Final = """\
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ruyi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.42.0
|
|
4
4
|
Summary: Package manager for RuyiSDK
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -221,7 +221,7 @@ Classifier: Topic :: Software Development :: Build Tools
|
|
|
221
221
|
Classifier: Topic :: Software Development :: Embedded Systems
|
|
222
222
|
Classifier: Topic :: System :: Software Distribution
|
|
223
223
|
Classifier: Typing :: Typed
|
|
224
|
-
Requires-Dist: argcomplete (>=2.0.0
|
|
224
|
+
Requires-Dist: argcomplete (>=2.0.0)
|
|
225
225
|
Requires-Dist: arpy
|
|
226
226
|
Requires-Dist: fastjsonschema (>=2.15.1)
|
|
227
227
|
Requires-Dist: jinja2 (>=3,<4)
|
|
@@ -332,6 +332,10 @@ Various aspects of `ruyi` can be configured with files or environment variables.
|
|
|
332
332
|
look up its config accordingly. If these are not explicitly set though, as in
|
|
333
333
|
typical use cases, the default config directory is most likely `~/.config/ruyi`.
|
|
334
334
|
|
|
335
|
+
GNU/Linux distribution packagers and system administrators will find that the
|
|
336
|
+
directories `/usr/share/ruyi` and `/usr/local/share/ruyi` are searched for the
|
|
337
|
+
config file on such systems.
|
|
338
|
+
|
|
335
339
|
### Config file
|
|
336
340
|
|
|
337
341
|
Currently `ruyi` will look for an optional `config.toml` in its XDG config
|
|
@@ -361,9 +365,8 @@ branch = "main"
|
|
|
361
365
|
# details.
|
|
362
366
|
#
|
|
363
367
|
# If unset or empty, this default value is used: data will be collected and
|
|
364
|
-
#
|
|
365
|
-
|
|
366
|
-
mode = "on"
|
|
368
|
+
# stored locally; nothing will be uploaded automatically.
|
|
369
|
+
mode = "local"
|
|
367
370
|
# The time the user's consent is given to telemetry data uploading. If the
|
|
368
371
|
# system time is later than the time given here, telemetry consent banner will
|
|
369
372
|
# not be displayed any more each time `ruyi` is executed. The exact consent
|
|
@@ -407,12 +410,16 @@ There are 3 telemetry modes available:
|
|
|
407
410
|
* `off`: data will not be collected nor uploaded.
|
|
408
411
|
* `on`: data will be collected and periodically uploaded.
|
|
409
412
|
|
|
410
|
-
By default the `
|
|
411
|
-
will record some non-sensitive information locally
|
|
412
|
-
states of `ruyi`,
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
413
|
+
By default the `local` mode is active from `ruyi` 0.42.0 (inclusive) on, which
|
|
414
|
+
means every `ruyi` invocation will record some non-sensitive information locally
|
|
415
|
+
alongside various other states of `ruyi`, but collected data will not be
|
|
416
|
+
uploaded automatically unless you explicitly request so (for example by
|
|
417
|
+
switching to the `on` mode, or by executing `ruyi telemetry upload`).
|
|
418
|
+
|
|
419
|
+
In case the `on` mode is active, collected data will be periodically uploaded
|
|
420
|
+
to servers managed by the RuyiSDK team in the People's Republic of China, in a
|
|
421
|
+
weekly fashion. The upload will happen on a random weekday which is determined
|
|
422
|
+
by the installation's anonymous ID alone.
|
|
416
423
|
|
|
417
424
|
You can change the telemetry mode by editing `ruyi`'s config file, or simply
|
|
418
425
|
disable telemetry altogether by setting the `RUYI_TELEMETRY_OPTOUT` environment
|
|
@@ -5,22 +5,21 @@ ruyi/cli/builtin_commands.py,sha256=cYyPSF00DSBH1WMv6mHcMygbFRBGXObMWhbXHs5K1Mc,
|
|
|
5
5
|
ruyi/cli/cmd.py,sha256=kR3aEiDE3AfPoP0Zr7MO-09CKoExbkLLmPvve9oKaUg,6725
|
|
6
6
|
ruyi/cli/completer.py,sha256=cnOkU7veDe-jP8ROXZL2uBop2HgWfaAZl6dromnPLx8,1426
|
|
7
7
|
ruyi/cli/completion.py,sha256=ffLs3Dv7pY_uinwH98wkBPohRvAjpUOGqy01OTA_Bgo,841
|
|
8
|
-
ruyi/cli/config_cli.py,sha256=
|
|
9
|
-
ruyi/cli/main.py,sha256=
|
|
10
|
-
ruyi/cli/oobe.py,sha256=
|
|
11
|
-
ruyi/cli/self_cli.py,sha256=
|
|
8
|
+
ruyi/cli/config_cli.py,sha256=9kq5W3Ir_lfwImhvrUmQ1KTKy1aRCv_UU1CmL5eGyJs,4038
|
|
9
|
+
ruyi/cli/main.py,sha256=qWgCKbWG8sHxM7rInkoQfzZOHKo2k8JwdgJp_AMH388,4455
|
|
10
|
+
ruyi/cli/oobe.py,sha256=3m7oLPBhzn5JgagwQYDPy8vhZ4v8mBiDlO5n4cR_jVA,2712
|
|
11
|
+
ruyi/cli/self_cli.py,sha256=UEX3Xjy8RGGn9W2O5cM-5rLCwpsM7uSQ9vYYBYxyjyQ,8837
|
|
12
12
|
ruyi/cli/user_input.py,sha256=ZJPyCAD7Aizkt37f_KDtW683aKvGZ2pL85Rg_y1LLfg,3711
|
|
13
13
|
ruyi/cli/version_cli.py,sha256=L2pejZ7LIPYLUTb9NIz4t51KB8ai8igPBuE64tyqUuI,1275
|
|
14
|
-
ruyi/config/__init__.py,sha256=
|
|
15
|
-
ruyi/config/editor.py,sha256=
|
|
16
|
-
ruyi/config/errors.py,sha256=
|
|
14
|
+
ruyi/config/__init__.py,sha256=9w1dtgvNxIW65eWzhRN4gl5-460hv-GipIoRZ_oqfXc,16859
|
|
15
|
+
ruyi/config/editor.py,sha256=piAJ-a68iX6IFWXy1g9OXoSs_3DardZwoaSPdStKKL0,4243
|
|
16
|
+
ruyi/config/errors.py,sha256=Yrzd3hX5Gb_bCawoVrWjFrLrpO-q_IOtg4ikE0P8ia0,2561
|
|
17
17
|
ruyi/config/news.py,sha256=83LjQjJHsqOPdRrytG7VBFubG6pyDwJ-Mg37gpBRU20,1061
|
|
18
|
-
ruyi/config/schema.py,sha256=
|
|
18
|
+
ruyi/config/schema.py,sha256=u0TdpoUV6I7ZGZKlHAAGnqZbfaoTiA45UsM5c82M99M,7405
|
|
19
19
|
ruyi/device/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
ruyi/device/provision.py,sha256=
|
|
20
|
+
ruyi/device/provision.py,sha256=9rUbLtybBQ8x9qRXaMwz_yAMf51vb3wmYE0qm54P5Bk,20906
|
|
21
21
|
ruyi/device/provision_cli.py,sha256=sc6AF8ohWrXA-kIAYdZcD6sl1HHbj_dH2cCi-pjjOQg,1031
|
|
22
22
|
ruyi/log/__init__.py,sha256=ehgUl8iY1oRfP_nJKrln5lnvJFSPPU1J-3g-PK28YWk,6516
|
|
23
|
-
ruyi/mux/.gitignore,sha256=qszWGLxNyQrD4ZOh1D_CXo6rGCLfIApDlNVm28J7Hww,6
|
|
24
23
|
ruyi/mux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
24
|
ruyi/mux/runtime.py,sha256=_d7p-4w2bA2jgCsgcEZxiV1HPnJqdFp0cy4uMGNBsd8,7116
|
|
26
25
|
ruyi/mux/venv/__init__.py,sha256=D_1H2rk3YeNiS_yfU5ie78vQBNXSQ2fKexUC2AiprNE,305
|
|
@@ -29,7 +28,7 @@ ruyi/mux/venv/maker.py,sha256=RzipQWANOpKZxXGG_XiExyoQzHBw-j6wew7Iu6hZwZE,27234
|
|
|
29
28
|
ruyi/mux/venv/venv_cli.py,sha256=rIKNcYKu1j7ahHXNgv_ytYVR7qJOoly-TaHPvb21FGA,2974
|
|
30
29
|
ruyi/mux/venv_cfg.py,sha256=m75JCVLFWE1gE8OzcNDOHqwUc2c6ikJhZ-GhjsXv94U,6840
|
|
31
30
|
ruyi/pluginhost/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
-
ruyi/pluginhost/api.py,sha256=
|
|
31
|
+
ruyi/pluginhost/api.py,sha256=Zh69xJfR7PEbM6fjoFZRNqeqTneWAmC71NjA76HodSY,5711
|
|
33
32
|
ruyi/pluginhost/ctx.py,sha256=MsP1L7nchnbSrauId3GaQzv0YJLnWE0u_H3ZFEmjUX8,6590
|
|
34
33
|
ruyi/pluginhost/paths.py,sha256=3EVY3_i3LLff4Lk9py-E317C7_ysiRatfCiuxvCsVWw,4227
|
|
35
34
|
ruyi/pluginhost/plugin_cli.py,sha256=rLdXG4_OJ2nlaSjFBmTaclx_N2f0s_Wi-9qcSeq7h9I,968
|
|
@@ -46,14 +45,14 @@ ruyi/ruyipkg/augmented_pkg.py,sha256=QbzYNHCRanFx9xwYRosN_dAEzOSxmOBxyBVli8SC9FE
|
|
|
46
45
|
ruyi/ruyipkg/canonical_dump.py,sha256=Qu25YXwJpjWUBte0C3bmmaJxe7zyqfN-2-__u2_dJDM,9363
|
|
47
46
|
ruyi/ruyipkg/checksum.py,sha256=ChYFPXl7-Y8p_bDerkXOGroRz4k6ejjWY9ViaVWuEgk,1274
|
|
48
47
|
ruyi/ruyipkg/cli_completion.py,sha256=kJf7vN5rXi5zAwTI3dr4J8HdUzR3-ZXnsdaNQV6kqzo,1151
|
|
49
|
-
ruyi/ruyipkg/distfile.py,sha256=
|
|
48
|
+
ruyi/ruyipkg/distfile.py,sha256=wkyYVcSM2b3vD462TtoltdLcUZElCwWvcZBiPG3lTZ0,7002
|
|
50
49
|
ruyi/ruyipkg/entity.py,sha256=s8h5kNaR2vsP6v_QUFIO6SZiUrt0jrb8GU11bNT6fn4,14498
|
|
51
50
|
ruyi/ruyipkg/entity_cli.py,sha256=hF66i3sJX8XVLIWq376GA9vzgegfQy-6s0x0L831Irk,3802
|
|
52
51
|
ruyi/ruyipkg/entity_provider.py,sha256=jDfS2Jh01PVHo0kb1XyI5WkZgL4fv21AooXLLwqK-1I,8741
|
|
53
|
-
ruyi/ruyipkg/
|
|
52
|
+
ruyi/ruyipkg/fetcher.py,sha256=_btz2hkTz0uEUCCSAhOK7tlhAuvzCu42ZTUCP-RpU7U,9047
|
|
54
53
|
ruyi/ruyipkg/host.py,sha256=pmqgggi7koDCWgzFexwHpycv4SZ07VF6xUbi4s8FSKA,1399
|
|
55
|
-
ruyi/ruyipkg/install.py,sha256=
|
|
56
|
-
ruyi/ruyipkg/install_cli.py,sha256=
|
|
54
|
+
ruyi/ruyipkg/install.py,sha256=RhJwIqGxMveWrGmpP-2uWpZBRnqkA75HwyHwrNTdqow,18176
|
|
55
|
+
ruyi/ruyipkg/install_cli.py,sha256=joIV5iY4iblDinoH2pgbHNYkMVWqMxtVNj89T0IpJuk,5267
|
|
57
56
|
ruyi/ruyipkg/list.py,sha256=iO7666xFEWKTDNtJqVLaaQKsp0BfLTzgXBwFFHrqCGc,4172
|
|
58
57
|
ruyi/ruyipkg/list_cli.py,sha256=9tRWWRcJv3yJqzup6Oc89E3xahGp5jIfybfEvwwd55I,2243
|
|
59
58
|
ruyi/ruyipkg/list_filter.py,sha256=F64_UhwUEiaUR73EkLu91qoUBA-Yz9mEVWj8XY46MXQ,5467
|
|
@@ -62,28 +61,28 @@ ruyi/ruyipkg/news.py,sha256=5uN4UYqvEW5MU_10W9MzwFo3lH25oZCvMMjfAjLed7U,3857
|
|
|
62
61
|
ruyi/ruyipkg/news_cli.py,sha256=s5mirEBmO2lZITfjcpzDakP7rNYPqopdM9odbwmAPpg,2378
|
|
63
62
|
ruyi/ruyipkg/news_store.py,sha256=ocTO4YDpL_HrKaIRFvJ_Gxvpmr7V7-R8b1B8f5Eue9c,5276
|
|
64
63
|
ruyi/ruyipkg/pkg_manifest.py,sha256=FmksKjQyBnU4zA3MFXiHdB2EjNmhs-J9km9vE-zBQlk,18836
|
|
65
|
-
ruyi/ruyipkg/profile.py,sha256=
|
|
64
|
+
ruyi/ruyipkg/profile.py,sha256=6xwL24crALShqD8bazGOIAFTYCpM_91mD8d6YMxM2FU,10762
|
|
66
65
|
ruyi/ruyipkg/profile_cli.py,sha256=ud9MS9JQLOtec_1tFRu669I-imVfi1DHU3AuBJym9mw,848
|
|
67
66
|
ruyi/ruyipkg/protocols.py,sha256=lPKRaAcK3DY3wmkyO2tKpFvPQ_4QA4aSNNsNLvi78O8,1833
|
|
68
|
-
ruyi/ruyipkg/repo.py,sha256=
|
|
69
|
-
ruyi/ruyipkg/state.py,sha256=
|
|
70
|
-
ruyi/ruyipkg/unpack.py,sha256=
|
|
67
|
+
ruyi/ruyipkg/repo.py,sha256=l5d4XPZS-ofbwlAOk5GZMM7AFGay1n1xDVzge935oA4,25231
|
|
68
|
+
ruyi/ruyipkg/state.py,sha256=Ie4vrt79Wp9P0L36tMj36tOtDlFnIb6uwVGDQCQyg8s,11544
|
|
69
|
+
ruyi/ruyipkg/unpack.py,sha256=hPd-lOnDXp1lEb4mdbLUEckT4QXblSFxz-dGAdwaAjc,11207
|
|
71
70
|
ruyi/ruyipkg/unpack_method.py,sha256=MonFFvcDb7MVsi2w4yitnCeZkmWmS7nRMMY-wSt9AMs,2106
|
|
72
71
|
ruyi/ruyipkg/update_cli.py,sha256=ywsAAUPctJ3_2qPDvFL2__ql9m8tGZ6ZfrwbSk30Xh4,1425
|
|
73
72
|
ruyi/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
74
73
|
ruyi/telemetry/aggregate.py,sha256=Ybt25xrg0n62A9Ipslv18EBC-kLkyYsPeEzgguTtChg,1981
|
|
75
74
|
ruyi/telemetry/event.py,sha256=GZnFj6E59Q7mjp-2VRApAZH3rT_bu4_cWb5QMCPm-Zc,982
|
|
76
75
|
ruyi/telemetry/node_info.py,sha256=ix-udhoKQI-HV7qVeEbd-jy3ZyhXL62sVA9uX4fowEg,5178
|
|
77
|
-
ruyi/telemetry/provider.py,sha256=
|
|
76
|
+
ruyi/telemetry/provider.py,sha256=U88ydGga3dwYAi7mfNKmEEyfhAZ4xelF2L-jNAk29IY,16486
|
|
78
77
|
ruyi/telemetry/scope.py,sha256=e45VPAvRAqSxrL0ESorN9SCnR_I6Bwi2CMPJDDshJEE,1133
|
|
79
|
-
ruyi/telemetry/store.py,sha256=
|
|
78
|
+
ruyi/telemetry/store.py,sha256=jodRQ7aRye9qYfvc0uaPQ5DLYGmYYuJtb8dnUN9KVs8,8010
|
|
80
79
|
ruyi/telemetry/telemetry_cli.py,sha256=PBVMUSE3P6IBKQVMji_bueVenCdbchbOlowySXy0468,3364
|
|
81
80
|
ruyi/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
82
81
|
ruyi/utils/ar.py,sha256=w9wiYYdbInLewr5IRTcP0TiOw2ibVDQEmnV0hHm9WlA,2271
|
|
83
82
|
ruyi/utils/ci.py,sha256=66DBm4ooA7yozDtXCJFd1n2jJXTsEnxPSpkNzLfE28M,2970
|
|
84
83
|
ruyi/utils/frontmatter.py,sha256=4EOohEYCZ_q6ncpDv7ktJYf9PN4WEdgFfdE9hZBV3Zg,1052
|
|
85
|
-
ruyi/utils/git.py,sha256=
|
|
86
|
-
ruyi/utils/global_mode.py,sha256=
|
|
84
|
+
ruyi/utils/git.py,sha256=YspRRkfxLXluCv4LNx6q_mjkPdoX7WSM9aR7EfudqlM,5991
|
|
85
|
+
ruyi/utils/global_mode.py,sha256=9Y17rVDm157EKvU6GJ8K1rnVTXnYyLN5e_7psObne00,5691
|
|
87
86
|
ruyi/utils/l10n.py,sha256=l003oQ5M8fWIKQHbYTVSc6oHzFFGU2sbKac7Hh6FNFU,2530
|
|
88
87
|
ruyi/utils/markdown.py,sha256=Mpq--ClM4j9lm_-5zO53ptYePUTLI4rg0V1YshOwsf8,2654
|
|
89
88
|
ruyi/utils/mounts.py,sha256=31BGsVOpD2bO7PYpm8I1ogDHzP3MvrMWznKonS3Ajos,1210
|
|
@@ -92,12 +91,12 @@ ruyi/utils/porcelain.py,sha256=pF6ieSE2xlnC0HBADFY0m-uuwVNNME3wlbHo2jWdLFA,1403
|
|
|
92
91
|
ruyi/utils/prereqs.py,sha256=oWAaH-smpTMQxpHt782YBxqHxrTaheataslN988-a78,2076
|
|
93
92
|
ruyi/utils/ssl_patch.py,sha256=a5gf4br6nC39wTHsqiFtcJ-mGzqB-YzK6DHSeERLaHQ,5661
|
|
94
93
|
ruyi/utils/templating.py,sha256=94xBJTkIfDqmUBTc9hnLO54zQoC7hwGWONGF3YbaqHk,966
|
|
95
|
-
ruyi/utils/toml.py,sha256=
|
|
94
|
+
ruyi/utils/toml.py,sha256=aniIF3SGfR69_s3GWWwlnoKxW4B5IDVY2CM0eUI55_c,3501
|
|
96
95
|
ruyi/utils/url.py,sha256=Wyct6syS4GmZC6mY7SK-YgBWxKl3cOOBXtp9UtvGkto,186
|
|
97
|
-
ruyi/utils/xdg_basedir.py,sha256=
|
|
98
|
-
ruyi/version.py,sha256=
|
|
99
|
-
ruyi-0.
|
|
100
|
-
ruyi-0.
|
|
101
|
-
ruyi-0.
|
|
102
|
-
ruyi-0.
|
|
103
|
-
ruyi-0.
|
|
96
|
+
ruyi/utils/xdg_basedir.py,sha256=RwVH199jPcLVsg5ngR62RaNS5hqnMpkdt31LqkCfa1g,2751
|
|
97
|
+
ruyi/version.py,sha256=womuPV6RmxG2TAe5KhRnuQctXR6qLV3be8xJN3QqUdI,595
|
|
98
|
+
ruyi-0.42.0.dist-info/METADATA,sha256=Te5Fou6OsjO0de6Yk3V9Ei6o3jz_2izFKuSgAchaznM,24266
|
|
99
|
+
ruyi-0.42.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
100
|
+
ruyi-0.42.0.dist-info/entry_points.txt,sha256=GXSNSy7OgFrnlU5xm5dE3l3PGO92Qf6VDIUCdvQNm8E,49
|
|
101
|
+
ruyi-0.42.0.dist-info/licenses/LICENSE-Apache.txt,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
102
|
+
ruyi-0.42.0.dist-info/RECORD,,
|
ruyi/mux/.gitignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
!venv
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|