Phase A1: bootstrap Python ci_orchestrator package

- pyproject.toml (hatchling) with ruff/mypy/pytest dev deps
- src/ci_orchestrator/: config, credentials, backends (Protocol + WorkstationVmrunBackend), transport (WinRM via pypsrp, SSH via paramiko)
- CLI entry point with PoC 'wait-ready' subcommand (Windows + Linux guests)
- tests/python/: 35 unit tests, 83% coverage, all backends/transport mocked
- gitea/workflows/lint.yml: new 'python' job (ruff + mypy --strict + pytest --cov-fail-under=70)
- config.example.toml + README setup section + .gitignore Python entries

Neutral VmBackend Protocol prepared for Phase C ESXi extension.
See plans/implementation-plan-A-B.md step A1.
This commit is contained in:
2026-05-13 11:25:07 +02:00
parent 4d957d34b2
commit bd31a3f2f3
26 changed files with 1920 additions and 2 deletions
+58
View File
@@ -0,0 +1,58 @@
"""Tests for KeyringCredentialStore (mocked keyring backend)."""
from __future__ import annotations
import sys
import types
from typing import Any
import pytest
from ci_orchestrator.credentials import KeyringCredentialStore
def _install_fake_keyring(monkeypatch: pytest.MonkeyPatch, **behaviour: Any) -> None:
fake = types.ModuleType("keyring")
def get_credential(target: str, _username: str | None) -> Any:
return behaviour.get("credential_map", {}).get(target)
def get_password(service: str, target: str) -> str | None:
return behaviour.get("password_map", {}).get((service, target))
fake.get_credential = get_credential # type: ignore[attr-defined]
fake.get_password = get_password # type: ignore[attr-defined]
monkeypatch.setitem(sys.modules, "keyring", fake)
class _FakeCred:
def __init__(self, username: str, password: str) -> None:
self.username = username
self.password = password
def test_get_returns_credential(monkeypatch: pytest.MonkeyPatch) -> None:
_install_fake_keyring(
monkeypatch,
credential_map={"BuildVMGuest": _FakeCred("Administrator", "s3cret")},
)
cred = KeyringCredentialStore().get("BuildVMGuest")
assert cred.username == "Administrator"
assert cred.password == "s3cret"
def test_get_falls_back_to_password(monkeypatch: pytest.MonkeyPatch) -> None:
_install_fake_keyring(
monkeypatch,
credential_map={},
password_map={("ci", "GiteaPAT"): "tokentoken"},
)
cred = KeyringCredentialStore().get("GiteaPAT")
assert cred.username == "GiteaPAT"
assert cred.password == "tokentoken"
def test_get_raises_when_missing(monkeypatch: pytest.MonkeyPatch) -> None:
_install_fake_keyring(monkeypatch, credential_map={}, password_map={})
with pytest.raises(KeyError):
KeyringCredentialStore().get("Unknown")