fix production manifest publishing

This commit is contained in:
2026-07-19 16:09:51 +04:00
parent f02f4d73f0
commit 63c9af7846
+70 -4
View File
@@ -1,17 +1,43 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import hashlib
import json
import os import os
import sys import sys
from pathlib import Path from pathlib import Path
import boto3 import boto3
from botocore.config import Config from botocore.config import Config
from botocore.exceptions import ClientError
S3_PREFIX = "World of Warcraft" S3_PREFIX = "World of Warcraft"
MANIFEST_S3_KEY = "manifest.json" MANIFEST_S3_KEY = "manifest.json"
def compute_build_hash(files: list[dict]) -> str:
lines = [
f'{item["path"]}:{item["size"]}:{item["sha256"]}'
for item in sorted(files, key=lambda item: item["path"])
]
return hashlib.sha256("\n".join(lines).encode("utf-8")).hexdigest()
def merge_manifests(base_manifest: dict, staging_manifest: dict) -> dict:
files_by_path = {
item["path"]: item
for item in base_manifest.get("files", [])
}
for item in staging_manifest.get("files", []):
files_by_path[item["path"]] = item
files = [files_by_path[path] for path in sorted(files_by_path)]
return {
"buildHash": compute_build_hash(files),
"files": files,
}
def load_dotenv(env_path: Path) -> dict[str, str]: def load_dotenv(env_path: Path) -> dict[str, str]:
values: dict[str, str] = {} values: dict[str, str] = {}
if not env_path.exists(): if not env_path.exists():
@@ -61,6 +87,38 @@ def main() -> int:
config=Config(s3={"addressing_style": "path"}) if use_path_style else None, config=Config(s3={"addressing_style": "path"}) if use_path_style else None,
) )
manifest_path = project_root / "manifest.json"
if not manifest_path.exists():
print("ERROR: manifest.json not found", file=sys.stderr)
return 1
try:
staging_manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
print(f"ERROR: unable to read local manifest.json: {error}", file=sys.stderr)
return 1
try:
response = s3.get_object(Bucket=bucket, Key=MANIFEST_S3_KEY)
production_manifest = json.loads(response["Body"].read())
except ClientError as error:
error_code = error.response.get("Error", {}).get("Code")
if error_code not in {"NoSuchKey", "404"}:
print(f"ERROR: unable to read production manifest: {error}", file=sys.stderr)
return 1
production_manifest = {"files": []}
except (UnicodeDecodeError, json.JSONDecodeError) as error:
print(f"ERROR: invalid production manifest: {error}", file=sys.stderr)
return 1
merged_manifest = merge_manifests(production_manifest, staging_manifest)
print(
"Manifest merge: "
f"production={len(production_manifest.get('files', []))}, "
f"staging={len(staging_manifest.get('files', []))}, "
f"result={len(merged_manifest['files'])}"
)
dist_dir = project_root / "dist" dist_dir = project_root / "dist"
if not dist_dir.exists(): if not dist_dir.exists():
print(f"ERROR: dist/ directory not found. Run build first.", file=sys.stderr) print(f"ERROR: dist/ directory not found. Run build first.", file=sys.stderr)
@@ -87,18 +145,26 @@ def main() -> int:
errors = True errors = True
# Загрузка манифеста # Загрузка манифеста
manifest_path = project_root / "manifest.json" if errors:
if not manifest_path.exists(): print("\nFinished with errors; production manifest was not changed.")
print(f"ERROR: manifest.json not found", file=sys.stderr)
return 1 return 1
manifest_path.write_text(
json.dumps(merged_manifest, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
newline="\n",
)
print(f"Uploading manifest.json -> s3://{bucket}/{MANIFEST_S3_KEY}") print(f"Uploading manifest.json -> s3://{bucket}/{MANIFEST_S3_KEY}")
try: try:
s3.upload_file( s3.upload_file(
str(manifest_path), str(manifest_path),
bucket, bucket,
MANIFEST_S3_KEY, MANIFEST_S3_KEY,
ExtraArgs={"ContentType": "application/json"}, ExtraArgs={
"ContentType": "application/json",
"CacheControl": "no-cache, no-store, must-revalidate",
},
) )
print(f" OK") print(f" OK")
except Exception as e: except Exception as e: