mirror of
https://github.com/hanzoai/agent.git
synced 2026-09-11 21:08:51 +00:00
Consolidate all Hanzo agent infrastructure into one repo: - control-plane/: Go control plane server (was agents-new) - sdk/: Go, Python, TypeScript SDKs - deployments/: Docker, Kubernetes, Helm configs - Dockerfile.sdk: Python SDK image (ghcr.io/hanzoai/agent:latest) - CI builds both SDK + control-plane images No more agentfield — everything is hanzoai/agent.
128 lines
3.4 KiB
Python
Executable File
128 lines
3.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Generate and insert release notes produced by git-cliff."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
CHANGELOG = REPO_ROOT / "CHANGELOG.md"
|
|
CLIFF_CONFIG = REPO_ROOT / ".cliff.toml"
|
|
MARKER = "<!-- changelog:entries -->"
|
|
|
|
|
|
def run_git_cliff(version: str) -> str:
|
|
"""Invoke git-cliff and return the rendered release entry."""
|
|
cmd = [
|
|
"git",
|
|
"cliff",
|
|
"--config",
|
|
str(CLIFF_CONFIG),
|
|
"--unreleased",
|
|
"--tag",
|
|
version,
|
|
]
|
|
result = subprocess.run(
|
|
cmd,
|
|
cwd=REPO_ROOT,
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
if result.returncode != 0:
|
|
msg = result.stderr.strip() or result.stdout.strip()
|
|
raise SystemExit(
|
|
f"git-cliff failed with exit code {result.returncode}: {msg}"
|
|
)
|
|
return result.stdout.strip()
|
|
|
|
|
|
def ensure_marker(text: str) -> tuple[str, str]:
|
|
if MARKER not in text:
|
|
raise SystemExit(
|
|
f"Missing changelog marker '{MARKER}'. "
|
|
"Make sure CHANGELOG.md follows the expected template."
|
|
)
|
|
before, after = text.split(MARKER, 1)
|
|
return before, after
|
|
|
|
|
|
def remove_existing_entry(body: str, version: str) -> str:
|
|
pattern = re.compile(
|
|
rf"^## \[{re.escape(version)}].*?(?=^## \[|\Z)",
|
|
re.MULTILINE | re.DOTALL,
|
|
)
|
|
updated, count = pattern.subn("", body, count=1)
|
|
if count:
|
|
return updated.lstrip("\n")
|
|
return body
|
|
|
|
|
|
def build_changelog(prefix: str, entry: str, suffix: str) -> str:
|
|
suffix = suffix.lstrip("\n")
|
|
parts = [
|
|
prefix.rstrip(),
|
|
"",
|
|
MARKER,
|
|
"",
|
|
entry.strip(),
|
|
]
|
|
if suffix:
|
|
parts.extend(["", suffix.rstrip()])
|
|
return "\n".join(part for part in parts if part is not None) + "\n"
|
|
|
|
|
|
def update_changelog(version: str, entry: str, dry_run: bool) -> None:
|
|
current = CHANGELOG.read_text(encoding="utf-8")
|
|
before, after = ensure_marker(current)
|
|
cleaned_after = remove_existing_entry(after, version)
|
|
next_text = build_changelog(before, entry, cleaned_after)
|
|
if dry_run:
|
|
sys.stdout.write(next_text)
|
|
return
|
|
CHANGELOG.write_text(next_text, encoding="utf-8")
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(
|
|
description="Render the latest git history into CHANGELOG.md."
|
|
)
|
|
parser.add_argument(
|
|
"--version",
|
|
required=True,
|
|
help="Semantic version (without leading 'v') for the release entry.",
|
|
)
|
|
parser.add_argument(
|
|
"--dry-run",
|
|
action="store_true",
|
|
help="Print the would-be changelog instead of updating the file.",
|
|
)
|
|
args = parser.parse_args(argv)
|
|
|
|
try:
|
|
rendered = run_git_cliff(args.version)
|
|
except FileNotFoundError as exc:
|
|
raise SystemExit(
|
|
"git-cliff is not installed. Install it via "
|
|
"`cargo install git-cliff` or download a release binary."
|
|
) from exc
|
|
|
|
if not rendered:
|
|
today = datetime.utcnow().strftime("%Y-%m-%d")
|
|
rendered = (
|
|
f"## [{args.version}] - {today}\n\n"
|
|
"- No notable changes were recorded for this release.\n"
|
|
)
|
|
|
|
update_changelog(args.version, rendered, args.dry_run)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|