GRM-44: fix: bridge test suite gaps — lint scripts, include integration tests
This commit is contained in:
@@ -67,10 +67,10 @@ remove:
|
||||
$(BIN)/grm remove $(NAME) $(if $(HOST),--host $(HOST),) $(if $(USER),--user $(USER),) $(if $(TOKEN),--token $(TOKEN),) $(if $(ASK_BECOME_PASS),--ask-become-pass,)
|
||||
|
||||
lint-ruff:
|
||||
$(BIN)/ruff check src/ tests/
|
||||
$(BIN)/ruff check src/ tests/ scripts/
|
||||
|
||||
lint-format:
|
||||
$(BIN)/ruff format --check src/ tests/
|
||||
$(BIN)/ruff format --check src/ tests/ scripts/
|
||||
|
||||
typecheck:
|
||||
$(BIN)/pyright
|
||||
@@ -78,7 +78,7 @@ typecheck:
|
||||
lint: lint-ruff lint-format typecheck lint-bandit
|
||||
|
||||
lint-bandit:
|
||||
$(BIN)/bandit -r src/ scripts/ scripts/ci/
|
||||
$(BIN)/bandit -r src/ scripts/
|
||||
|
||||
ansible-lint:
|
||||
$(BIN)/ansible-lint ansible/
|
||||
@@ -95,7 +95,7 @@ test-integration:
|
||||
$(BIN)/pytest tests/integration/ -v --no-cov
|
||||
|
||||
pytest-cov:
|
||||
$(BIN)/pytest tests/unit/ -v --cov=src/gitea_runner_manager --cov=scripts --cov=scripts/ci --cov-report=term-missing --cov-fail-under=100
|
||||
$(BIN)/pytest tests/ -v --cov=src/gitea_runner_manager --cov=scripts --cov-report=term-missing --cov-fail-under=100
|
||||
|
||||
MOLECULE := $(realpath $(BIN))/molecule
|
||||
MOLECULE_BASE := cd $(CURDIR)/ansible/roles/gitea-runner && ANSIBLE_ALLOW_BROKEN_CONDITIONALS=true ANSIBLE_INJECT_INVOCATION=1 $(MOLECULE)
|
||||
|
||||
@@ -66,14 +66,8 @@ def discover_scenarios(root: Path | None = None) -> list[str]:
|
||||
if root is None:
|
||||
root = MOLECULE_ROOT
|
||||
if not root.is_dir():
|
||||
raise click.ClickException(
|
||||
_("Molecule directory not found: {path}", path=str(root))
|
||||
)
|
||||
scenarios = [
|
||||
d.name
|
||||
for d in root.iterdir()
|
||||
if d.is_dir() and not d.name.startswith("_") and d.name != "common"
|
||||
]
|
||||
raise click.ClickException(_("Molecule directory not found: {path}", path=str(root)))
|
||||
scenarios = [d.name for d in root.iterdir() if d.is_dir() and not d.name.startswith("_") and d.name != "common"]
|
||||
return sorted(scenarios)
|
||||
|
||||
|
||||
@@ -92,9 +86,7 @@ def distribute(pairs: list[TestPair], max_runners: int) -> list[list[TestPair]]:
|
||||
return groups
|
||||
|
||||
|
||||
def pairs_for_runner(
|
||||
pairs: list[TestPair], runner_index: int, max_runners: int
|
||||
) -> list[TestPair]:
|
||||
def pairs_for_runner(pairs: list[TestPair], runner_index: int, max_runners: int) -> list[TestPair]:
|
||||
"""Return the subset of pairs assigned to *runner_index*."""
|
||||
groups = distribute(pairs, max_runners)
|
||||
if runner_index < 0 or runner_index >= len(groups):
|
||||
|
||||
@@ -53,7 +53,7 @@ def extract_cli_commands() -> list[str]:
|
||||
content = CLI_FILE.read_text()
|
||||
commands: list[str] = []
|
||||
# Find all @cli.command(...) occurrences, then the next def statement
|
||||
for match in re.finditer(r'@cli\.command\b', content):
|
||||
for match in re.finditer(r"@cli\.command\b", content):
|
||||
# Check for explicit name="..." in the decorator arguments
|
||||
decorator_end = content.find(")", match.start())
|
||||
decorator_text = content[match.start() : decorator_end + 1]
|
||||
@@ -63,7 +63,7 @@ def extract_cli_commands() -> list[str]:
|
||||
continue
|
||||
# Find the next def statement after this decorator
|
||||
after = content[decorator_end:]
|
||||
def_match = re.search(r'def\s+(\w+)\s*\(', after)
|
||||
def_match = re.search(r"def\s+(\w+)\s*\(", after)
|
||||
if def_match:
|
||||
commands.append(def_match.group(1))
|
||||
return commands
|
||||
|
||||
@@ -40,9 +40,7 @@ from gitea_runner_manager.i18n import _
|
||||
POLL_INTERVAL = 10
|
||||
|
||||
|
||||
def get_running_jobs(
|
||||
gitea_url: str, owner: str, repo: str, token: str, run_id: int
|
||||
) -> list[dict]:
|
||||
def get_running_jobs(gitea_url: str, owner: str, repo: str, token: str, run_id: int) -> list[dict]:
|
||||
"""Return jobs for the given workflow run."""
|
||||
url = f"{gitea_url}/api/v1/repos/{owner}/{repo}/actions/runs/{run_id}/jobs"
|
||||
headers = {"Authorization": f"token {token}"}
|
||||
@@ -52,9 +50,7 @@ def get_running_jobs(
|
||||
return data.get("jobs", [])
|
||||
|
||||
|
||||
def any_other_runner_failed(
|
||||
jobs: list[dict], current_job_name: str, current_index: int
|
||||
) -> bool:
|
||||
def any_other_runner_failed(jobs: list[dict], current_job_name: str, current_index: int) -> bool:
|
||||
"""Return True if any other molecule matrix job has failed."""
|
||||
for job in jobs:
|
||||
name = job.get("name", "")
|
||||
@@ -83,11 +79,7 @@ def poll_for_other_failures(
|
||||
try:
|
||||
jobs = get_running_jobs(gitea_url, owner, repo, token, run_id)
|
||||
if any_other_runner_failed(jobs, job_name, current_index):
|
||||
click.echo(
|
||||
_(
|
||||
"Another molecule runner failed. Stopping this runner early."
|
||||
)
|
||||
)
|
||||
click.echo(_("Another molecule runner failed. Stopping this runner early."))
|
||||
failed_event.set()
|
||||
return
|
||||
except requests.RequestException as exc:
|
||||
@@ -132,11 +124,7 @@ def cli(pairs: tuple[str, ...]) -> None:
|
||||
owner, repo = "oblachno-oss", "grm"
|
||||
|
||||
if not all([gitea_url, token, run_id]):
|
||||
click.echo(
|
||||
_(
|
||||
"GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation."
|
||||
)
|
||||
)
|
||||
click.echo(_("GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation."))
|
||||
|
||||
repo_root = Path(__file__).resolve().parent.parent
|
||||
role_dir = repo_root / "ansible" / "roles" / "gitea-runner"
|
||||
@@ -173,9 +161,7 @@ def cli(pairs: tuple[str, ...]) -> None:
|
||||
|
||||
scenario = pair.split("|")[0]
|
||||
platform_name = pair.split("|")[1]
|
||||
click.echo(
|
||||
_("Running: {scenario} on {platform}", scenario=scenario, platform=platform_name)
|
||||
)
|
||||
click.echo(_("Running: {scenario} on {platform}", scenario=scenario, platform=platform_name))
|
||||
|
||||
cmd = build_molecule_cmd(scenario)
|
||||
env = build_env_for_pair(pair, base_env)
|
||||
@@ -208,9 +194,7 @@ def cli(pairs: tuple[str, ...]) -> None:
|
||||
|
||||
rc = process.returncode
|
||||
if rc != 0:
|
||||
click.echo(
|
||||
_("FAILED: {pair} exited with code {code}", pair=pair, code=rc)
|
||||
)
|
||||
click.echo(_("FAILED: {pair} exited with code {code}", pair=pair, code=rc))
|
||||
sys.exit(rc)
|
||||
|
||||
click.echo(_("PASSED: {pair}", pair=pair))
|
||||
|
||||
@@ -38,9 +38,7 @@ def _handle_http_error(e: APIError) -> None:
|
||||
status=e.status,
|
||||
)
|
||||
)
|
||||
raise click.ClickException(
|
||||
_("HTTP error: {status} — {message}", status=e.status, message=e.message)
|
||||
)
|
||||
raise click.ClickException(_("HTTP error: {status} — {message}", status=e.status, message=e.message))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
@@ -30,9 +30,7 @@ def _arch() -> str:
|
||||
return "amd64"
|
||||
if machine in {"aarch64", "arm64"}:
|
||||
return "arm64"
|
||||
raise click.ClickException(
|
||||
f"Unsupported architecture: {machine}"
|
||||
)
|
||||
raise click.ClickException(f"Unsupported architecture: {machine}")
|
||||
|
||||
|
||||
def _install_with_go() -> bool:
|
||||
|
||||
Reference in New Issue
Block a user