from __future__ import annotations import subprocess from pathlib import Path ROOT = Path(__file__).resolve().parents[2] IN_PLACE = ROOT / "scripts" / "upgrade_gitea_runner_3_5.sh" FLEET_BOOTSTRAP = ROOT / "scripts" / "upgrade_ci_runner_fleet.sh" OLD_IMAGE = ( "gitea/runner@sha256:" "3ebc3ad9654552377896e1edbc77ac420025bb01ff1736bc43232d0bf2866f5a" ) NEW_IMAGE = ( "gitea/runner:3.5.0@sha256:" "66b7da94dc7dcadb2e076bec6928221336a9a637196399281c4b766fe1288242" ) def test_runner_3_5_upgrade_script_is_syntactically_valid() -> None: for script in (IN_PLACE, FLEET_BOOTSTRAP): subprocess.run( ["bash", "-n"], input=script.read_bytes().replace(b"\r\n", b"\n"), check=True, ) def test_future_fleet_bootstrap_uses_reviewed_runner_3_5_pin() -> None: text = FLEET_BOOTSTRAP.read_text(encoding="utf-8") assert NEW_IMAGE in text assert OLD_IMAGE not in text assert "gitea/runner:latest" not in text assert "# v3.5.0" in text def test_in_place_upgrade_is_fixed_target_and_fail_closed() -> None: text = IN_PLACE.read_text(encoding="utf-8") assert 'readonly PRIMARY="itworx-security-runner"' in text assert 'readonly SECONDARY="itworx-validation-runner-2"' in text assert 'readonly DIND="gitea-runner-dind"' in text assert OLD_IMAGE in text assert NEW_IMAGE in text assert 'readonly CONFIRMATION="UPGRADE_GITEA_RUNNER_3_5"' in text assert 'active DinD workload prevents runner upgrade' in text assert 'grep -F -c "$OLD_IMAGE"' in text assert 'runner image baseline is not the reviewed 2.0.0 pin' in text assert 'docker pull "$NEW_IMAGE"' in text assert 'gitea-runner "$NEW_IMAGE" --version' in text assert 'set_runner_disabled "$PRIMARY" true' in text assert 'set_runner_disabled "$SECONDARY" true' in text assert 'set_runner_disabled "$PRIMARY" false' in text assert 'set_runner_disabled "$SECONDARY" false' in text assert ( 'up -d --no-deps --force-recreate "$PRIMARY_SERVICE" "$SECONDARY_SERVICE"' in text ) assert 'docker compose --project-directory "$ROOT"' in text assert 'cp -a -- "$backup/docker-compose.yml" "$COMPOSE_FILE"' in text # The maintenance path may inspect DinD for active CI work, but must never # recreate or prune the daemon, Gitea, unrelated networks, or host storage. forbidden = ( 'force-recreate "$DIND"', 'force-recreate "$GITEA_CONTAINER"', "docker system prune", "docker network prune", "docker volume prune", 'docker rm --force "$DIND"', 'docker rm --force "$GITEA_CONTAINER"', ) for token in forbidden: assert token not in text def test_runner_3_5_upgrade_preserves_separate_runner_credentials() -> None: text = IN_PLACE.read_text(encoding="utf-8") assert ( 'PRIMARY_RUNNER_FILE="/mnt/cache/appdata/itworx-security-runner/.runner"' in text ) assert ( 'SECONDARY_RUNNER_FILE="/mnt/cache/appdata/itworx-validation-runner-2/.runner"' in text ) assert ( 'readlink -f "$PRIMARY_RUNNER_FILE"' in text and 'readlink -f "$SECONDARY_RUNNER_FILE"' in text )