fix(site-publish): preflight all immutable routes
Test / contract (pull_request) Successful in 6s

Authored-By: OpenAI (GPT-5) <noreply@openai.com>
This commit is contained in:
Evelyn Chen
2026-08-29 22:23:51 +00:00
parent 5261b9b99f
commit fb2e440bbb
3 changed files with 38 additions and 26 deletions
+25 -9
View File
@@ -149,9 +149,8 @@ def publish_immutable_rule(artifact, route, rule, html_dir, aws_env):
)
def s3_sync(artifact, route, site_dir, credential_env_names=None):
endpoint = artifact["s3_endpoint"]
html_dir = site_dir / artifact["build_dir"]
def publication_aws_env(artifact, credential_env_names=None):
"""Build the route-scoped AWS environment without leaking other credentials."""
access_key = env(artifact["credentials"]["access_key_env"])
secret_key = env(artifact["credentials"]["secret_key_env"])
aws_env = os.environ.copy()
@@ -165,6 +164,22 @@ def s3_sync(artifact, route, site_dir, credential_env_names=None):
"AWS_SECRET_ACCESS_KEY": secret_key,
"AWS_DEFAULT_REGION": os.environ.get("AWS_DEFAULT_REGION", "sjc001"),
})
return aws_env
def publish_route_immutables(artifact, route, site_dir, credential_env_names=None):
"""Publish one route's immutable partitions during the global preflight."""
html_dir = site_dir / artifact["build_dir"]
aws_env = publication_aws_env(artifact, credential_env_names)
for rule in artifact["cache_rules"]:
if _is_immutable(rule):
publish_immutable_rule(artifact, route, rule, html_dir, aws_env)
def s3_sync(artifact, route, site_dir, credential_env_names=None):
endpoint = artifact["s3_endpoint"]
html_dir = site_dir / artifact["build_dir"]
aws_env = publication_aws_env(artifact, credential_env_names)
bucket = artifact["bucket"]
object_prefix = route["path"].strip("/")
destination = f"s3://{bucket}/{object_prefix + '/' if object_prefix else ''}"
@@ -176,12 +191,6 @@ def s3_sync(artifact, route, site_dir, credential_env_names=None):
exclude_args = [arg for pattern in artifact["excludes"] for arg in ("--exclude", pattern)]
if artifact["excludes"]:
print(f"Excluding patterns: {artifact['excludes']}")
# Validate and publish every append-only target before a mutable channel can
# point at it. Partial immutable success is safe; partial mutable success is
# not.
for rule in artifact["cache_rules"]:
if _is_immutable(rule):
publish_immutable_rule(artifact, route, rule, html_dir, aws_env)
print(f"Syncing artifact {artifact['name']}{destination} via {endpoint}")
# Upload with the final cache policy before cleanup. Sync and deletion are
# scoped to the same current route prefix and cache partition. A route move
@@ -296,6 +305,13 @@ def deploy_static(site_name, site_dir, action_dir, token, cfg):
validate_publication_environment(cfg)
for artifact in cfg["artifacts"]:
validate_artifact_output(site_dir, artifact)
# Complete immutable work across the whole publication before any route's
# mutable pointers can change. Partial immutable success is safe; mixing a
# new route with an old route after a later immutable failure is not.
for route in cfg["routes"]:
publish_route_immutables(
artifact_by_name[route["artifact"]], route, site_dir, credential_env_names,
)
for route in cfg["routes"]:
s3_sync(artifact_by_name[route["artifact"]], route, site_dir, credential_env_names)
if cfg["compatibility"]: