Files
gitea-mirror/docs/superpowers/plans/2026-06-28-hardening-cli-and-sync.md
T
alex 998665a4bf
Test and coverage / build (push) Failing after 56s
feat: small refactorings
2026-06-28 01:18:08 +03:00

4.3 KiB

Hardening CLI And Sync Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Make Gitea mirror fail reliably on API/git/config/path errors and expose a proper package CLI entrypoint.

Architecture: Keep the existing module layout, but move executable orchestration into an importable src.cli module. Preserve gitea-mirror.py as a compatibility wrapper while making process_repo return success/failure for aggregate exit handling.

Tech Stack: Python 3.12, requests, pydantic v2, pytest, ruff, uv.


Task 1: API Errors Are Fatal

Files:

  • Modify: src/gitea_api.py

  • Test: tests/test_gitea_api.py

  • Step 1: Write failing tests

def test_get_repositories_raises_on_http_error(monkeypatch):
    api = GiteaApi(endpoint="https://example.com", api_token="token")
    session = FakeSession([FakeResponse(status_code=500, data=[])])
    monkeypatch.setattr(requests, "session", lambda: session)

    with pytest.raises(GiteaApiError):
        api.get_repositories()
  • Step 2: Run test to verify it fails

Run: uv run pytest tests/test_gitea_api.py -v Expected: FAIL because GiteaApiError does not exist or is not raised.

  • Step 3: Implement minimal code

Add GiteaApiError, request timeout, network exception handling, JSON/schema exception handling, and raise instead of returning [].

  • Step 4: Run test to verify it passes

Run: uv run pytest tests/test_gitea_api.py -v Expected: PASS.

Task 2: Git Failures Affect Exit Code

Files:

  • Create: src/cli.py

  • Modify: gitea-mirror.py

  • Test: tests/test_mirror_process.py, tests/test_cli.py

  • Step 1: Write failing tests

def test_process_repo_returns_false_when_clone_fails(monkeypatch, tmp_path):
    monkeypatch.setattr(cli, "git_clone", lambda **kwargs: False)
    assert not cli.process_repo(config=make_config(tmp_path, FetchConfig(ssh_key="/tmp/key")), repo=make_repo())
  • Step 2: Run test to verify it fails

Run: uv run pytest tests/test_mirror_process.py tests/test_cli.py -v Expected: FAIL because process_repo currently returns None and no importable CLI module exists.

  • Step 3: Implement minimal code

Move orchestration to src.cli, return booleans from process_repo, aggregate failures in run, and make main return non-zero for failures.

  • Step 4: Run test to verify it passes

Run: uv run pytest tests/test_mirror_process.py tests/test_cli.py -v Expected: PASS.

Task 3: Validate Repository Paths And Config

Files:

  • Modify: src/models.py

  • Modify: src/repository_name.py

  • Test: tests/test_config.py, tests/test_repository_name.py

  • Step 1: Write failing tests

def test_output_path_rejects_parent_traversal(tmp_path):
    repo = make_repo()
    with pytest.raises(ValueError):
        get_repository_path(str(tmp_path), "../{name}", repo)
  • Step 2: Run test to verify it fails

Run: uv run pytest tests/test_config.py tests/test_repository_name.py -v Expected: FAIL because validation is missing.

  • Step 3: Implement minimal code

Add repository format validation in Config, log level validation in LoggingConfig, and a safe output path helper that rejects absolute paths and paths escaping out_dir.

  • Step 4: Run test to verify it passes

Run: uv run pytest tests/test_config.py tests/test_repository_name.py -v Expected: PASS.

Task 4: Package CLI Entrypoint

Files:

  • Modify: pyproject.toml

  • Modify: gitea-mirror.py

  • Test: tests/test_cli.py

  • Step 1: Write failing test

def test_project_defines_console_script():
    data = tomllib.loads(Path("pyproject.toml").read_text())
    assert data["project"]["scripts"]["gitea-mirror"] == "src.cli:main"
  • Step 2: Run test to verify it fails

Run: uv run pytest tests/test_cli.py -v Expected: FAIL because [project.scripts] is absent.

  • Step 3: Implement minimal code

Add [project.scripts] gitea-mirror = "src.cli:main" and make gitea-mirror.py delegate to src.cli.main.

  • Step 4: Run full verification

Run: make check Expected: PASS.