|
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- Scaffold a deploy / QA note from a git commit range.
-
- Usage (from repo root):
- python scripts/generate_deploy_test_plan.py HEAD~5..HEAD
- python scripts/generate_deploy_test_plan.py abc1234..def5678 --out docs/deploy/20260801_example.md
-
- The script lists commits and touched files. You (or the agent) still fill
- test steps and expected results after reading the diffs.
- """
-
- from __future__ import annotations
-
- import argparse
- import subprocess
- import sys
- from datetime import date
- from pathlib import Path
-
- ROOT = Path(__file__).resolve().parents[1]
-
-
- def run(args: list[str]) -> str:
- r = subprocess.run(
- args,
- cwd=ROOT,
- capture_output=True,
- text=True,
- encoding="utf-8",
- errors="replace",
- )
- if r.returncode != 0:
- raise SystemExit(r.stderr or r.stdout or f"command failed: {args}")
- return r.stdout.strip()
-
-
- def main() -> None:
- p = argparse.ArgumentParser(description="Scaffold deploy test plan from git range")
- p.add_argument(
- "range",
- help="Git revision range, e.g. HEAD~5..HEAD or origin/production..HEAD",
- )
- p.add_argument(
- "--out",
- type=Path,
- default=None,
- help="Optional output path under docs/deploy/",
- )
- p.add_argument("--title", default="Deploy note (draft)", help="Document title")
- args = p.parse_args()
-
- log = run(["git", "log", "--oneline", args.range])
- if not log:
- print("No commits in range.", file=sys.stderr)
- sys.exit(1)
-
- stat = run(["git", "diff", "--stat", args.range])
- name_status = run(["git", "diff", "--name-status", args.range])
-
- commits = [ln for ln in log.splitlines() if ln.strip()]
- commit_bullets = "\n".join(f"- `{c[:7]}` — {c[8:]}" for c in commits)
-
- body = f"""# {args.title}
- Date: {date.today().isoformat()}
- Branch / build: (fill)
- Range: `{args.range}`
- Author: (fill)
-
- > Auto-scaffolded from git. **Replace the Test plan with real steps** after reviewing the diff.
-
- ## Summary
- - (TODO: 1–3 bullets — user-facing impact)
-
- ## Scope
- - Backend: (see files below)
- - Frontend: (check paired repo if UI)
- - DB / Liquibase: (none / list changelog files)
- - Config / ops: (none / list)
-
- ## Commits
- {commit_bullets}
-
- ## Files touched
- ```
- {name_status}
- ```
-
- ### Diffstat
- ```
- {stat}
- ```
-
- ## Test plan
- | # | Steps (who / where / data) | Expected result |
- |---|----------------------------|-----------------|
- | 1 | TODO — happy path | TODO |
- | 2 | TODO — edge / failure case | TODO |
- | 3 | TODO — regression on related screen | TODO |
-
- ## Out of scope / not tested
- - (TODO)
-
- ## Rollback
- - Revert range `{args.range}` / redeploy previous build
- """
-
- if args.out:
- out = args.out if args.out.is_absolute() else ROOT / args.out
- out.parent.mkdir(parents=True, exist_ok=True)
- out.write_text(body, encoding="utf-8")
- print(f"Wrote {out.relative_to(ROOT)}")
- else:
- sys.stdout.reconfigure(encoding="utf-8")
- print(body)
-
-
- if __name__ == "__main__":
- main()
|