From dc4fe02f4d8f447628154d0096f88d8af6aef1c6 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Tue, 30 Jun 2026 02:33:52 +0000 Subject: [PATCH 1/2] chore(release): use force push and clean up git helpers in prep tool This change fixes an edge case in the release preparation tool by using force push when pushing the preparation branch, resolving failures when the remote branch already exists. It also cleans up git helpers to use long option names and fail-fast on errors, with corresponding test updates. --- tests/tools/private/release/release_test.py | 10 +++++++--- tools/private/release/git.py | 13 ++++++------- tools/private/release/prepare.py | 3 ++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/tools/private/release/release_test.py b/tests/tools/private/release/release_test.py index a1467d8987..11362bc391 100644 --- a/tests/tools/private/release/release_test.py +++ b/tests/tools/private/release/release_test.py @@ -591,6 +591,10 @@ def setUp(self): self.mock_get_latest_version = patch( "tools.private.release.utils.get_latest_version" ).start() + self.mock_get_current_branch = patch( + "tools.private.release.git.get_current_branch" + ).start() + self.mock_get_current_branch.return_value = None self.addCleanup(patch.stopall) def test_no_markers(self): @@ -825,7 +829,7 @@ def test_prepare_use_associated_pr_from_tracking_issue( self.mock_git.checkout.assert_called_once_with("prepare-2.0.0") self.mock_git.commit.assert_not_called() self.mock_git.push.assert_called_once_with( - "origin", "prepare-2.0.0", set_upstream=True + "origin", "prepare-2.0.0", set_upstream=True, force=True ) self.mock_gh.get_open_pr.assert_called_once_with("prepare-2.0.0") self.mock_gh.create_pr.assert_not_called() # Should NOT create a new PR @@ -855,7 +859,7 @@ def test_prepare_create_pr_when_none_associated(self, mock_replace, mock_changel self.mock_git.checkout.assert_called_once_with("prepare-2.0.0") self.mock_git.commit.assert_not_called() self.mock_git.push.assert_called_once_with( - "origin", "prepare-2.0.0", set_upstream=True + "origin", "prepare-2.0.0", set_upstream=True, force=True ) self.mock_gh.get_open_pr.assert_called_once_with("prepare-2.0.0") self.mock_gh.create_pr.assert_called_once_with("2.0.0", 123) @@ -886,7 +890,7 @@ def test_prepare_reuse_existing_pr(self, mock_replace, mock_changelog): self.mock_git.checkout.assert_called_once_with("prepare-2.0.0") self.mock_git.commit.assert_not_called() self.mock_git.push.assert_called_once_with( - "origin", "prepare-2.0.0", set_upstream=True + "origin", "prepare-2.0.0", set_upstream=True, force=True ) self.mock_gh.get_open_pr.assert_called_once_with("prepare-2.0.0") self.mock_gh.create_pr.assert_not_called() diff --git a/tools/private/release/git.py b/tools/private/release/git.py index 9c2662d4e6..ed6e637d3f 100644 --- a/tools/private/release/git.py +++ b/tools/private/release/git.py @@ -41,11 +41,13 @@ def commit(message, amend=False, no_edit=False): run_cmd(*cmd, capture_output=False) -def push(remote, ref, set_upstream=False): +def push(remote, ref, set_upstream=False, force=False): """Pushes a reference to a remote repository.""" cmd = ["git", "push"] if set_upstream: - cmd.append("-u") + cmd.append("--set-upstream") + if force: + cmd.append("--force") cmd.extend([remote, ref]) run_cmd(*cmd, capture_output=False) @@ -128,8 +130,5 @@ def get_tags_at_head(): def get_current_branch(): - """Returns the current git branch name, or None if not in a git repo.""" - try: - return run_cmd("git", "rev-parse", "--abbrev-ref", "HEAD") - except subprocess.CalledProcessError: - return None + """Returns the current git branch name.""" + return run_cmd("git", "rev-parse", "--abbrev-ref", "HEAD") diff --git a/tools/private/release/prepare.py b/tools/private/release/prepare.py index c9012e0d04..2d32ee4de0 100644 --- a/tools/private/release/prepare.py +++ b/tools/private/release/prepare.py @@ -113,7 +113,8 @@ def cmd_prepare(args): print("No files modified by the release tool. Nothing to commit.") print(f"Pushing branch {branch_name} to origin...") - git.push("origin", branch_name, set_upstream=True) + # Force push to overwrite the remote branch if it already exists (e.g. from a previous run) + git.push("origin", branch_name, set_upstream=True, force=True) # --- Create PR --- # Determine if we need to create a PR or reuse an existing one From fc9d8e33b23ae461cfb84d147bf0d7538ea1da5e Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Tue, 30 Jun 2026 03:53:42 +0000 Subject: [PATCH 2/2] chore(release): update get_current_branch mock in tests Update the mock to return 'main' instead of None to be more realistic. --- tests/tools/private/release/release_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tools/private/release/release_test.py b/tests/tools/private/release/release_test.py index 11362bc391..293b82b277 100644 --- a/tests/tools/private/release/release_test.py +++ b/tests/tools/private/release/release_test.py @@ -594,7 +594,7 @@ def setUp(self): self.mock_get_current_branch = patch( "tools.private.release.git.get_current_branch" ).start() - self.mock_get_current_branch.return_value = None + self.mock_get_current_branch.return_value = "main" self.addCleanup(patch.stopall) def test_no_markers(self):