RPF-WP-0061-T03: ccr/* branch gate and CI workflow; first automated platform review on CCR-2026-0051
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 306568@bnt-lap001 Assistant-Session: 8bdf8150-ea56-41e7-9132-13f04f6b0b91
This commit is contained in:
parent
b6148da443
commit
10fddbc98f
5 changed files with 191 additions and 7 deletions
|
|
@ -220,6 +220,64 @@ def command_check_ids(args: argparse.Namespace, cc: Any) -> int:
|
|||
return 1 if duplicates else 0
|
||||
|
||||
|
||||
def tree_digests(root: Path) -> dict[str, str]:
|
||||
return {
|
||||
str(path.relative_to(root)): hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
for path in root.rglob("*")
|
||||
if path.is_file() and ".git" not in path.relative_to(root).parts
|
||||
and "__pycache__" not in path.parts
|
||||
}
|
||||
|
||||
|
||||
def branch_gate(main_root: Path, stem: str, cc: Any, profile_path: Path = DEFAULT_PROFILE) -> list[str]:
|
||||
"""CI gate for a ccr/<stem> branch checked out at REPO_DIR (RPF-WP-0061-T03).
|
||||
|
||||
The branch may add exactly one new CCR and its new policy file, nothing
|
||||
else; the CCR must be an in-profile submission from a listed submitter with
|
||||
a policy-as-code review against the current profile digest.
|
||||
"""
|
||||
errors: list[str] = []
|
||||
ccr_rel = f"credential-change-requests/{stem}.yaml"
|
||||
path = REPO_DIR / ccr_rel
|
||||
if not CCR_FILE_RE.match(path.name) or not path.exists():
|
||||
return [f"branch ccr/{stem} must add {ccr_rel}"]
|
||||
if (main_root / ccr_rel).exists():
|
||||
errors.append(f"{ccr_rel} already exists on main")
|
||||
ccr, validation_errors, _warnings = cc.validate_ccr(path)
|
||||
errors.extend(f"validate: {item}" for item in validation_errors)
|
||||
policy_rel = (ccr.get("openbao") or {}).get("policy_file")
|
||||
allowed = {ccr_rel} | ({policy_rel} if policy_rel else set())
|
||||
branch, main = tree_digests(REPO_DIR), tree_digests(main_root)
|
||||
changed = {k for k in branch.keys() | main.keys() if branch.get(k) != main.get(k)}
|
||||
for extra in sorted(changed - allowed):
|
||||
errors.append(f"ccr branch may not change {extra}")
|
||||
if policy_rel and policy_rel in main and main[policy_rel] != branch.get(policy_rel):
|
||||
errors.append(f"{policy_rel} exists on main; a submission may not modify it")
|
||||
if ccr.get("status") != "proposed":
|
||||
errors.append(f"status must be proposed, got {ccr.get('status')}")
|
||||
profile, digest = load_profile(profile_path)
|
||||
if (ccr.get("requester") or {}).get("agent") not in profile["submitters"]:
|
||||
errors.append(f"requester is not a listed submitter {profile['submitters']}")
|
||||
errors.extend(standard_violations(ccr, profile, path.parent, cc))
|
||||
comments = (ccr.get("review") or {}).get("comments") or []
|
||||
if len(comments) != 1 or comments[0].get("decision") != "approved" or digest not in str(
|
||||
comments[0].get("comment")
|
||||
):
|
||||
errors.append("review must hold exactly the policy-as-code approval for the current profile")
|
||||
errors.extend(f"duplicate id: {k} {v}" for k, v in duplicate_ids(path.parent, cc).items())
|
||||
return errors
|
||||
|
||||
|
||||
def command_check_branch(args: argparse.Namespace, cc: Any) -> int:
|
||||
stem = args.branch.removeprefix("refs/heads/").removeprefix("ccr/")
|
||||
errors = branch_gate(Path(args.main_root).resolve(), stem, cc)
|
||||
for item in errors:
|
||||
print(f"[FAIL] ccr/{stem}: {item}")
|
||||
if not errors:
|
||||
print(f"[OK] ccr/{stem}: one in-profile CCR, nothing else changed")
|
||||
return 1 if errors else 0
|
||||
|
||||
|
||||
def git(*argv: str) -> None:
|
||||
subprocess.run(["git", "-C", str(REPO_DIR), *argv], check=True)
|
||||
|
||||
|
|
@ -294,6 +352,13 @@ def add_parsers(sub: Any, cc: Any, state_hub_url: str) -> None:
|
|||
check_ids = sub.add_parser("check-ids", help="Fail on duplicate CCR ids or id/file name mismatch")
|
||||
check_ids.set_defaults(func=lambda args: command_check_ids(args, cc))
|
||||
|
||||
check_branch = sub.add_parser(
|
||||
"check-branch", help="CI gate: a ccr/* branch adds exactly one in-profile CCR and its policy"
|
||||
)
|
||||
check_branch.add_argument("--branch", required=True, help="ccr/<id>-<slug> or refs/heads/ccr/...")
|
||||
check_branch.add_argument("--main-root", required=True, help="tree of main to compare against")
|
||||
check_branch.set_defaults(func=lambda args: command_check_branch(args, cc))
|
||||
|
||||
submit_p = sub.add_parser(
|
||||
"submit",
|
||||
help="File a client CCR draft under the next CCR id and run the standard review",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue