Skip to content

chore(release): factor code, make testable, clarify workflow names#3882

Merged
rickeylev merged 3 commits into
bazel-contrib:mainfrom
rickeylev:refactor-release-tools-classes
Jul 2, 2026
Merged

chore(release): factor code, make testable, clarify workflow names#3882
rickeylev merged 3 commits into
bazel-contrib:mainfrom
rickeylev:refactor-release-tools-classes

Conversation

@rickeylev

Copy link
Copy Markdown
Collaborator

This refactors the release tool code to be more testable and broken
up into smaller files. Workflows are also renamed to better match
what they do and the command they run.

rickeylev added 3 commits July 2, 2026 07:11
Factored out cmd_* functions from release.py into separate classes with explicit dependency injection of Git and GitHub helpers. This improves testability and code organization. Simplified mocks in tests to use injection instead of module-level patching.
Renamed release-related GHA workflow files in .github/workflows to start with 'release_' and match the corresponding subcommands of the release tool. Also unified extension to .yaml.
Updated the name field in the GHA workflow files to follow the 'Release: <Command Name>' convention, matching the release tool subcommands.
@rickeylev rickeylev requested a review from aignas as a code owner July 2, 2026 07:28
@rickeylev rickeylev merged commit 839deaf into bazel-contrib:main Jul 2, 2026
5 of 6 checks passed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the release tools by migrating module-level functions in git.py and gh.py into object-oriented Git and GitHub helper classes, and reorganizing subcommands into dedicated class-based command modules. The review feedback provides valuable robustness improvements, such as safely handling potentially missing or null fields in GitHub API responses (like mergeCommit and PR body), wrapping string-to-integer conversions in try-except blocks to prevent crashes on malformed references, and replacing hardcoded "upstream" remote references with a configurable --remote argument in the promote-rc subcommand.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +42 to +43
commit_sha = pr_info["mergeCommit"]["oid"]
short_commit = commit_sha[:8]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Directly accessing pr_info["mergeCommit"]["oid"] can raise a KeyError or TypeError if mergeCommit is missing or None. It is safer to retrieve it using .get() and verify its presence before accessing the OID.

Suggested change
commit_sha = pr_info["mergeCommit"]["oid"]
short_commit = commit_sha[:8]
merge_commit = pr_info.get("mergeCommit")
if not merge_commit or "oid" not in merge_commit:
print(f"Error: PR #{args.pr} has no merge commit SHA.")
return 1
commit_sha = merge_commit["oid"]
short_commit = commit_sha[:8]

Comment on lines +337 to +339
pr_num = int(item.pr_ref.lstrip("#"))
print(f"Resolving PR #{pr_num} to merge commit...")
try:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If item.pr_ref is malformed or contains non-digit characters (e.g., due to manual edits in the tracking issue), int() will raise a ValueError outside of the try block, crashing the entire command. Moving the parsing inside a dedicated try-except block ensures that any parsing errors are gracefully caught and handled as resolution failures.

Suggested change
pr_num = int(item.pr_ref.lstrip("#"))
print(f"Resolving PR #{pr_num} to merge commit...")
try:
try:
pr_num = int(item.pr_ref.lstrip("#"))
except ValueError:
print(f"Error: Invalid PR reference '{item.pr_ref}'. Gating.")
item.status = "error-resolution-failed"
resolved_items.append(item)
continue
print(f"Resolving PR #{pr_num} to merge commit...")
try:

return 1

# Resolve issue number from PR body
pr_body = pr_info.get("body", "")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the PR has an empty description, GitHub CLI returns null for the body, which parses to None in Python. Calling re.search on None will raise a TypeError. Use pr_info.get("body") or "" to safely handle this case.

Suggested change
pr_body = pr_info.get("body", "")
pr_body = pr_info.get("body") or ""

"""Executes the promote-rc subcommand (Phase 3)."""
args = self.args
# Fetch from upstream to ensure we have the latest tags
self.git.fetch("upstream", tags=True, force=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Hardcoding the remote name as "upstream" can cause failures for users who have configured their main remote under a different name (e.g., "origin"). It is better to use args.remote to allow flexibility.

Suggested change
self.git.fetch("upstream", tags=True, force=True)
self.git.fetch(args.remote, tags=True, force=True)

if version is None:
version = determine_next_version()

latest_rc = get_latest_rc_tag(version, remote="upstream")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use args.remote instead of hardcoding "upstream" to match the configured remote.

Suggested change
latest_rc = get_latest_rc_tag(version, remote="upstream")
latest_rc = get_latest_rc_tag(version, remote=args.remote)


# Tag the specific commit without checkout, and push to upstream
self.git.tag(version, commit_sha)
self.git.push("upstream", version)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use args.remote instead of hardcoding "upstream" to push the promoted tag to the correct remote.

Suggested change
self.git.push("upstream", version)
self.git.push(args.remote, version)

Comment on lines +125 to +129
parser.add_argument(
"--issue",
type=int,
help="The tracking issue number (optional).",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add a --remote argument to the parser (defaulting to "upstream") to allow users to specify their remote name, matching other subcommands.

Suggested change
parser.add_argument(
"--issue",
type=int,
help="The tracking issue number (optional).",
)
parser.add_argument(
"--issue",
type=int,
help="The tracking issue number (optional).",
)
parser.add_argument(
"--remote",
type=str,
default="upstream",
help="The git remote to fetch from and push to (default: upstream).",
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant