Add registry-backed module package releases
This commit is contained in:
@@ -26,6 +26,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument("--web-metadata", type=Path, required=True)
|
||||
parser.add_argument("--deployer", type=Path, required=True)
|
||||
parser.add_argument("--deployer-url", required=True)
|
||||
parser.add_argument("--package-lock", type=Path, required=True)
|
||||
parser.add_argument("--artifact-base-url", required=True)
|
||||
parser.add_argument("--source-commit", required=True)
|
||||
parser.add_argument("--version", required=True)
|
||||
@@ -45,6 +46,11 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
|
||||
def finalize(args: argparse.Namespace) -> dict[str, Any]:
|
||||
composition = _json_object(args.composition)
|
||||
_validate_package_lock(
|
||||
_json_object(args.package_lock),
|
||||
version=args.version,
|
||||
composition=composition,
|
||||
)
|
||||
api = _image_metadata(_json_object(args.api_metadata), "api")
|
||||
web = _image_metadata(_json_object(args.web_metadata), "web")
|
||||
dependencies = dict(_dependency(value) for value in args.dependency)
|
||||
@@ -99,6 +105,10 @@ def finalize(args: argparse.Namespace) -> dict[str, Any]:
|
||||
"url": args.deployer_url,
|
||||
"sha256": _sha256_file(args.deployer),
|
||||
},
|
||||
"package_lock": {
|
||||
"url": f"{artifact_base}/package-artifacts.lock.json",
|
||||
"sha256": _sha256_file(args.package_lock),
|
||||
},
|
||||
"images": {
|
||||
"api": {
|
||||
**api,
|
||||
@@ -247,6 +257,57 @@ def _json_object(path: Path) -> dict[str, Any]:
|
||||
return value
|
||||
|
||||
|
||||
def _validate_package_lock(
|
||||
lock: dict[str, Any],
|
||||
*,
|
||||
version: str,
|
||||
composition: dict[str, Any],
|
||||
) -> None:
|
||||
if lock.get("schema_version") != "1" or lock.get("release_version") != version:
|
||||
raise ValueError("package lock schema or release version does not match")
|
||||
unsigned = dict(lock)
|
||||
expected_hash = unsigned.pop("lock_sha256", None)
|
||||
actual_hash = hashlib.sha256(
|
||||
json.dumps(unsigned, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
||||
).hexdigest()
|
||||
if expected_hash != actual_hash:
|
||||
raise ValueError("package lock hash does not match its contents")
|
||||
rows = lock.get("python")
|
||||
if not isinstance(rows, list):
|
||||
raise ValueError("package lock Python artifacts are missing")
|
||||
locked = _package_identities(rows, name_key="name")
|
||||
composed = _package_identities(
|
||||
composition["python"]["packages"],
|
||||
name_key="package",
|
||||
)
|
||||
if locked != composed:
|
||||
raise ValueError("package lock does not match the runtime wheel composition")
|
||||
|
||||
|
||||
def _package_identities(
|
||||
rows: list[object],
|
||||
*,
|
||||
name_key: str,
|
||||
) -> dict[str, tuple[str, str]]:
|
||||
identities: dict[str, tuple[str, str]] = {}
|
||||
for row in rows:
|
||||
if not isinstance(row, dict):
|
||||
raise ValueError("package artifact identity is malformed")
|
||||
name = row.get(name_key)
|
||||
version = row.get("version")
|
||||
digest = row.get("sha256")
|
||||
if (
|
||||
not isinstance(name, str)
|
||||
or not isinstance(version, str)
|
||||
or not isinstance(digest, str)
|
||||
or SHA256.fullmatch(digest) is None
|
||||
or name in identities
|
||||
):
|
||||
raise ValueError("package artifact identity is malformed or duplicated")
|
||||
identities[name] = (version, digest)
|
||||
return identities
|
||||
|
||||
|
||||
def _https_url(value: str, label: str) -> str:
|
||||
parsed = urlsplit(value)
|
||||
if parsed.scheme != "https" or not parsed.netloc or parsed.username or parsed.password:
|
||||
|
||||
Reference in New Issue
Block a user