feat: release v0.28 generic Go project scaffolding and lifecycle entry
This commit is contained in:
85
scripts/verifyProgramRelease.py
Normal file
85
scripts/verifyProgramRelease.py
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify the generic project CLI and its consumer from a fresh remote module cache."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
from verifyErrorInheritanceRelease import run
|
||||
|
||||
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 / "reports/programRemote.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()
|
||||
with tempfile.TemporaryDirectory(prefix="goOmniProgramRelease-") 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} with an empty module cache", flush=True)
|
||||
run(["go", "install", MODULE + "/cmd/go-omni@" + args.version], temp, env)
|
||||
cli = temp / "bin/go-omni"
|
||||
# No version argument and no local replacement: installed CLI owns the default.
|
||||
starter = temp / "program"
|
||||
run([cli, "project", "new", starter, "--module", "example.com/released"], 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")
|
||||
downloaded = Path(dependency["Dir"])
|
||||
env.update(GO_OMNI_SCAFFOLD_INTEGRATION="1", GO_OMNI_RELEASE_CLI=str(cli), GO_OMNI_RELEASE_VERSION=args.version)
|
||||
print("Testing downloaded generator, shared file transaction and generated program lifecycle", flush=True)
|
||||
output = run(["go", "test", "-race", "-count=1", "-json", "-timeout=240s",
|
||||
"./cmd/go-omni/projectgen", "./cmd/go-omni/internal/genfs"], downloaded, 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 "TestGeneratedProgramConsumer" in passed and "TestConflictRollsBackEarlierWrites" in passed, output
|
||||
assert not any(e.get("Action") == "fail" for e in events), output
|
||||
evidence = "".join(e.get("Output", "") for e in events)
|
||||
assert "no local replace" in evidence, evidence
|
||||
# Check the committed standalone example with only project-owned additions.
|
||||
for name in ("internal/config/config.go", "internal/run/run.go", "etc/config.yaml"):
|
||||
text = (ROOT / "examples/program" / name).read_text().replace("example.com/program", "example.com/released")
|
||||
(starter / name).write_text(text)
|
||||
run([cli, "project", "check"], starter, env)
|
||||
run(["go", "run", ".", "-check-config"], starter, env)
|
||||
example_output = run(["go", "run", "."], starter, env)
|
||||
assert "program.completed" in example_output, example_output
|
||||
run(["go", "test", "./..."], starter, env)
|
||||
sources = list((ROOT / "cmd/go-omni/projectgen").rglob("*.go"))
|
||||
sources += list((ROOT / "cmd/go-omni/projectgen/templates").glob("*.tmpl"))
|
||||
sources += list((ROOT / "cmd/go-omni/internal/genfs").glob("*.go"))
|
||||
sources += [ROOT / "cmd/go-omni/project.go", ROOT / "scripts/verifyProgramRelease.py"]
|
||||
report = {
|
||||
"passed": True, "frameworkVersion": args.version, "frameworkSum": dependency["Sum"],
|
||||
"freshModuleCache": True, "noLocalReplace": True,
|
||||
"durationSeconds": round(time.monotonic() - started, 2), "tests": passed,
|
||||
"checks": ["remoteCLIInstall", "installedVersionDefault", "generatedConsumerBuildRaceVet",
|
||||
"defaultNoAPIFiles", "defaultNoNetworkListener", "finiteProgram", "SIGINT", "SIGTERM",
|
||||
"initializationFailureAndPanic", "runErrorPanicGoexit", "fatalComponentFailure",
|
||||
"nonzeroProcessFailureAndTimeoutExits", "partialStartupDraining", "acknowledgementBeforeResourceClose", "workContextSurvivesGracefulStop",
|
||||
"cancellationGrace", "unresponsiveRunAndBackgroundWork", "retainedResourcesAndLogger",
|
||||
"explicitConfigurationOnly", "checkConfigWithoutDependencies", "handwrittenFilesPreserved",
|
||||
"generatedFileConflict", "missingGeneratedFileRestored", "missingHandwrittenFileNotRecreated",
|
||||
"readOnlyCheckAndDryRun", "templateDriftUpgrade", "manifestIdentityAndVersionGuards",
|
||||
"symlinkAndLockProtection", "fileTransactionRollback", "publishedExampleFiniteRun"],
|
||||
"sourceHashes": {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted(sources)},
|
||||
}
|
||||
args.report.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n")
|
||||
print(f"PASS remote generic program release {args.version}: {len(report['checks'])} checks", flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user