fast-dev-cli 0.19.1__tar.gz → 0.19.3__tar.gz
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.
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/PKG-INFO +1 -1
- fast_dev_cli-0.19.3/fast_dev_cli/__init__.py +1 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/fast_dev_cli/cli.py +117 -88
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/pyproject.toml +2 -2
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_exec.py +10 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_sync.py +26 -8
- fast_dev_cli-0.19.1/fast_dev_cli/__init__.py +0 -1
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/LICENSE +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/README.md +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/fast_dev_cli/__main__.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/fast_dev_cli/py.typed +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/pdm_build.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/scripts/check.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/scripts/deps.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/scripts/format.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/scripts/test.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/__init__.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/assets/uv-tx.lock +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/assets/uv.lock +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/conftest.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_bump.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_deps.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_fast_test.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_functions.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_help.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_lint.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_poetry_version_plugin.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_pypi.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_runserver.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_tag.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_upgrade.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_upload.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/test_version.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.3}/tests/utils.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.19.3"
|
|
@@ -12,16 +12,7 @@ import subprocess # nosec:B404
|
|
|
12
12
|
import sys
|
|
13
13
|
from functools import cached_property
|
|
14
14
|
from pathlib import Path
|
|
15
|
-
from typing import
|
|
16
|
-
TYPE_CHECKING,
|
|
17
|
-
Any,
|
|
18
|
-
Literal,
|
|
19
|
-
# Optional is required by Option generated by typer
|
|
20
|
-
Optional,
|
|
21
|
-
cast,
|
|
22
|
-
get_args,
|
|
23
|
-
overload,
|
|
24
|
-
)
|
|
15
|
+
from typing import TYPE_CHECKING, Any, Literal, cast, get_args, overload
|
|
25
16
|
|
|
26
17
|
import typer
|
|
27
18
|
from click import UsageError
|
|
@@ -148,14 +139,25 @@ class Shell:
|
|
|
148
139
|
@property
|
|
149
140
|
def command(self) -> list[str] | str:
|
|
150
141
|
command: list[str] | str = self._cmd
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
142
|
+
if isinstance(command, str):
|
|
143
|
+
cs = shlex.split(command)
|
|
144
|
+
if "shell" not in self._kw and not (set(self._cmd) & {"|", ">", "&"}):
|
|
145
|
+
command = self.extend_user(cs)
|
|
146
|
+
elif any(i.startswith("~") for i in cs):
|
|
147
|
+
command = re.sub(r" ~", " " + os.path.expanduser("~"), command)
|
|
148
|
+
else:
|
|
149
|
+
command = self.extend_user(command)
|
|
157
150
|
return command
|
|
158
151
|
|
|
152
|
+
@staticmethod
|
|
153
|
+
def extend_user(cs: list[str]) -> list[str]:
|
|
154
|
+
if cs[0] == "echo":
|
|
155
|
+
return cs
|
|
156
|
+
for i, c in enumerate(cs):
|
|
157
|
+
if c.startswith("~"):
|
|
158
|
+
cs[i] = os.path.expanduser(c)
|
|
159
|
+
return cs
|
|
160
|
+
|
|
159
161
|
def _run(self) -> subprocess.CompletedProcess[str]:
|
|
160
162
|
return self.run_by_subprocess(self.command, **self._kw)
|
|
161
163
|
|
|
@@ -391,7 +393,36 @@ class BumpUp(DryRun):
|
|
|
391
393
|
return is_emoji(first_char)
|
|
392
394
|
|
|
393
395
|
@staticmethod
|
|
396
|
+
def parse_dynamic_version(
|
|
397
|
+
toml_text: str,
|
|
398
|
+
context: dict,
|
|
399
|
+
work_dir: Path | None = None,
|
|
400
|
+
) -> str | None:
|
|
401
|
+
if work_dir is None:
|
|
402
|
+
work_dir = Project.get_work_dir()
|
|
403
|
+
for tool in ("pdm", "hatch"):
|
|
404
|
+
with contextlib.suppress(KeyError):
|
|
405
|
+
version_path = cast(str, context["tool"][tool]["version"]["path"])
|
|
406
|
+
if (
|
|
407
|
+
Path(version_path).exists()
|
|
408
|
+
or work_dir.joinpath(version_path).exists()
|
|
409
|
+
):
|
|
410
|
+
return version_path
|
|
411
|
+
# version = { source = "file", path = "fast_dev_cli/__init__.py" }
|
|
412
|
+
v_key = "version = "
|
|
413
|
+
p_key = 'path = "'
|
|
414
|
+
for line in toml_text.splitlines():
|
|
415
|
+
if not line.startswith(v_key):
|
|
416
|
+
continue
|
|
417
|
+
if p_key in (value := line.split(v_key, 1)[-1].split("#")[0]):
|
|
418
|
+
filename = value.split(p_key, 1)[-1].split('"')[0]
|
|
419
|
+
if work_dir.joinpath(filename).exists():
|
|
420
|
+
return filename
|
|
421
|
+
return None
|
|
422
|
+
|
|
423
|
+
@classmethod
|
|
394
424
|
def parse_filename(
|
|
425
|
+
cls,
|
|
395
426
|
toml_text: str | None = None,
|
|
396
427
|
work_dir: Path | None = None,
|
|
397
428
|
package_name: str | None = None,
|
|
@@ -414,74 +445,55 @@ class BumpUp(DryRun):
|
|
|
414
445
|
try:
|
|
415
446
|
version_value = context["tool"]["poetry"]["version"]
|
|
416
447
|
except KeyError:
|
|
417
|
-
if not Project.manage_by_poetry()
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
with contextlib.suppress(KeyError):
|
|
422
|
-
version_path = cast(
|
|
423
|
-
str, context["tool"][tool]["version"]["path"]
|
|
424
|
-
)
|
|
425
|
-
if (
|
|
426
|
-
Path(version_path).exists()
|
|
427
|
-
or work_dir.joinpath(version_path).exists()
|
|
428
|
-
):
|
|
429
|
-
return version_path
|
|
430
|
-
# version = { source = "file", path = "fast_dev_cli/__init__.py" }
|
|
431
|
-
v_key = "version = "
|
|
432
|
-
p_key = 'path = "'
|
|
433
|
-
for line in toml_text.splitlines():
|
|
434
|
-
if not line.startswith(v_key):
|
|
435
|
-
continue
|
|
436
|
-
if p_key in (value := line.split(v_key, 1)[-1].split("#")[0]):
|
|
437
|
-
filename = value.split(p_key, 1)[-1].split('"')[0]
|
|
438
|
-
if work_dir.joinpath(filename).exists():
|
|
439
|
-
return filename
|
|
448
|
+
if not Project.manage_by_poetry() and (
|
|
449
|
+
filename := cls.parse_dynamic_version(toml_text, context, work_dir)
|
|
450
|
+
):
|
|
451
|
+
return filename
|
|
440
452
|
else:
|
|
441
453
|
by_version_plugin = version_value in ("0", "0.0.0", "init")
|
|
442
454
|
if by_version_plugin:
|
|
455
|
+
return cls.parse_plugin_version(context, package_name)
|
|
456
|
+
|
|
457
|
+
return TOML_FILE
|
|
458
|
+
|
|
459
|
+
@staticmethod
|
|
460
|
+
def parse_plugin_version(context: dict, package_name: str | None) -> str:
|
|
461
|
+
try:
|
|
462
|
+
package_item = context["tool"]["poetry"]["packages"]
|
|
463
|
+
except KeyError:
|
|
443
464
|
try:
|
|
444
|
-
|
|
465
|
+
project_name = context["project"]["name"]
|
|
445
466
|
except KeyError:
|
|
446
|
-
|
|
447
|
-
project_name = context["project"]["name"]
|
|
448
|
-
except KeyError:
|
|
449
|
-
packages = []
|
|
450
|
-
else:
|
|
451
|
-
packages = [(poetry_module_name(project_name), "")]
|
|
467
|
+
packages = []
|
|
452
468
|
else:
|
|
453
|
-
packages = [
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
raise ParseError("Version file not found! Where are you now?")
|
|
482
|
-
return os.path.relpath(init_file, cwd)
|
|
483
|
-
|
|
484
|
-
return TOML_FILE
|
|
469
|
+
packages = [(poetry_module_name(project_name), "")]
|
|
470
|
+
else:
|
|
471
|
+
packages = [
|
|
472
|
+
(j, i.get("from", "")) for i in package_item if (j := i.get("include"))
|
|
473
|
+
]
|
|
474
|
+
# In case of managed by `poetry-plugin-version`
|
|
475
|
+
cwd = Path.cwd()
|
|
476
|
+
pattern = re.compile(r"__version__\s*=\s*['\"]")
|
|
477
|
+
ds: list[Path] = []
|
|
478
|
+
if package_name is not None:
|
|
479
|
+
packages.insert(0, (package_name, ""))
|
|
480
|
+
for package_name, source_dir in packages:
|
|
481
|
+
ds.append(cwd / package_name)
|
|
482
|
+
ds.append(cwd / "src" / package_name)
|
|
483
|
+
if source_dir and source_dir != "src":
|
|
484
|
+
ds.append(cwd / source_dir / package_name)
|
|
485
|
+
module_name = poetry_module_name(cwd.name)
|
|
486
|
+
ds.extend([cwd / module_name, cwd / "src" / module_name, cwd])
|
|
487
|
+
for d in ds:
|
|
488
|
+
init_file = d / "__init__.py"
|
|
489
|
+
if (init_file.exists() and pattern.search(init_file.read_text("utf8"))) or (
|
|
490
|
+
(init_file := init_file.with_name("__version__.py")).exists()
|
|
491
|
+
and pattern.search(init_file.read_text("utf8"))
|
|
492
|
+
):
|
|
493
|
+
break
|
|
494
|
+
else:
|
|
495
|
+
raise ParseError("Version file not found! Where are you now?")
|
|
496
|
+
return os.path.relpath(init_file, cwd)
|
|
485
497
|
|
|
486
498
|
def get_part(self, s: str) -> str:
|
|
487
499
|
choices: dict[str, str] = {}
|
|
@@ -517,7 +529,11 @@ class BumpUp(DryRun):
|
|
|
517
529
|
cmd += " && git push && git push --tags && git log -1"
|
|
518
530
|
else:
|
|
519
531
|
cmd += " --allow-dirty"
|
|
520
|
-
if
|
|
532
|
+
if (
|
|
533
|
+
should_sync
|
|
534
|
+
and not self._no_sync
|
|
535
|
+
and (sync := Project.get_sync_command(only_me=True))
|
|
536
|
+
):
|
|
521
537
|
cmd = f"{sync} && " + cmd
|
|
522
538
|
return cmd
|
|
523
539
|
|
|
@@ -561,7 +577,7 @@ def bump_version(
|
|
|
561
577
|
commit: bool = Option(
|
|
562
578
|
False, "--commit", "-c", help="Whether run `git commit` after version changed"
|
|
563
579
|
),
|
|
564
|
-
emoji:
|
|
580
|
+
emoji: bool | None = Option(
|
|
565
581
|
None, "--emoji", help="Whether add emoji prefix to commit message"
|
|
566
582
|
),
|
|
567
583
|
no_sync: bool = Option(
|
|
@@ -744,9 +760,12 @@ class Project:
|
|
|
744
760
|
return True
|
|
745
761
|
|
|
746
762
|
@classmethod
|
|
747
|
-
def get_sync_command(
|
|
763
|
+
def get_sync_command(
|
|
764
|
+
cls, prod: bool = True, doc: dict | None = None, only_me: bool = False
|
|
765
|
+
) -> str:
|
|
766
|
+
pdm_i = "pdm install --frozen" + " --prod" * prod
|
|
748
767
|
if cls.is_pdm_project():
|
|
749
|
-
return
|
|
768
|
+
return pdm_i
|
|
750
769
|
elif cls.manage_by_poetry(cache=True):
|
|
751
770
|
cmd = "poetry install"
|
|
752
771
|
if prod:
|
|
@@ -761,7 +780,17 @@ class Project:
|
|
|
761
780
|
cmd += " --only=main"
|
|
762
781
|
return cmd
|
|
763
782
|
elif cls.get_manage_tool(cache=True) == "uv":
|
|
764
|
-
|
|
783
|
+
install_me = "uv pip install -e ."
|
|
784
|
+
if doc is None:
|
|
785
|
+
doc = tomllib.loads(cls.load_toml_text())
|
|
786
|
+
is_distribution = (
|
|
787
|
+
doc.get("tool", {}).get("pdm", {}).get("distribution") is not False
|
|
788
|
+
)
|
|
789
|
+
if only_me:
|
|
790
|
+
return install_me if is_distribution else pdm_i
|
|
791
|
+
cmd = "uv sync --inexact" + " --no-dev" * prod
|
|
792
|
+
if is_distribution:
|
|
793
|
+
cmd += f" && {install_me}"
|
|
765
794
|
return ""
|
|
766
795
|
|
|
767
796
|
@classmethod
|
|
@@ -1251,7 +1280,7 @@ def check(
|
|
|
1251
1280
|
|
|
1252
1281
|
@cli.command(name="lint")
|
|
1253
1282
|
def make_style(
|
|
1254
|
-
files:
|
|
1283
|
+
files: list[str] | None = typer.Argument(default=None), # noqa:B008
|
|
1255
1284
|
check_only: bool = Option(False, "--check-only", "-c"),
|
|
1256
1285
|
bandit: bool = Option(False, "--bandit", help="Run `bandit -r <package_dir>`"),
|
|
1257
1286
|
prefix: bool = Option(
|
|
@@ -1434,9 +1463,9 @@ def dev(
|
|
|
1434
1463
|
|
|
1435
1464
|
@cli.command(name="dev")
|
|
1436
1465
|
def runserver(
|
|
1437
|
-
file_or_port:
|
|
1438
|
-
port:
|
|
1439
|
-
host:
|
|
1466
|
+
file_or_port: str | None = typer.Argument(default=None),
|
|
1467
|
+
port: int | None = Option(None, "-p", "--port"),
|
|
1468
|
+
host: str | None = Option(None, "-h", "--host"),
|
|
1440
1469
|
dry: bool = DryOption,
|
|
1441
1470
|
) -> None:
|
|
1442
1471
|
"""Start a fastapi server(only for fastapi>=0.111.0)"""
|
|
@@ -1624,7 +1653,7 @@ class UvPypi(DryRun):
|
|
|
1624
1653
|
|
|
1625
1654
|
@cli.command()
|
|
1626
1655
|
def pypi(
|
|
1627
|
-
file:
|
|
1656
|
+
file: str | None = typer.Argument(default=None),
|
|
1628
1657
|
dry: bool = DryOption,
|
|
1629
1658
|
verbose: bool = False,
|
|
1630
1659
|
quiet: bool = False,
|
|
@@ -41,7 +41,7 @@ dependencies = [
|
|
|
41
41
|
"bumpversion2 >=1.4.3",
|
|
42
42
|
"pytest >=8.2.0",
|
|
43
43
|
]
|
|
44
|
-
version = "0.19.
|
|
44
|
+
version = "0.19.3"
|
|
45
45
|
|
|
46
46
|
[project.urls]
|
|
47
47
|
Homepage = "https://github.com/waketzheng/fast-dev-cli"
|
|
@@ -57,7 +57,7 @@ fast = "fast_dev_cli:cli.main"
|
|
|
57
57
|
[dependency-groups]
|
|
58
58
|
dev = [
|
|
59
59
|
"twine>=6.1.0",
|
|
60
|
-
"asynctor>=0.
|
|
60
|
+
"asynctor>=0.10.0",
|
|
61
61
|
]
|
|
62
62
|
|
|
63
63
|
[tool.pdm]
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
1
3
|
import pytest
|
|
2
4
|
|
|
3
5
|
from fast_dev_cli.cli import Exit, capture_cmd_output, run_by_subprocess
|
|
@@ -15,6 +17,8 @@ def test_exec_dry():
|
|
|
15
17
|
)
|
|
16
18
|
assert "success" in out
|
|
17
19
|
assert "failed" not in out
|
|
20
|
+
out = capture_cmd_output('fast exec "python ~/0.py" --dry')
|
|
21
|
+
assert "--> python ~/0.py" in out
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
def test_exec():
|
|
@@ -29,6 +33,12 @@ def test_exec():
|
|
|
29
33
|
)
|
|
30
34
|
assert "failed" in out
|
|
31
35
|
assert "success" not in out
|
|
36
|
+
out = capture_cmd_output('fast exec "ls -a ~/"')
|
|
37
|
+
assert "--> ls -a ~" in out
|
|
38
|
+
assert ".bashrc" in out
|
|
39
|
+
home = os.path.expanduser("~")
|
|
40
|
+
expected = capture_cmd_output(f"ls {home}")
|
|
41
|
+
assert not (set(expected.splitlines()) - set(out.splitlines()))
|
|
32
42
|
|
|
33
43
|
|
|
34
44
|
def test_run_by_subprocess(capsys):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import functools
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
@@ -30,6 +31,16 @@ build-backend = "poetry.core.masonry.api"
|
|
|
30
31
|
"""
|
|
31
32
|
|
|
32
33
|
|
|
34
|
+
@functools.cache
|
|
35
|
+
def is_pip_installed() -> bool:
|
|
36
|
+
try:
|
|
37
|
+
import pip # NOQA
|
|
38
|
+
except ImportError:
|
|
39
|
+
return False
|
|
40
|
+
else:
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
|
|
33
44
|
def test_sync_not_in_venv(mocker, capsys):
|
|
34
45
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=False)
|
|
35
46
|
test_dir = Path(__file__).parent
|
|
@@ -44,10 +55,15 @@ def test_sync_not_in_venv(mocker, capsys):
|
|
|
44
55
|
mocker.patch(
|
|
45
56
|
"fast_dev_cli.cli.UpgradeDependencies.should_with_dev", return_value=True
|
|
46
57
|
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
pdm_export = "pdm export --without-hashes --with=dev -o req.txt"
|
|
59
|
+
installing = "pdm run python -m pip install -r req.txt"
|
|
60
|
+
if not is_pip_installed():
|
|
61
|
+
installing = (
|
|
62
|
+
"pdm run python -m ensurepip && pdm run python -m pip install -U pip && "
|
|
63
|
+
+ installing
|
|
64
|
+
)
|
|
65
|
+
expected = f"{pdm_export} && {installing}"
|
|
66
|
+
assert Sync("req.txt", "", True, dry=True).gen() == expected
|
|
51
67
|
|
|
52
68
|
|
|
53
69
|
def test_sync(mocker):
|
|
@@ -62,10 +78,12 @@ def test_sync(mocker):
|
|
|
62
78
|
mocker.patch(
|
|
63
79
|
"fast_dev_cli.cli.UpgradeDependencies.should_with_dev", return_value=True
|
|
64
80
|
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
81
|
+
pdm_export = "pdm export --without-hashes --with=dev -o req.txt"
|
|
82
|
+
if is_pip_installed():
|
|
83
|
+
expected = f"{pdm_export} && python -m pip install -r req.txt"
|
|
84
|
+
else:
|
|
85
|
+
expected = f"{pdm_export} && python -m ensurepip && python -m pip install -U pip && python -m pip install -r req.txt"
|
|
86
|
+
assert Sync("req.txt", "", True, dry=True).gen() == expected
|
|
69
87
|
|
|
70
88
|
|
|
71
89
|
UV_TOML_EXAMPLE = """
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.19.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|