diff --git a/tools/publish_gitea.py b/tools/publish_gitea.py index eff1bba..62d2628 100755 --- a/tools/publish_gitea.py +++ b/tools/publish_gitea.py @@ -52,6 +52,10 @@ def check_bundle(args, source): refs = remote_refs() require(refs.get("refs/heads/main") == args.old_commit, "Canonical main changed; review before publishing") require("refs/tags/" + args.tag not in refs, "Release tag already exists; never overwrite") + if args.ops_branch: + require(re.fullmatch(r"ops/[a-z0-9][a-z0-9/-]*", args.ops_branch), "Invalid operations branch") + require("refs/heads/" + args.ops_branch not in refs, "Operations branch already exists; do not overwrite") + run(["git", "--git-dir", source, "merge-base", "--is-ancestor", args.commit, "refs/heads/" + args.ops_branch]) return refs def record(path, state): @@ -165,14 +169,22 @@ def publish(args): server, stop, thread, client = askpass_server(directory, token) env = dict(os.environ, GIT_ASKPASS=str(client), GIT_TERMINAL_PROMPT="0", LC_ALL="C") # No force flag. The normal receive-pack checks and Gitea hooks run. + refspecs = [args.commit + ":refs/heads/main", "refs/tags/" + args.tag] + if args.ops_branch: + refspecs.append("refs/heads/" + args.ops_branch + ":refs/heads/" + args.ops_branch) run(["git", "--git-dir", source, "-c", "credential.helper=", "push", "--atomic", - REPOSITORY, args.commit + ":refs/heads/main", "refs/tags/" + args.tag], env=env, timeout=120) + REPOSITORY, *refspecs], env=env, timeout=120) state["push_performed"] = True state["phase"] = "PUSH_COMPLETED" after = remote_refs() require(after.get("refs/heads/main") == args.commit, "Main ref readback mismatch") resolved_tag = after.get("refs/tags/" + args.tag + "^{}", after.get("refs/tags/" + args.tag)) require(resolved_tag == args.commit, "Release tag readback mismatch") + if args.ops_branch: + expected = run(["git", "--git-dir", source, "rev-parse", "refs/heads/" + args.ops_branch]).stdout.decode().strip() + require(after.get("refs/heads/" + args.ops_branch) == expected, "Operations ref readback mismatch") + state["ops_branch"] = args.ops_branch + state["ops_commit"] = expected state["refs_verified"] = True record(args.journal, state) except Exception as error: @@ -213,6 +225,7 @@ def main(): parser.add_argument("--commit", required=True) parser.add_argument("--old-commit", required=True) parser.add_argument("--tag", required=True) + parser.add_argument("--ops-branch", help="Publish one new operations branch atomically with the release") parser.add_argument("--journal", type=Path) parser.add_argument("--apply", action="store_true") args = parser.parse_args()