Initial curated ITWorx_MCP_Hub source from 368fba691ea8d23403b4d56b53b5ec670d7c5020
This commit is contained in:
commit
cb5be898ea
663 files changed
+82674
No files matched your search
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a service's image twice from a clean context and compare the results.
|
||||
|
||||
Distinguishes full image-ID (byte-for-byte, including embedded metadata like
|
||||
history timestamps) reproducibility from layer-content (diffID) reproducibility,
|
||||
per the WP-200 requirement not to claim byte-identical results without proof.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _run(command: list[str]) -> str:
|
||||
completed = subprocess.run( # noqa: S603
|
||||
command, cwd=ROOT, capture_output=True, text=True, check=True
|
||||
)
|
||||
return completed.stdout.strip()
|
||||
|
||||
|
||||
def _build(dockerfile: Path, tag: str, build_args: dict[str, str]) -> None:
|
||||
command = [
|
||||
"docker",
|
||||
"buildx",
|
||||
"build",
|
||||
"--no-cache",
|
||||
"--output",
|
||||
"type=docker,rewrite-timestamp=true,unpack=false",
|
||||
"-f",
|
||||
str(dockerfile),
|
||||
"-t",
|
||||
tag,
|
||||
]
|
||||
for name, value in build_args.items():
|
||||
command.extend(["--build-arg", f"{name}={value}"])
|
||||
command.append(str(ROOT))
|
||||
subprocess.run(command, cwd=ROOT, check=True) # noqa: S603
|
||||
|
||||
|
||||
def _image_id(tag: str) -> str:
|
||||
return _run(["docker", "inspect", "--format", "{{.Id}}", tag])
|
||||
|
||||
|
||||
def _layer_diff_ids(tag: str) -> list[str]:
|
||||
raw = _run(["docker", "inspect", "--format", "{{json .RootFS.Layers}}", tag])
|
||||
return list(json.loads(raw))
|
||||
|
||||
|
||||
def compare_two_builds(
|
||||
dockerfile: Path, build_args: dict[str, str], tag_a: str, tag_b: str
|
||||
) -> dict[str, Any]:
|
||||
_build(dockerfile, tag_a, build_args)
|
||||
_build(dockerfile, tag_b, build_args)
|
||||
image_id_a, image_id_b = _image_id(tag_a), _image_id(tag_b)
|
||||
layers_a, layers_b = _layer_diff_ids(tag_a), _layer_diff_ids(tag_b)
|
||||
return {
|
||||
"dockerfile": str(dockerfile.relative_to(ROOT)),
|
||||
"buildArgs": {key: value for key, value in build_args.items() if key != "SOURCE_DATE_EPOCH"}
|
||||
| {"SOURCE_DATE_EPOCH": build_args.get("SOURCE_DATE_EPOCH", "")},
|
||||
"imageIdA": image_id_a,
|
||||
"imageIdB": image_id_b,
|
||||
"imageIdMatch": image_id_a == image_id_b,
|
||||
"layerDiffIdsA": layers_a,
|
||||
"layerDiffIdsB": layers_b,
|
||||
"layerDiffIdsMatch": layers_a == layers_b,
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--dockerfile", type=Path, required=True)
|
||||
parser.add_argument("--service-name", required=False)
|
||||
parser.add_argument("--git-revision", required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
args.dockerfile = args.dockerfile.resolve()
|
||||
epoch = _run(["git", "show", "-s", "--format=%ct", args.git_revision])
|
||||
created = _run(["git", "show", "-s", "--format=%cI", args.git_revision])
|
||||
build_args = {
|
||||
"GIT_REVISION": args.git_revision,
|
||||
"SOURCE_DATE_EPOCH": epoch,
|
||||
"BUILD_DATE": created,
|
||||
}
|
||||
if args.service_name:
|
||||
build_args["SERVICE_NAME"] = args.service_name
|
||||
stamp = datetime.now(UTC).strftime("%Y%m%dT%H%M%SZ")
|
||||
tag_a = f"local/wp200-repro-a:{stamp}"
|
||||
tag_b = f"local/wp200-repro-b:{stamp}"
|
||||
report = compare_two_builds(args.dockerfile, build_args, tag_a, tag_b)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print(json.dumps(report, indent=2, sort_keys=True))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in new issue
Block a user