fix: change create_github_release to take either --publish-alpha or --publish-release (#3231)
No more picking out version numbers by hand! Now we let the script do it: ``` $ ./codex-rs/scripts/create_github_release --dry-run --publish-alpha Running gh api GET /repos/openai/codex/releases/latest Running gh api GET /repos/openai/codex/releases?per_page=100 Publishing version 0.31.0-alpha.3 $ ./codex-rs/scripts/create_github_release --dry-run --publish-release Running gh api GET /repos/openai/codex/releases/latest Publishing version 0.31.0 ``` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/3230). * __->__ #3231 * #3230 * #3228 * #3226
This commit is contained in:
@@ -14,10 +14,24 @@ CARGO_TOML_PATH = "codex-rs/Cargo.toml"
|
|||||||
|
|
||||||
|
|
||||||
def parse_args(argv: list[str]) -> argparse.Namespace:
|
def parse_args(argv: list[str]) -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser(description="Create a tagged Codex release.")
|
parser = argparse.ArgumentParser(description="Publish a tagged Codex release.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"version",
|
"-n",
|
||||||
help="Version string used for Cargo.toml and the Git tag (e.g. 0.1.0-alpha.4).",
|
"--dry-run",
|
||||||
|
action="store_true",
|
||||||
|
help="Print the version that would be used and exit before making changes.",
|
||||||
|
)
|
||||||
|
|
||||||
|
group = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
group.add_argument(
|
||||||
|
"--publish-alpha",
|
||||||
|
action="store_true",
|
||||||
|
help="Publish the next alpha release for the upcoming minor version.",
|
||||||
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--publish-release",
|
||||||
|
action="store_true",
|
||||||
|
help="Publish the next stable release by bumping the minor version.",
|
||||||
)
|
)
|
||||||
return parser.parse_args(argv[1:])
|
return parser.parse_args(argv[1:])
|
||||||
|
|
||||||
@@ -25,6 +39,11 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
|
|||||||
def main(argv: list[str]) -> int:
|
def main(argv: list[str]) -> int:
|
||||||
args = parse_args(argv)
|
args = parse_args(argv)
|
||||||
try:
|
try:
|
||||||
|
version = determine_version(args)
|
||||||
|
print(f"Publishing version {version}")
|
||||||
|
if args.dry_run:
|
||||||
|
return 0
|
||||||
|
|
||||||
print("Fetching branch head...")
|
print("Fetching branch head...")
|
||||||
base_commit = get_branch_head()
|
base_commit = get_branch_head()
|
||||||
print(f"Base commit: {base_commit}")
|
print(f"Base commit: {base_commit}")
|
||||||
@@ -34,7 +53,7 @@ def main(argv: list[str]) -> int:
|
|||||||
print("Fetching Cargo.toml...")
|
print("Fetching Cargo.toml...")
|
||||||
current_contents = fetch_file_contents(base_commit)
|
current_contents = fetch_file_contents(base_commit)
|
||||||
print("Updating version...")
|
print("Updating version...")
|
||||||
updated_contents = replace_version(current_contents, args.version)
|
updated_contents = replace_version(current_contents, version)
|
||||||
print("Creating blob...")
|
print("Creating blob...")
|
||||||
blob_sha = create_blob(updated_contents)
|
blob_sha = create_blob(updated_contents)
|
||||||
print(f"Blob SHA: {blob_sha}")
|
print(f"Blob SHA: {blob_sha}")
|
||||||
@@ -42,13 +61,13 @@ def main(argv: list[str]) -> int:
|
|||||||
tree_sha = create_tree(base_tree, blob_sha)
|
tree_sha = create_tree(base_tree, blob_sha)
|
||||||
print(f"Tree SHA: {tree_sha}")
|
print(f"Tree SHA: {tree_sha}")
|
||||||
print("Creating commit...")
|
print("Creating commit...")
|
||||||
commit_sha = create_commit(args.version, tree_sha, base_commit)
|
commit_sha = create_commit(version, tree_sha, base_commit)
|
||||||
print(f"Commit SHA: {commit_sha}")
|
print(f"Commit SHA: {commit_sha}")
|
||||||
print("Creating tag...")
|
print("Creating tag...")
|
||||||
tag_sha = create_tag(args.version, commit_sha)
|
tag_sha = create_tag(version, commit_sha)
|
||||||
print(f"Tag SHA: {tag_sha}")
|
print(f"Tag SHA: {tag_sha}")
|
||||||
print("Creating tag ref...")
|
print("Creating tag ref...")
|
||||||
create_tag_ref(args.version, tag_sha)
|
create_tag_ref(version, tag_sha)
|
||||||
print("Done.")
|
print("Done.")
|
||||||
except ReleaseError as error:
|
except ReleaseError as error:
|
||||||
print(f"ERROR: {error}", file=sys.stderr)
|
print(f"ERROR: {error}", file=sys.stderr)
|
||||||
@@ -59,6 +78,7 @@ def main(argv: list[str]) -> int:
|
|||||||
class ReleaseError(RuntimeError):
|
class ReleaseError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def run_gh_api(endpoint: str, *, method: str = "GET", payload: dict | None = None) -> dict:
|
def run_gh_api(endpoint: str, *, method: str = "GET", payload: dict | None = None) -> dict:
|
||||||
print(f"Running gh api {method} {endpoint}")
|
print(f"Running gh api {method} {endpoint}")
|
||||||
command = [
|
command = [
|
||||||
@@ -204,5 +224,73 @@ def create_tag_ref(version: str, tag_sha: str) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def determine_version(args: argparse.Namespace) -> str:
|
||||||
|
latest_version = get_latest_release_version()
|
||||||
|
major, minor, patch = parse_semver(latest_version)
|
||||||
|
next_minor_version = format_version(major, minor + 1, patch)
|
||||||
|
|
||||||
|
if args.publish_release:
|
||||||
|
return next_minor_version
|
||||||
|
|
||||||
|
alpha_prefix = f"{next_minor_version}-alpha."
|
||||||
|
releases = list_releases()
|
||||||
|
highest_alpha = 0
|
||||||
|
found_alpha = False
|
||||||
|
for release in releases:
|
||||||
|
tag = release.get("tag_name", "")
|
||||||
|
candidate = strip_tag_prefix(tag)
|
||||||
|
if candidate and candidate.startswith(alpha_prefix):
|
||||||
|
suffix = candidate[len(alpha_prefix) :]
|
||||||
|
try:
|
||||||
|
alpha_number = int(suffix)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
highest_alpha = max(highest_alpha, alpha_number)
|
||||||
|
found_alpha = True
|
||||||
|
|
||||||
|
if found_alpha:
|
||||||
|
return f"{alpha_prefix}{highest_alpha + 1}"
|
||||||
|
return f"{alpha_prefix}1"
|
||||||
|
|
||||||
|
|
||||||
|
def get_latest_release_version() -> str:
|
||||||
|
response = run_gh_api(f"/repos/{REPO}/releases/latest")
|
||||||
|
tag = response.get("tag_name")
|
||||||
|
version = strip_tag_prefix(tag)
|
||||||
|
if not version:
|
||||||
|
raise ReleaseError("Latest release tag has unexpected format.")
|
||||||
|
return version
|
||||||
|
|
||||||
|
|
||||||
|
def list_releases() -> list[dict]:
|
||||||
|
response = run_gh_api(f"/repos/{REPO}/releases?per_page=100")
|
||||||
|
if not isinstance(response, list):
|
||||||
|
raise ReleaseError("Unexpected response when listing releases.")
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def strip_tag_prefix(tag: str | None) -> str | None:
|
||||||
|
if not tag:
|
||||||
|
return None
|
||||||
|
prefix = "rust-v"
|
||||||
|
if not tag.startswith(prefix):
|
||||||
|
return None
|
||||||
|
return tag[len(prefix) :]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_semver(version: str) -> tuple[int, int, int]:
|
||||||
|
parts = version.split(".")
|
||||||
|
if len(parts) != 3:
|
||||||
|
raise ReleaseError(f"Unexpected version format: {version}")
|
||||||
|
try:
|
||||||
|
return int(parts[0]), int(parts[1]), int(parts[2])
|
||||||
|
except ValueError as error:
|
||||||
|
raise ReleaseError(f"Version components must be integers: {version}") from error
|
||||||
|
|
||||||
|
|
||||||
|
def format_version(major: int, minor: int, patch: int) -> str:
|
||||||
|
return f"{major}.{minor}.{patch}"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(main(sys.argv))
|
sys.exit(main(sys.argv))
|
||||||
|
|||||||
@@ -8,16 +8,23 @@ Currently, we made Codex binaries available in three places:
|
|||||||
|
|
||||||
# Cutting a Release
|
# Cutting a Release
|
||||||
|
|
||||||
Currently, choosing the version number for the next release is a manual process. In general, just go to https://github.com/openai/codex/releases/latest and see what the latest release is and increase the minor version by `1`, so if the current release is `0.20.0`, then the next release should be `0.21.0`.
|
Run the `codex-rs/scripts/create_github_release` script in the repository to publish a new release. The script will choose the appropriate version number depending on the type of release you are creating.
|
||||||
|
|
||||||
Assuming you are trying to publish `0.21.0`, first you would run:
|
To cut a new alpha release from `main` (feel free to cut alphas liberally):
|
||||||
|
|
||||||
```shell
|
```
|
||||||
VERSION=0.21.0
|
./codex-rs/scripts/create_github_release --publish-alpha
|
||||||
./codex-rs/scripts/create_github_release.sh "$VERSION"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This will kick off a GitHub Action to build the release, so go to https://github.com/openai/codex/actions/workflows/rust-release.yml to find the corresponding workflow. (Note: we should automate finding the workflow URL with `gh`.)
|
To cut a new _public_ release from `main` (which requires more caution), run:
|
||||||
|
|
||||||
|
```
|
||||||
|
./codex-rs/scripts/create_github_release --publish-release
|
||||||
|
```
|
||||||
|
|
||||||
|
TIP: Add the `--dry-run` flag to report the next version number for the respective release and exit.
|
||||||
|
|
||||||
|
Running the publishing script will kick off a GitHub Action to build the release, so go to https://github.com/openai/codex/actions/workflows/rust-release.yml to find the corresponding workflow. (Note: we should automate finding the workflow URL with `gh`.)
|
||||||
|
|
||||||
When the workflow finishes, the GitHub Release is "done," but you still have to consider npm and Homebrew.
|
When the workflow finishes, the GitHub Release is "done," but you still have to consider npm and Homebrew.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user