"""ORCH-036 TC-14: prod deploy is build-ONCE — retag the staging image, no rebuild (AC-7). The detached prod-deploy command must pass ``SOURCE_IMAGE=`` to the hook so it retags the staging-validated image onto the prod tag instead of running ``docker build``. We assert the composed ssh command carries the staging source image and never asks the hook to build. """ import os os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token") os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token") from src import self_deploy # noqa: E402 def test_tc14_deploy_command_retags_staging_image_no_build(monkeypatch): monkeypatch.setattr(self_deploy.settings, "deploy_ssh_user", "slin") monkeypatch.setattr(self_deploy.settings, "deploy_ssh_host", "mva154") monkeypatch.setattr( self_deploy.settings, "deploy_prod_source_image", "orchestrator-orchestrator-staging" ) cmd = self_deploy.build_deploy_command("orchestrator", "ORCH-036", "feature/ORCH-036-x") remote = cmd[-1] # The prevalidated staging image is handed to the hook as SOURCE_IMAGE (build-once). assert "SOURCE_IMAGE=orchestrator-orchestrator-staging" in remote # No rebuild is requested in the remote command. assert "docker build" not in remote assert "--build" not in remote def test_tc14_hook_retag_branch_present(): """The hook itself must honour SOURCE_IMAGE by retagging (no rebuild).""" import pathlib hook = pathlib.Path(__file__).resolve().parents[1] / "scripts" / "orchestrator-deploy-hook.sh" text = hook.read_text(encoding="utf-8") assert 'SOURCE_IMAGE="${SOURCE_IMAGE:-}"' in text # Build-once retag branch present; the hook never runs `docker build`. assert 'docker tag "$SOURCE_IMAGE" "$TARGET_IMAGE"' in text # No EXECUTABLE `docker build` line on the PROD path (comments are fine). # ORCH-058: the only build allowed is the staging-freshness rebuild, # which is explicitly tagged with `--build-arg GIT_SHA` (Strategy A). # Executable lines only: drop comments and `log "..."` strings that merely # mention "docker build" in human-readable diagnostics. exec_lines = [ ln.strip() for ln in text.splitlines() if ln.strip() and not ln.strip().startswith("#") and not ln.strip().startswith("log ") ] for ln in exec_lines: if "docker build" in ln: assert "--build-arg GIT_SHA" in ln, ( f"unexpected docker build on prod retag path: {ln}" ) # --------------------------------------------------------------------------- # ORCH-058 TC-06: build_deploy_command threads EXPECTED_REVISION (Strategy B) # --------------------------------------------------------------------------- def test_tc06_deploy_command_passes_expected_revision(monkeypatch): """When image-freshness is active, the prod hook receives EXPECTED_REVISION.""" from src import image_freshness monkeypatch.setattr(self_deploy.settings, "deploy_ssh_user", "slin") monkeypatch.setattr(self_deploy.settings, "deploy_ssh_host", "mva154") monkeypatch.setattr( self_deploy.settings, "deploy_prod_source_image", "orchestrator-orchestrator-staging" ) monkeypatch.setattr( image_freshness, "expected_revision", lambda repo, branch: "abc123def456" ) cmd = self_deploy.build_deploy_command("orchestrator", "ORCH-058", "feature/ORCH-058-x") remote = cmd[-1] assert "EXPECTED_REVISION=abc123def456" in remote def test_tc06_no_expected_revision_when_inactive(monkeypatch): """When image-freshness resolves to no SHA, EXPECTED_REVISION is omitted.""" from src import image_freshness monkeypatch.setattr(self_deploy.settings, "deploy_ssh_user", "slin") monkeypatch.setattr(self_deploy.settings, "deploy_ssh_host", "mva154") monkeypatch.setattr( self_deploy.settings, "deploy_prod_source_image", "orchestrator-orchestrator-staging" ) monkeypatch.setattr(image_freshness, "expected_revision", lambda repo, branch: "") cmd = self_deploy.build_deploy_command("orchestrator", "ORCH-058", "feature/ORCH-058-x") remote = cmd[-1] assert "EXPECTED_REVISION=" not in remote