Files
go-omni/scripts/verifyManagedRelease.py

98 lines
6.0 KiB
Python

#!/usr/bin/env python3
"""Accept a fixed managed-project release using a fresh remote module cache."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import signal
import tempfile
import time
from verifyErrorInheritanceRelease import run
from verifyManagedProject import serve, verify_contract_changes
ROOT = Path(__file__).resolve().parents[1]
MODULE = "git.yuntuo.hk/k2safe/go-omni"
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--version", required=True)
parser.add_argument("--report", type=Path, default=ROOT / "performance/managedProjectRemote.json")
args = parser.parse_args()
if not re.fullmatch(r"v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?", args.version):
parser.error("a fixed release version is required")
env = {k: v for k, v in os.environ.items() if not k.startswith("GO_OMNI_")}
env.update(GOWORK="off", GOFLAGS="", GOPRIVATE="git.yuntuo.hk", GONOSUMDB="git.yuntuo.hk")
started = time.monotonic()
checks = ["remoteCLIInstall", "freshModuleCache", "fixedVersionNoReplace"]
with tempfile.TemporaryDirectory(prefix="goOmniManagedRelease-") as directory:
temp = Path(directory)
(temp / "bin").mkdir()
env.update(GOBIN=str(temp / "bin"), GOMODCACHE=str(temp / "modcache"))
print(f"Installing remote CLI {args.version} in an empty module cache", flush=True)
run(["go", "install", MODULE + "/cmd/go-omni@" + args.version], temp, env)
cli = temp / "bin/go-omni"
# Do not pass --framework-version: verify that the installed CLI pins
# its own downloaded version, without a source-directory replacement.
starter = temp / "starter"
run([cli, "new", "--module", "example.com/releaseStarter", starter], temp, env)
run(["go", "mod", "tidy"], starter, env)
dependency = json.loads(run(["go", "list", "-m", "-json", MODULE], starter, env))
assert dependency["Version"] == args.version and dependency.get("Sum") and not dependency.get("Replace")
verify_contract_changes(cli, starter, env)
checks += ["contractChangePreview", "persistentLogicSignatureFailure", "businessSourcePreserved", "fixedConsumerBuild"]
serve(["go", "run", "."], starter, env, "nethttp", signal.SIGTERM, False, logs=temp)
for transport in ("nethttp", "native"):
for sig in (signal.SIGINT, signal.SIGTERM):
serve([str(starter / "releaseStarter")], starter, env, transport, sig, True, logs=temp)
checks += ["rootGoRun", "nethttpAndNativeSignals", "listenerReleased"]
# Only project-owned business implementations come from the example.
# Main, handlers, DTOs and startup are emitted by the downloaded CLI.
managed = temp / "managed"
run([cli, "new", "--api", ROOT / "examples/contracts/managed.api", "--module", "example.com/managed", managed], temp, env)
example = ROOT / "examples/managedService"
sources = list((example / "internal/logic/demo").glob("*.go"))
sources += [example / "internal/svc/options.go", example / "internal/apierrors/catalog.go"]
for source in sources:
target = managed / source.relative_to(example)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(source, target)
run([cli, "api", "generate", "--check"], managed, env)
run(["go", "mod", "tidy"], managed, env)
run(["go", "build", "-o", temp / "managedServer", "."], managed, env)
managed_dependency = json.loads(run(["go", "list", "-m", "-json", MODULE], managed, env))
assert managed_dependency["Sum"] == dependency["Sum"] and not managed_dependency.get("Replace")
for transport in ("nethttp", "native"):
serve([str(temp / "managedServer")], managed, env, transport, signal.SIGTERM, True, contract=True, logs=temp)
checks += ["default404And405", "goOwnedErrorCatalog", "unwrappedDownloads", "rawCallback", "multipart400And413", "nethttpAndNativeTransfers"]
env.update(GO_OMNI_SCAFFOLD_INTEGRATION="1", GO_OMNI_RELEASE_CLI=str(cli), GO_OMNI_RELEASE_VERSION=args.version)
tests = ["TestManagedGeneratedProject", "TestErrorInheritanceConsumer", "TestErrorParamsConsumer", "TestCompatibilityConsumer"]
print("Running generated consumers against the remote framework, including lifecycle failure cleanup", flush=True)
output = run(["go", "test", "-race", "-count=1", "-json", "-timeout=240s", "./cmd/go-omni/apigen", "-run", "^(" + "|".join(tests) + ")$"], ROOT, env)
events = [json.loads(line) for line in output.splitlines() if line.startswith("{")]
passed = [e["Test"] for e in events if e.get("Action") == "pass" and "Test" in e]
assert sorted(passed) == sorted(tests), output
evidence = "".join(e.get("Output", "") for e in events)
sums = re.findall(r"checksum (h1:\S+); no local replace", evidence)
assert len(sums) == len(tests) and all(s == dependency["Sum"] for s in sums), "remote consumer checksum mismatch"
checks += ["initializationAndListenFailureCleanup", "drainBeforeDependencyClose", "generatedConsumerRaceTests", "errorParamsAndInheritance", "manualHooksAndScalars"]
sources = ["scripts/verifyManagedRelease.py", "scripts/verifyManagedProject.py", "cmd/go-omni/apigen/managed_test.go"]
sources += [str(p.relative_to(ROOT)) for p in (ROOT / "cmd/go-omni/apigen/testdata/managed").glob("*.go")]
report = {"passed": True, "frameworkVersion": args.version, "frameworkSum": dependency["Sum"],
"freshModuleCache": True, "noLocalReplace": True,
"durationSeconds": round(time.monotonic() - started, 2), "checks": checks,
"sourceHashes": {p: hashlib.sha256((ROOT / p).read_bytes()).hexdigest() for p in sources}}
args.report.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n")
print(f"PASS remote managed project release {args.version}; temporary files and processes removed", flush=True)
if __name__ == "__main__":
main()