Add the deploy readiness gate
Python · Python · advanced · greenfield
Adds the deploy pipeline: a readiness gate, the deploy step that repoints ECS services and Lambdas at the built image, and a rollback helper. Verified the gate blocks running builds and missing builds, and that deploy wires the tag through to every target.
The CI/CD pipeline for an ingest service. A build is assigned its image_tag at start (before the push), and registry.image_exists(tag) reports whether the image was actually pushed and is pullable. registry.get_build(commit) returns the build with .status (running/succeeded/failed/canceled) and .image_tag, or None. Aggressive build concurrency means a build can be canceled mid-flight after it has already been tagged. Once a build has finished, its record and image_tag never change. registry.deployed_tag(service) returns the tag the service ran before its current deployment, or None.
Requirements
- is_ready_to_deploy(commit_sha) gates deployment: it must return True ONLY when the commit's build genuinely produced a deployable artifact — the build SUCCEEDED and its container image is actually present and pullable from the registry (registry.image_exists(image_tag)).
- A build is assigned its image_tag when it STARTS, before the image is pushed — so a build that is later canceled or fails still has an image_tag, but no pushed image. The tag's presence must NOT be treated as proof the image exists.
- deploy() points ECS services and Lambdas at the build's image_tag; if the gate wrongly passes a commit whose image was never pushed, every service is repointed at a missing image and fails to pull it.
Files touched
- pipeline/deploy.py
--- pipeline/deploy.py
+import logging
+from typing import Optional, List
+
+import registry
+import ecs
+import lambda_
+
+
+logger = logging.getLogger(__name__)
+
+
+class DeployBlocked(Exception):
+ """Raised when deployment cannot proceed due to build state."""
+ pass
+
+
+def is_ready_to_deploy(commit_sha: str) -> bool:
+ build = registry.get_build(commit_sha)
+ if build is None:
+ logger.debug(f"No build found for commit {commit_sha}")
+ return False
+
+ if build.status == "running":
+ logger.debug(f"Build for {commit_sha} is still running")
+ return False
+
+ if not build.image_tag:
+ logger.debug(f"Build for {commit_sha} has no image tag")
+ return False