create_release_branch.py
1 import argparse 2 import os 3 import subprocess 4 5 from packaging.version import Version 6 7 8 def main(new_version: str, remote: str, dry_run: bool = False) -> None: 9 version = Version(new_version) 10 release_branch = f"branch-{version.major}.{version.minor}" 11 exists_on_remote = ( 12 subprocess.check_output( 13 ["git", "ls-remote", "--heads", remote, release_branch], text=True 14 ).strip() 15 != "" 16 ) 17 if exists_on_remote: 18 print(f"{release_branch} already exists on {remote}, skipping branch creation") 19 return 20 21 prev_branch = subprocess.check_output(["git", "branch", "--show-current"], text=True).strip() 22 try: 23 exists_on_local = ( 24 subprocess.check_output(["git", "branch", "--list", release_branch], text=True).strip() 25 != "" 26 ) 27 if exists_on_local: 28 print(f"Deleting existing {release_branch}") 29 subprocess.check_call(["git", "branch", "-D", release_branch]) 30 31 print(f"Creating {release_branch}") 32 subprocess.check_call(["git", "checkout", "-b", release_branch, "master"]) 33 print(f"Pushing {release_branch} to {remote}") 34 subprocess.check_call([ 35 "git", 36 "push", 37 remote, 38 release_branch, 39 *(["--dry-run"] if dry_run else []), 40 ]) 41 finally: 42 subprocess.check_call(["git", "checkout", prev_branch]) 43 44 45 if __name__ == "__main__": 46 parser = argparse.ArgumentParser(description="Create a release branch") 47 parser.add_argument("--new-version", required=True, help="New version to release") 48 parser.add_argument("--remote", default="origin", help="Git remote to use (default: origin)") 49 parser.add_argument( 50 "--dry-run", 51 action="store_true", 52 default=os.environ.get("DRY_RUN", "true").lower() == "true", 53 help="Dry run mode (default: True, can be set via DRY_RUN env var)", 54 ) 55 parser.add_argument( 56 "--no-dry-run", 57 action="store_false", 58 dest="dry_run", 59 help="Disable dry run mode", 60 ) 61 args = parser.parse_args() 62 main(args.new_version, args.remote, args.dry_run)