GRM-32: fix: security, dead code, idempotence, and documentation cleanup
This commit is contained in:
@@ -1,18 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run all molecule scenarios on all supported OS platforms.
|
||||
# Used by `make molecule-all`. Sequential — CI uses parallel matrix instead.
|
||||
# Platform list is sourced from scripts/distribute_molecule.py to avoid duplication.
|
||||
set -euo pipefail
|
||||
|
||||
MOLECULE_BIN="$(realpath "${BIN:-.venv/bin}/molecule")"
|
||||
ROLE_DIR="$(cd "$(dirname "$0")/.." && pwd)/ansible/roles/gitea-runner"
|
||||
SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
for p in \
|
||||
ubuntu-2204:geerlingguy/docker-ubuntu2204-ansible:latest:/lib/systemd/systemd \
|
||||
ubuntu-2404:geerlingguy/docker-ubuntu2404-ansible:latest:/lib/systemd/systemd \
|
||||
debian-12:geerlingguy/docker-debian12-ansible:latest:/lib/systemd/systemd \
|
||||
archlinux:marcstraube/archlinux-ansible:latest:/usr/lib/systemd/systemd
|
||||
do
|
||||
IFS=":" read -r name image command <<< "$p"
|
||||
# Read platforms from distribute_molecule.py (single source of truth)
|
||||
PLATFORMS_OUTPUT="$("$MOLECULE_BIN" python "${SCRIPTS_DIR}/distribute_molecule.py" --list-platforms 2>/dev/null || \
|
||||
python3 "${SCRIPTS_DIR}/distribute_molecule.py" --list-platforms)"
|
||||
|
||||
for p in $PLATFORMS_OUTPUT; do
|
||||
IFS="|" read -r name image command <<< "$p"
|
||||
export MOLECULE_PLATFORM_NAME="$name" MOLECULE_PLATFORM_IMAGE="$image"
|
||||
if [ -n "$command" ]; then
|
||||
export MOLECULE_PLATFORM_COMMAND="$command"
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run molecule (scenario, platform) pairs in parallel and kill on first failure.
|
||||
|
||||
Each pair is encoded as ``scenario|platform_name|platform_image|platform_command``.
|
||||
Subprocesses are started concurrently. If any subprocess exits with a non-zero
|
||||
status, the remaining subprocesses are terminated and this script exits with 1.
|
||||
|
||||
Usage:
|
||||
python3 scripts/run_molecule_parallel.py \
|
||||
default|ubuntu-2204|ubuntu:22.04| \
|
||||
lifecycle|debian-12|debian:12| \
|
||||
...
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import signal
|
||||
import subprocess # nosec B404
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
|
||||
from gitea_runner_manager.i18n import _
|
||||
|
||||
|
||||
def run_pair(pair: str, role_dir: Path) -> subprocess.Popen[bytes]:
|
||||
"""Start a subprocess for a single molecule (scenario, platform) pair."""
|
||||
scenario, platform_name, platform_image, platform_command = pair.split("|")
|
||||
env = os.environ.copy()
|
||||
env["MOLECULE_PLATFORM_NAME"] = platform_name
|
||||
env["MOLECULE_PLATFORM_IMAGE"] = platform_image
|
||||
if platform_command:
|
||||
env["MOLECULE_PLATFORM_COMMAND"] = platform_command
|
||||
elif "MOLECULE_PLATFORM_COMMAND" in env:
|
||||
del env["MOLECULE_PLATFORM_COMMAND"]
|
||||
|
||||
env["ANSIBLE_ALLOW_BROKEN_CONDITIONALS"] = "true"
|
||||
env["ANSIBLE_INJECT_INVOCATION"] = "1"
|
||||
|
||||
cmd = ["molecule", "test"]
|
||||
if scenario != "default":
|
||||
cmd.extend(["-s", scenario])
|
||||
|
||||
click.echo(_("Starting: {scenario} on {platform}", scenario=scenario, platform=platform_name))
|
||||
return subprocess.Popen( # nosec B603
|
||||
cmd,
|
||||
cwd=str(role_dir),
|
||||
env=env,
|
||||
preexec_fn=os.setsid,
|
||||
)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("pairs", nargs=-1, required=True)
|
||||
def cli(pairs: tuple[str, ...]) -> None:
|
||||
"""Run molecule pairs in parallel, stop on first failure."""
|
||||
repo_root = Path(__file__).resolve().parent.parent
|
||||
role_dir = repo_root / "ansible" / "roles" / "gitea-runner"
|
||||
|
||||
processes: list[subprocess.Popen[bytes]] = []
|
||||
pair_names: list[str] = []
|
||||
for pair in pairs:
|
||||
proc = run_pair(pair, role_dir)
|
||||
processes.append(proc)
|
||||
pair_names.append(pair)
|
||||
|
||||
first_failure: str | None = None
|
||||
returncode = 0
|
||||
while processes:
|
||||
finished_indices: list[int] = []
|
||||
for i, proc in enumerate(processes):
|
||||
ret = proc.poll()
|
||||
if ret is not None:
|
||||
finished_indices.append(i)
|
||||
if ret != 0:
|
||||
first_failure = pair_names[i]
|
||||
returncode = ret
|
||||
|
||||
if first_failure is not None:
|
||||
click.echo(
|
||||
_(
|
||||
"FAILURE: {pair} exited with code {code}. Stopping remaining tests.",
|
||||
pair=first_failure,
|
||||
code=returncode,
|
||||
)
|
||||
)
|
||||
for proc in processes:
|
||||
if proc.poll() is None:
|
||||
with contextlib.suppress(ProcessLookupError):
|
||||
os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
|
||||
# Wait briefly, then SIGKILL survivors
|
||||
for proc in processes:
|
||||
try:
|
||||
proc.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
with contextlib.suppress(ProcessLookupError):
|
||||
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
|
||||
proc.wait()
|
||||
sys.exit(returncode)
|
||||
|
||||
if not finished_indices:
|
||||
# No process finished yet, wait a bit
|
||||
for proc in processes:
|
||||
try:
|
||||
proc.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
continue
|
||||
|
||||
# Remove finished processes from the list
|
||||
for i in sorted(finished_indices, reverse=True):
|
||||
del processes[i]
|
||||
del pair_names[i]
|
||||
|
||||
click.echo(_("All molecule tests passed."))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
cli()
|
||||
Reference in New Issue
Block a user