98 lines
6.3 KiB
Python
98 lines
6.3 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", "dependencyFatalShutdown", "fatalProcessExitCode", "initializationShutdownNotification", "configuredBuiltinEndpoints", "projectHealthEnvelope", "endpointConflictCleanup"]
|
|
|
|
sources = ["lifecycle/controller.go", "config/endpoints.go", "health/health.go", "httpx/response.go", "app/app.go", "app/transport.go", "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()
|