gardusig-cli 0.1.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.
- cli/__init__.py +3 -0
- cli/__main__.py +4 -0
- cli/cli.py +71 -0
- cli/commands/__init__.py +1 -0
- cli/commands/backup.py +49 -0
- cli/commands/bookmarks.py +21 -0
- cli/commands/chrome.py +77 -0
- cli/commands/contest.py +120 -0
- cli/commands/docker.py +386 -0
- cli/commands/drive.py +223 -0
- cli/commands/gh.py +419 -0
- cli/commands/git.py +790 -0
- cli/commands/links.py +81 -0
- cli/commands/notion.py +215 -0
- cli/commands/publish.py +63 -0
- cli/commands/restore.py +8 -0
- cli/integration/__init__.py +5 -0
- cli/integration/cli_api_checks.py +531 -0
- cli/integration/contest_integration.py +156 -0
- cli/integration/docker_integration.py +346 -0
- cli/integration/docker_mocks.py +125 -0
- cli/integration/git_mocks.py +59 -0
- cli/integration/integration_coverage.py +329 -0
- cli/integration/public_commands.py +82 -0
- cli/integration/public_endpoints.py +1151 -0
- cli/integration/tag_zip_integration.py +449 -0
- cli/integration/workflow_integration.py +804 -0
- cli/integration/workspaces.py +83 -0
- cli/internal/__init__.py +1 -0
- cli/internal/read/__init__.py +9 -0
- cli/internal/read/git.py +47 -0
- cli/internal/read/safety.py +99 -0
- cli/internal/write/__init__.py +13 -0
- cli/internal/write/gate.py +66 -0
- cli/internal/write/git.py +38 -0
- cli/models/__init__.py +1 -0
- cli/models/backup.py +10 -0
- cli/models/bookmark.py +14 -0
- cli/models/repository.py +9 -0
- cli/models/task.py +68 -0
- cli/providers/__init__.py +1 -0
- cli/providers/base.py +23 -0
- cli/providers/chrome.py +9 -0
- cli/providers/drive_client.py +56 -0
- cli/providers/gh.py +47 -0
- cli/providers/github.py +13 -0
- cli/providers/google_drive.py +31 -0
- cli/providers/icloud_drive.py +17 -0
- cli/providers/notion.py +244 -0
- cli/providers/onedrive.py +31 -0
- cli/providers/proton_drive.py +31 -0
- cli/services/__init__.py +3 -0
- cli/services/backup_repository.py +181 -0
- cli/services/backup_zip.py +65 -0
- cli/services/bookmark_sync.py +9 -0
- cli/services/contest_docker.py +193 -0
- cli/services/contest_runner.py +274 -0
- cli/services/contest_serde.py +30 -0
- cli/services/docker_runtime.py +332 -0
- cli/services/drive_sync.py +108 -0
- cli/services/gh_sequence.py +66 -0
- cli/services/gh_service.py +344 -0
- cli/services/git_archive.py +5 -0
- cli/services/git_review.py +34 -0
- cli/services/git_shortcuts.py +610 -0
- cli/services/notion_markdown.py +173 -0
- cli/services/notion_pairs.py +232 -0
- cli/services/notion_sync.py +220 -0
- cli/services/pypi_publish.py +80 -0
- cli/services/replica_deploy.py +128 -0
- cli/utils/__init__.py +12 -0
- cli/utils/catalog.py +179 -0
- cli/utils/config.py +316 -0
- cli/utils/confirm.py +18 -0
- cli/utils/external_client.py +143 -0
- cli/utils/fs.py +13 -0
- cli/utils/hashing.py +12 -0
- cli/utils/http.py +10 -0
- cli/utils/logger.py +15 -0
- cli/utils/process.py +62 -0
- cli/utils/quick_defaults.py +40 -0
- cli/utils/retry.py +30 -0
- cli/utils/yaml.py +22 -0
- cli/utils/zip.py +18 -0
- cli/workflows/__init__.py +1 -0
- cli/workflows/backup.py +1 -0
- cli/workflows/restore.py +1 -0
- gardusig_cli-0.1.0.dist-info/METADATA +274 -0
- gardusig_cli-0.1.0.dist-info/RECORD +93 -0
- gardusig_cli-0.1.0.dist-info/WHEEL +5 -0
- gardusig_cli-0.1.0.dist-info/entry_points.txt +2 -0
- gardusig_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- gardusig_cli-0.1.0.dist-info/top_level.txt +1 -0
cli/__init__.py
ADDED
cli/__main__.py
ADDED
cli/cli.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from cli import __version__
|
|
6
|
+
from cli.commands.backup import backup_app
|
|
7
|
+
from cli.commands.bookmarks import bookmarks_app
|
|
8
|
+
from cli.commands.chrome import chrome_app
|
|
9
|
+
from cli.commands.contest import contest_app
|
|
10
|
+
from cli.commands.docker import docker_app
|
|
11
|
+
from cli.commands.drive import drive_app
|
|
12
|
+
from cli.commands.gh import gh_app
|
|
13
|
+
from cli.commands.git import git_app
|
|
14
|
+
from cli.commands.links import links_app
|
|
15
|
+
from cli.commands.notion import notion_app
|
|
16
|
+
from cli.commands.publish import publish_app
|
|
17
|
+
from cli.commands.restore import restore_app
|
|
18
|
+
from cli.utils.logger import setup_logging
|
|
19
|
+
|
|
20
|
+
app = typer.Typer(
|
|
21
|
+
name="cli",
|
|
22
|
+
help="Git shortcuts and drive (tag zips) for macOS. Run `cli links` for docs and scripts.",
|
|
23
|
+
no_args_is_help=True,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
app.add_typer(links_app, name="links")
|
|
27
|
+
app.add_typer(git_app, name="git")
|
|
28
|
+
app.add_typer(gh_app, name="gh")
|
|
29
|
+
app.add_typer(backup_app, name="backup", hidden=True)
|
|
30
|
+
app.add_typer(restore_app, name="restore")
|
|
31
|
+
app.add_typer(drive_app, name="drive")
|
|
32
|
+
app.add_typer(notion_app, name="notion")
|
|
33
|
+
app.add_typer(chrome_app, name="chrome")
|
|
34
|
+
app.add_typer(bookmarks_app, name="bookmarks", hidden=True)
|
|
35
|
+
app.add_typer(docker_app, name="docker")
|
|
36
|
+
app.add_typer(contest_app, name="contest")
|
|
37
|
+
app.add_typer(publish_app, name="publish")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@app.callback(invoke_without_command=True)
|
|
41
|
+
def main(
|
|
42
|
+
ctx: typer.Context,
|
|
43
|
+
version: bool = typer.Option(False, "--version", "-V", help="Show version and exit."),
|
|
44
|
+
verbose: bool = typer.Option(False, "--verbose", "-v"),
|
|
45
|
+
) -> None:
|
|
46
|
+
setup_logging(verbose=verbose)
|
|
47
|
+
if version:
|
|
48
|
+
typer.echo(__version__)
|
|
49
|
+
raise typer.Exit()
|
|
50
|
+
if ctx.invoked_subcommand is None and not version:
|
|
51
|
+
typer.echo(ctx.get_help())
|
|
52
|
+
typer.echo("\nFull index: cli links | docs/README.md")
|
|
53
|
+
raise typer.Exit()
|
|
54
|
+
ctx.obj = {"verbose": verbose}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def run() -> None:
|
|
58
|
+
"""CLI entrypoint — surfaces ExternalCallError as a clean user message."""
|
|
59
|
+
from cli.utils.config import load_local_env
|
|
60
|
+
from cli.utils.external_client import ExternalCallError
|
|
61
|
+
|
|
62
|
+
load_local_env()
|
|
63
|
+
try:
|
|
64
|
+
app()
|
|
65
|
+
except ExternalCallError as exc:
|
|
66
|
+
typer.echo(exc.user_message, err=True)
|
|
67
|
+
raise typer.Exit(1) from exc
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# Short alias: cli g <cmd> == cli git <cmd>
|
|
71
|
+
app.add_typer(git_app, name="g", hidden=True)
|
cli/commands/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI command modules."""
|
cli/commands/backup.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Hidden aliases for legacy `cli backup` → use `cli drive` instead."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from cli.commands.drive import delete_cmd, ingest_cmd, list_cmd, status_cmd
|
|
8
|
+
|
|
9
|
+
backup_app = typer.Typer(
|
|
10
|
+
help="(deprecated) use cli drive",
|
|
11
|
+
no_args_is_help=True,
|
|
12
|
+
hidden=True,
|
|
13
|
+
)
|
|
14
|
+
repository_app = typer.Typer(help="(deprecated)", hidden=True)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@backup_app.command("status")
|
|
18
|
+
def backup_status_alias() -> None:
|
|
19
|
+
"""Deprecated: use `cli drive status`."""
|
|
20
|
+
status_cmd()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@repository_app.command("sync")
|
|
24
|
+
def repository_sync_alias(
|
|
25
|
+
path: str | None = typer.Argument(None),
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Deprecated: use `cli drive ingest`."""
|
|
28
|
+
ingest_cmd(path)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@repository_app.command("list")
|
|
32
|
+
def repository_list_alias(
|
|
33
|
+
path: str | None = typer.Argument(None),
|
|
34
|
+
) -> None:
|
|
35
|
+
"""Deprecated: use `cli drive list`."""
|
|
36
|
+
list_cmd(path)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@repository_app.command("delete")
|
|
40
|
+
def repository_delete_alias(
|
|
41
|
+
path: str = typer.Argument(...),
|
|
42
|
+
tag: str = typer.Argument(...),
|
|
43
|
+
yes: bool = typer.Option(False, "--yes", "-y"),
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Deprecated: use `cli drive delete`."""
|
|
46
|
+
delete_cmd(path, tag, yes=yes)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
backup_app.add_typer(repository_app, name="repository")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Hidden legacy alias: `cli bookmarks` → `cli chrome bookmarks`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from cli.commands.chrome import bookmarks_deploy_to_chrome, bookmarks_ingest_from_chrome
|
|
8
|
+
|
|
9
|
+
bookmarks_app = typer.Typer(help="(deprecated) use cli chrome bookmarks", hidden=True)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@bookmarks_app.command("export")
|
|
13
|
+
def legacy_export() -> None:
|
|
14
|
+
"""Deprecated: remote-centric Chrome→local; use `cli chrome bookmarks ingest`."""
|
|
15
|
+
bookmarks_ingest_from_chrome()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@bookmarks_app.command("import")
|
|
19
|
+
def legacy_import() -> None:
|
|
20
|
+
"""Deprecated: remote-centric local→Chrome; use `cli chrome bookmarks deploy`."""
|
|
21
|
+
bookmarks_deploy_to_chrome()
|
cli/commands/chrome.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Chrome browser integrations (bookmarks today; more later)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from rich import print as rprint
|
|
10
|
+
|
|
11
|
+
from cli.utils.config import bookmarks_file_path, chrome_downloads_dir, project_root
|
|
12
|
+
|
|
13
|
+
chrome_app = typer.Typer(help="Chrome browser — bookmarks and future integrations.", no_args_is_help=True)
|
|
14
|
+
bookmarks_app = typer.Typer(help="Bookmark ingest / deploy (local-centric).", no_args_is_help=True)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _bookmarks_env() -> dict[str, str]:
|
|
18
|
+
env = os.environ.copy()
|
|
19
|
+
env["CLI_ROOT"] = str(project_root())
|
|
20
|
+
env["CLI_BOOKMARKS_FILE"] = str(bookmarks_file_path())
|
|
21
|
+
env["CLI_DOWNLOADS_DIR"] = str(chrome_downloads_dir())
|
|
22
|
+
return env
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _run_bookmarks_script(name: str) -> None:
|
|
26
|
+
script = project_root() / "scripts" / "chrome" / name
|
|
27
|
+
if not script.is_file():
|
|
28
|
+
raise typer.Exit(f"Script not found: {script}")
|
|
29
|
+
try:
|
|
30
|
+
subprocess.run([str(script)], env=_bookmarks_env(), check=True)
|
|
31
|
+
except subprocess.CalledProcessError as exc:
|
|
32
|
+
raise typer.Exit(exc.returncode) from exc
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def bookmarks_ingest_from_chrome() -> None:
|
|
36
|
+
"""Chrome → local: ingest bookmarks HTML into configured path."""
|
|
37
|
+
dest = bookmarks_file_path()
|
|
38
|
+
_run_bookmarks_script("export-bookmarks.sh")
|
|
39
|
+
rprint(f"[green]ingested[/green] → {dest}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def bookmarks_deploy_to_chrome() -> None:
|
|
43
|
+
"""Local → Chrome: deploy backup file into Chrome."""
|
|
44
|
+
src = bookmarks_file_path()
|
|
45
|
+
if not src.is_file():
|
|
46
|
+
raise typer.Exit(
|
|
47
|
+
f"Backup not found: {src}\nRun `cli chrome bookmarks ingest` first."
|
|
48
|
+
)
|
|
49
|
+
_run_bookmarks_script("import-bookmarks.sh")
|
|
50
|
+
rprint(f"[green]deployed[/green] to Chrome from {src}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@bookmarks_app.command("ingest")
|
|
54
|
+
def bookmarks_ingest_cmd() -> None:
|
|
55
|
+
"""Chrome → local: ingest bookmarks HTML to configured path."""
|
|
56
|
+
bookmarks_ingest_from_chrome()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@bookmarks_app.command("deploy")
|
|
60
|
+
def bookmarks_deploy_cmd() -> None:
|
|
61
|
+
"""Local → Chrome: deploy backup into Chrome."""
|
|
62
|
+
bookmarks_deploy_to_chrome()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@bookmarks_app.command("import", hidden=True)
|
|
66
|
+
def legacy_import_cmd() -> None:
|
|
67
|
+
"""Deprecated: remote-centric local→Chrome; use `cli chrome bookmarks deploy`."""
|
|
68
|
+
bookmarks_deploy_to_chrome()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@bookmarks_app.command("export", hidden=True)
|
|
72
|
+
def legacy_export_cmd() -> None:
|
|
73
|
+
"""Deprecated: remote-centric Chrome→local; use `cli chrome bookmarks ingest`."""
|
|
74
|
+
bookmarks_ingest_from_chrome()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
chrome_app.add_typer(bookmarks_app, name="bookmarks")
|
cli/commands/contest.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Competitive programming validation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich import print as rprint
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from cli.services.contest_docker import RunStatus
|
|
12
|
+
from cli.services.contest_runner import (
|
|
13
|
+
ContestValidateResult,
|
|
14
|
+
resolve_options,
|
|
15
|
+
validate_contest,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
contest_app = typer.Typer(
|
|
19
|
+
help="Competitive programming validation (generator, brute, fast).",
|
|
20
|
+
no_args_is_help=True,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _status_style(status: RunStatus) -> str:
|
|
25
|
+
if status == RunStatus.OK:
|
|
26
|
+
return "green"
|
|
27
|
+
if status == RunStatus.TLE:
|
|
28
|
+
return "yellow"
|
|
29
|
+
return "red"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _print_outcome_row(
|
|
33
|
+
table: Table,
|
|
34
|
+
tier: str,
|
|
35
|
+
role: str,
|
|
36
|
+
outcome,
|
|
37
|
+
) -> None:
|
|
38
|
+
table.add_row(
|
|
39
|
+
tier,
|
|
40
|
+
role,
|
|
41
|
+
f"[{_status_style(outcome.status)}]{outcome.status.value}[/]",
|
|
42
|
+
f"{outcome.seconds:.2f}s",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _print_result(result: ContestValidateResult) -> None:
|
|
47
|
+
if result.error and result.small is None and result.large is None:
|
|
48
|
+
rprint(f"[red]error:[/red] {result.error}")
|
|
49
|
+
rprint("[red]FAIL[/red]")
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
table = Table(title="Contest validate", show_header=True, header_style="bold")
|
|
53
|
+
table.add_column("Tier")
|
|
54
|
+
table.add_column("Role")
|
|
55
|
+
table.add_column("Status")
|
|
56
|
+
table.add_column("Time", justify="right")
|
|
57
|
+
|
|
58
|
+
if result.small:
|
|
59
|
+
_print_outcome_row(table, "small", "brute", result.small.brute)
|
|
60
|
+
_print_outcome_row(table, "small", "fast", result.small.fast)
|
|
61
|
+
match = result.small.outputs_match
|
|
62
|
+
if match is not None:
|
|
63
|
+
style = "green" if match else "red"
|
|
64
|
+
table.add_row(
|
|
65
|
+
"small",
|
|
66
|
+
"compare",
|
|
67
|
+
f"[{style}]{'match' if match else 'mismatch'}[/]",
|
|
68
|
+
"",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if result.large:
|
|
72
|
+
_print_outcome_row(table, "large", "brute", result.large.brute)
|
|
73
|
+
_print_outcome_row(table, "large", "fast", result.large.fast)
|
|
74
|
+
|
|
75
|
+
rprint(table)
|
|
76
|
+
|
|
77
|
+
if result.small_diff:
|
|
78
|
+
rprint("\n[red]small tier diff:[/red]")
|
|
79
|
+
rprint(result.small_diff)
|
|
80
|
+
|
|
81
|
+
for warning in result.warnings:
|
|
82
|
+
rprint(f"[yellow]warning:[/yellow] {warning}")
|
|
83
|
+
|
|
84
|
+
if result.error:
|
|
85
|
+
rprint(f"[red]error:[/red] {result.error}")
|
|
86
|
+
|
|
87
|
+
if result.passed:
|
|
88
|
+
rprint("[green]PASS[/green]")
|
|
89
|
+
else:
|
|
90
|
+
rprint("[red]FAIL[/red]")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@contest_app.command("validate")
|
|
94
|
+
def validate_cmd(
|
|
95
|
+
fast: Path | None = typer.Option(None, "--fast", help="Path to fast C++ solution."),
|
|
96
|
+
brute: Path | None = typer.Option(None, "--brute", help="Path to Python brute solution."),
|
|
97
|
+
generator: Path | None = typer.Option(None, "--generator", help="Path to Python input generator."),
|
|
98
|
+
config: Path | None = typer.Option(None, "--config", help="Optional YAML config with paths and limits."),
|
|
99
|
+
timeout: float | None = typer.Option(None, "--timeout", help="Per-container time limit in seconds."),
|
|
100
|
+
memory_mb: int | None = typer.Option(None, "--memory-mb", help="Per-container memory limit in MB."),
|
|
101
|
+
image: str | None = typer.Option(None, "--image", help="Docker image for solution runs."),
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Validate fast C++ vs brute Python on small (compare) and large (stress) tiers."""
|
|
104
|
+
try:
|
|
105
|
+
options = resolve_options(
|
|
106
|
+
fast=fast,
|
|
107
|
+
brute=brute,
|
|
108
|
+
generator=generator,
|
|
109
|
+
config=config,
|
|
110
|
+
timeout=timeout,
|
|
111
|
+
memory_mb=memory_mb,
|
|
112
|
+
image=image,
|
|
113
|
+
)
|
|
114
|
+
except (ValueError, FileNotFoundError) as exc:
|
|
115
|
+
rprint(f"[red]error:[/red] {exc}")
|
|
116
|
+
raise typer.Exit(1) from exc
|
|
117
|
+
|
|
118
|
+
result = validate_contest(options)
|
|
119
|
+
_print_result(result)
|
|
120
|
+
raise typer.Exit(0 if result.passed else 1)
|