feat: add --promote-alpha option to create_github_release script (#6370)

Historically, running `create_github_release --publish-release` would
always publish a new release from latest `main`, which isn't always the
best idea. We should really publish an alpha, let it bake, and then
promote it.

This PR introduces a new flag, `--promote-alpha`, which does exactly
that. It also works with `--dry-run`, so you can sanity check the commit
it will use as the base commit for the new release before running it for
real.

```shell
$ ./codex-rs/scripts/create_github_release --dry-run --promote-alpha 0.56.0-alpha.2
Publishing version 0.56.0
Running gh api GET /repos/openai/codex/git/refs/tags/rust-v0.56.0-alpha.2
Running gh api GET /repos/openai/codex/git/tags/7d4ef77bc35b011aa0c76c5cbe6cd7d3e53f1dfe
Running gh api GET /repos/openai/codex/compare/main...8b49211e67d3c863df5ecc13fc5f88516a20fa69
Would publish version 0.56.0 using base commit 62474a30e8 derived from rust-v0.56.0-alpha.2.
```
This commit is contained in:
Michael Bolin
2025-11-07 12:05:22 -08:00
committed by GitHub
parent c6ab92bc50
commit bb47f2226f

View File

@@ -21,6 +21,11 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
action="store_true",
help="Print the version that would be used and exit before making changes.",
)
parser.add_argument(
"--promote-alpha",
metavar="VERSION",
help="Promote an existing alpha tag (e.g., 0.56.0-alpha.5) by using its merge-base with main as the base commit.",
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
@@ -43,26 +48,43 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
args.publish_alpha
or args.publish_release
or args.emergency_version_override
or args.promote_alpha
):
parser.error(
"Must specify --publish-alpha, --publish-release, or --emergency-version-override."
"Must specify --publish-alpha, --publish-release, --promote-alpha, or --emergency-version-override."
)
return args
def main(argv: list[str]) -> int:
args = parse_args(argv)
# Strip the leading "v" if present.
promote_alpha = args.promote_alpha
if promote_alpha and promote_alpha.startswith("v"):
promote_alpha = promote_alpha[1:]
try:
if args.emergency_version_override:
if promote_alpha:
version = derive_release_version_from_alpha(promote_alpha)
elif args.emergency_version_override:
version = args.emergency_version_override
else:
version = determine_version(args)
print(f"Publishing version {version}")
if args.dry_run:
if promote_alpha:
base_commit = get_promote_alpha_base_commit(promote_alpha)
if args.dry_run:
print(
f"Would publish version {version} using base commit {base_commit} derived from rust-v{promote_alpha}."
)
return 0
elif args.dry_run:
return 0
print("Fetching branch head...")
base_commit = get_branch_head()
if not promote_alpha:
print("Fetching branch head...")
base_commit = get_branch_head()
print(f"Base commit: {base_commit}")
print("Fetching commit tree...")
base_tree = get_commit_tree(base_commit)
@@ -130,6 +152,39 @@ def get_branch_head() -> str:
raise ReleaseError("Unable to determine branch head.") from error
def get_promote_alpha_base_commit(alpha_version: str) -> str:
tag_name = f"rust-v{alpha_version}"
tag_commit_sha = get_tag_commit_sha(tag_name)
return get_merge_base_with_main(tag_commit_sha)
def get_tag_commit_sha(tag_name: str) -> str:
response = run_gh_api(f"/repos/{REPO}/git/refs/tags/{tag_name}")
try:
sha = response["object"]["sha"]
obj_type = response["object"]["type"]
except KeyError as error:
raise ReleaseError(f"Unable to resolve tag {tag_name}.") from error
while obj_type == "tag":
tag_response = run_gh_api(f"/repos/{REPO}/git/tags/{sha}")
try:
sha = tag_response["object"]["sha"]
obj_type = tag_response["object"]["type"]
except KeyError as error:
raise ReleaseError(f"Unable to resolve annotated tag {tag_name}.") from error
if obj_type != "commit":
raise ReleaseError(f"Tag {tag_name} does not reference a commit.")
return sha
def get_merge_base_with_main(commit_sha: str) -> str:
response = run_gh_api(f"/repos/{REPO}/compare/main...{commit_sha}")
try:
return response["merge_base_commit"]["sha"]
except KeyError as error:
raise ReleaseError("Unable to determine merge base with main.") from error
def get_commit_tree(commit_sha: str) -> str:
response = run_gh_api(f"/repos/{REPO}/git/commits/{commit_sha}")
try:
@@ -309,5 +364,12 @@ def format_version(major: int, minor: int, patch: int) -> str:
return f"{major}.{minor}.{patch}"
def derive_release_version_from_alpha(alpha_version: str) -> str:
match = re.match(r"^(\d+)\.(\d+)\.(\d+)-alpha\.(\d+)$", alpha_version)
if match is None:
raise ReleaseError(f"Unexpected alpha version format: {alpha_version}")
return f"{match.group(1)}.{match.group(2)}.{match.group(3)}"
if __name__ == "__main__":
sys.exit(main(sys.argv))