Skip to content

Commit bd65f81

Browse files
authored
chore: rewrite codex-rs/scripts/create_github_release.sh in Python (#3226)
Migrating to Python to make this script easier to iterate on. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/3226). * #3231 * #3230 * #3228 * __->__ #3226
1 parent ba9620a commit bd65f81

File tree

2 files changed

+130
-64
lines changed

2 files changed

+130
-64
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import os
5+
import re
6+
import subprocess
7+
import sys
8+
from pathlib import Path
9+
10+
11+
ROOT_DIR = Path(__file__).resolve().parent.parent
12+
CARGO_TOML = ROOT_DIR / "Cargo.toml"
13+
14+
15+
def parse_args(argv: list[str]) -> argparse.Namespace:
16+
parser = argparse.ArgumentParser(description="Create a tagged Codex release.")
17+
parser.add_argument(
18+
"version",
19+
help="Version string used for Cargo.toml and the Git tag (e.g. 0.1.0-alpha.4).",
20+
)
21+
return parser.parse_args(argv[1:])
22+
23+
24+
def main(argv: list[str]) -> int:
25+
os.chdir(ROOT_DIR)
26+
args = parse_args(argv)
27+
try:
28+
ensure_clean_worktree()
29+
branch = current_branch()
30+
ensure_on_main(branch)
31+
ensure_on_origin_main()
32+
create_release(args.version, branch)
33+
except ReleaseError as error:
34+
print(f"ERROR: {error}", file=sys.stderr)
35+
return 1
36+
return 0
37+
38+
39+
def ensure_clean_worktree() -> None:
40+
commands = [
41+
["diff", "--quiet"],
42+
["diff", "--cached", "--quiet"],
43+
]
44+
for command in commands:
45+
result = run_git(command, check=False)
46+
if result.returncode != 0:
47+
raise ReleaseError("You have uncommitted changes.")
48+
49+
untracked = run_git(["ls-files", "--others", "--exclude-standard"], capture_output=True)
50+
if untracked.stdout.strip():
51+
raise ReleaseError("You have untracked files.")
52+
53+
54+
def ensure_on_main(branch: str) -> None:
55+
if branch != "main":
56+
raise ReleaseError(
57+
f"Releases must be created from the 'main' branch (current: '{branch}')."
58+
)
59+
60+
61+
def ensure_on_origin_main() -> None:
62+
try:
63+
run_git(["fetch", "--quiet", "origin", "main"])
64+
except ReleaseError as error:
65+
raise ReleaseError(
66+
"Failed to fetch 'origin/main'. Ensure the 'origin' remote is configured and reachable."
67+
) from error
68+
69+
result = run_git(["merge-base", "--is-ancestor", "HEAD", "origin/main"], check=False)
70+
if result.returncode != 0:
71+
raise ReleaseError(
72+
"Your local 'main' HEAD commit is not present on 'origin/main'. "
73+
"Please push first (git push origin main) or check out a commit on 'origin/main'."
74+
)
75+
76+
77+
def current_branch() -> str:
78+
result = run_git(["symbolic-ref", "--short", "-q", "HEAD"], capture_output=True, check=False)
79+
branch = result.stdout.strip()
80+
if result.returncode != 0 or not branch:
81+
raise ReleaseError("Could not determine the current branch (detached HEAD?).")
82+
return branch
83+
84+
85+
def update_version(version: str) -> None:
86+
content = CARGO_TOML.read_text(encoding="utf-8")
87+
new_content, matches = re.subn(
88+
r'^version = "[^"]+"', f'version = "{version}"', content, count=1, flags=re.MULTILINE
89+
)
90+
if matches != 1:
91+
raise ReleaseError("Unable to update version in Cargo.toml.")
92+
CARGO_TOML.write_text(new_content, encoding="utf-8")
93+
94+
95+
def create_release(version: str, branch: str) -> None:
96+
tag = f"rust-v{version}"
97+
run_git(["checkout", "-b", tag])
98+
try:
99+
update_version(version)
100+
run_git(["add", "Cargo.toml"])
101+
run_git(["commit", "-m", f"Release {version}"])
102+
run_git(["tag", "-a", tag, "-m", f"Release {version}"])
103+
run_git(["push", "origin", f"refs/tags/{tag}"])
104+
finally:
105+
run_git(["checkout", branch])
106+
107+
108+
class ReleaseError(RuntimeError):
109+
pass
110+
111+
112+
def run_git(
113+
args: list[str], *, capture_output: bool = False, check: bool = True
114+
) -> subprocess.CompletedProcess:
115+
result = subprocess.run(
116+
["git", *args],
117+
cwd=ROOT_DIR,
118+
text=True,
119+
capture_output=capture_output,
120+
)
121+
if check and result.returncode != 0:
122+
stderr = result.stderr.strip() if result.stderr else ""
123+
stdout = result.stdout.strip() if result.stdout else ""
124+
message = stderr if stderr else stdout
125+
raise ReleaseError(message or f"git {' '.join(args)} failed")
126+
return result
127+
128+
129+
if __name__ == "__main__":
130+
sys.exit(main(sys.argv))

codex-rs/scripts/create_github_release.sh

Lines changed: 0 additions & 64 deletions
This file was deleted.

0 commit comments

Comments
 (0)