1 Commits
Author SHA1 Message Date
Evelyn Chen 069d1baaba feat(site-publish): add split-surface publishing
Test / contract (pull_request) Successful in 6s
Authored-By: OpenAI (GPT-5) <noreply@openai.com>
2026-08-29 21:59:13 +00:00
2 changed files with 11 additions and 13 deletions
+10 -11
View File
@@ -183,24 +183,26 @@ def s3_sync(artifact, route, site_dir, credential_env_names=None):
if _is_immutable(rule): if _is_immutable(rule):
publish_immutable_rule(artifact, route, rule, html_dir, aws_env) publish_immutable_rule(artifact, route, rule, html_dir, aws_env)
print(f"Syncing artifact {artifact['name']}{destination} via {endpoint}") print(f"Syncing artifact {artifact['name']}{destination} via {endpoint}")
# Upload with the final cache policy before cleanup. Sync and deletion are # Sync and deletion are scoped to the current route prefix. A route move
# scoped to the same current route prefix and cache partition. A route move
# leaves its old bucket partition intact but unreachable after the old # leaves its old bucket partition intact but unreachable after the old
# Ingress disappears, while stale mutable keys on the serving prefix are # Ingress disappears, while stale mutable keys on the serving prefix are
# deleted. Immutable subtrees are structurally excluded. `cp --recursive` # deleted. Immutable subtrees are structurally excluded. Partitioned
# refreshes metadata atomically per object before `sync --delete` removes # `cp --recursive` calls then re-upload each file once to refresh metadata
# stale keys without ever exposing new bytes under a provisional policy. # (cache-control, content-type) on objects sync skipped as unchanged.
# A no-op deploy therefore transfers the artifact bytes once. # A no-op deploy therefore transfers the artifact bytes once.
# AWS CLI guesses Content-Type from file extension on local→S3 uploads, # AWS CLI guesses Content-Type from file extension on local→S3 uploads,
# so a fresh upload always carries the right MIME type. # so a fresh upload always carries the right MIME type.
sync_excludes = [*exclude_args,
*(arg for path in immutable_paths for arg in ("--exclude", f"{path}/*"))]
run(["aws", "--endpoint-url", endpoint, "s3", "sync", f"{html_dir}/", destination,
"--delete", "--only-show-errors", "--cache-control", default_cache,
*sync_excludes], env=aws_env)
print("Re-stamping metadata on all objects...")
specific_paths = [rule["path"] for rule in artifact["cache_rules"] if rule["path"]] specific_paths = [rule["path"] for rule in artifact["cache_rules"] if rule["path"]]
default_filters = [arg for path in specific_paths for arg in ("--exclude", f"{path}/*")] default_filters = [arg for path in specific_paths for arg in ("--exclude", f"{path}/*")]
run(["aws", "--endpoint-url", endpoint, "s3", "cp", f"{html_dir}/", destination, run(["aws", "--endpoint-url", endpoint, "s3", "cp", f"{html_dir}/", destination,
"--recursive", "--only-show-errors", "--cache-control", default_cache, "--recursive", "--only-show-errors", "--cache-control", default_cache,
*default_filters, *exclude_args], env=aws_env) *default_filters, *exclude_args], env=aws_env)
run(["aws", "--endpoint-url", endpoint, "s3", "sync", f"{html_dir}/", destination,
"--delete", "--only-show-errors", "--cache-control", default_cache,
*default_filters, *exclude_args], env=aws_env)
for rule in artifact["cache_rules"]: for rule in artifact["cache_rules"]:
if not rule["path"]: if not rule["path"]:
continue continue
@@ -215,9 +217,6 @@ def s3_sync(artifact, route, site_dir, credential_env_names=None):
run(["aws", "--endpoint-url", endpoint, "s3", "cp", f"{html_dir}/", destination, run(["aws", "--endpoint-url", endpoint, "s3", "cp", f"{html_dir}/", destination,
"--recursive", "--only-show-errors", "--cache-control", rule["cache_control"], "--recursive", "--only-show-errors", "--cache-control", rule["cache_control"],
"--exclude", "*", "--include", include, *child_filters, *exclude_args], env=aws_env) "--exclude", "*", "--include", include, *child_filters, *exclude_args], env=aws_env)
run(["aws", "--endpoint-url", endpoint, "s3", "sync", f"{html_dir}/", destination,
"--delete", "--only-show-errors", "--cache-control", rule["cache_control"],
"--exclude", "*", "--include", include, *child_filters, *exclude_args], env=aws_env)
def garage_admin(method, path, token, body=None): def garage_admin(method, path, token, body=None):
+1 -2
View File
@@ -405,8 +405,7 @@ class PublishingTests(unittest.TestCase):
rendered = [" ".join(command) for command, _ in commands] rendered = [" ".join(command) for command, _ in commands]
self.assertIn("s3://baseline-dist/dist/", rendered[0]) self.assertIn("s3://baseline-dist/dist/", rendered[0])
self.assertIn("releases/*", rendered[0]) self.assertIn("releases/*", rendered[0])
self.assertNotIn("--delete", rendered[0]) self.assertIn("--delete", rendered[0])
self.assertIn("--delete", rendered[1])
self.assertTrue(all("s3://baseline-dist/dist/" in command for command in rendered[1:])) self.assertTrue(all("s3://baseline-dist/dist/" in command for command in rendered[1:]))
self.assertTrue(any("channels/" in command and self.assertTrue(any("channels/" in command and
"public, max-age=0, must-revalidate" in command "public, max-age=0, must-revalidate" in command