Skip to content

Commit 921587b

Browse files
committed
Handling trigger branch detached head
1 parent 6e08459 commit 921587b

File tree

3 files changed

+116
-6
lines changed

3 files changed

+116
-6
lines changed

Tools/scripts/Utils/createPrAfterRelease.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from ReleaseAutomation.release_config import ReleaseConfig
1919
from Utils.general_utils import get_package_version_from_manifest, update_changelog, update_package_version_by_patch, update_validation_exceptions
20-
from Utils.git_utils import get_local_repo
20+
from Utils.git_utils import get_local_repo, get_trigger_branch
2121

2222
def createPrAfterRelease(config: ReleaseConfig):
2323
"""
@@ -35,7 +35,8 @@ def createPrAfterRelease(config: ReleaseConfig):
3535

3636
try:
3737
repo = get_local_repo()
38-
trigger_branch = repo.active_branch.name
38+
trigger_branch = get_trigger_branch(repo, config.default_repo_branch)
39+
print(f"\nTrigger branch: {trigger_branch}")
3940

4041
if not config.github_manager.is_branch_present(trigger_branch):
4142
raise Exception(f"Trigger branch '{trigger_branch}' does not exist. Exiting.")
@@ -51,14 +52,31 @@ def createPrAfterRelease(config: ReleaseConfig):
5152

5253
repo.git.fetch('--prune', '--prune-tags')
5354

55+
# If we're in detached HEAD state, checkout the trigger branch first
56+
try:
57+
repo.active_branch.name
58+
except (TypeError, ValueError):
59+
# HEAD is detached, checkout the trigger branch
60+
print(f"HEAD is detached, checking out trigger branch '{trigger_branch}'...")
61+
repo.git.checkout(trigger_branch)
62+
5463
# Ensure we're on the trigger branch and have latest changes
5564
# Stash any uncommitted changes that might block operations
5665
has_uncommitted_changes = repo.is_dirty()
5766
if has_uncommitted_changes:
5867
print("Uncommitted changes detected. Stashing before operations...")
5968
repo.git.stash('push', '-m', 'Auto-stash before release PR creation')
6069

61-
repo.git.checkout(trigger_branch)
70+
# Make sure we're on the trigger branch (in case we were on a different branch)
71+
current_branch = None
72+
try:
73+
current_branch = repo.active_branch.name
74+
except (TypeError, ValueError):
75+
pass
76+
77+
if current_branch != trigger_branch:
78+
repo.git.checkout(trigger_branch)
79+
6280
repo.git.pull("origin", trigger_branch)
6381

6482
# Create a new branch for the release changes PR to trigger branch

Tools/scripts/Utils/git_utils.py

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,80 @@ def get_latest_git_revision(branch_name):
4141
except subprocess.CalledProcessError as e:
4242
raise Exception(f"Failed to get the latest revision for branch '{branch_name}'.") from e
4343

44+
45+
def get_trigger_branch(repo, default_branch):
46+
"""
47+
Gets the trigger branch name, handling detached HEAD state in CI environments.
48+
49+
In CI environments, the repository might be checked out at a specific commit (detached HEAD).
50+
This function tries multiple methods to determine the branch:
51+
1. Check if HEAD is attached to a branch
52+
2. Check environment variables (YAMATO_BRANCH, CI_COMMIT_REF_NAME, etc.)
53+
3. Use git commands to find which remote branch contains the current commit
54+
4. Fall back to the default branch if nothing else works
55+
56+
Args:
57+
repo: GitPython Repo object
58+
default_branch: Default branch name to fall back to
59+
60+
Returns:
61+
str: The branch name
62+
"""
63+
try:
64+
# Try to get the active branch name (works when HEAD is attached)
65+
return repo.active_branch.name
66+
except (TypeError, ValueError):
67+
# HEAD is detached, try other methods
68+
pass
69+
70+
# Method 1: Check environment variables
71+
# Yamato might set branch info in environment variables
72+
trigger_branch = os.environ.get('YAMATO_BRANCH') or \
73+
os.environ.get('CI_COMMIT_REF_NAME') or \
74+
os.environ.get('GITHUB_REF_NAME') or \
75+
os.environ.get('BRANCH_NAME')
76+
77+
if trigger_branch:
78+
# Remove 'refs/heads/' prefix if present
79+
trigger_branch = trigger_branch.replace('refs/heads/', '')
80+
print(f"Found trigger branch from environment variable: {trigger_branch}")
81+
return trigger_branch
82+
83+
# Method 2: Try to find which remote branch contains the current commit
84+
try:
85+
current_commit = repo.head.commit.hexsha
86+
# Fetch all remote branches
87+
repo.git.fetch('origin', '--prune', '--prune-tags')
88+
89+
# Try to find which remote branch points to this commit
90+
result = subprocess.run(
91+
['git', 'branch', '-r', '--contains', current_commit],
92+
capture_output=True,
93+
text=True,
94+
check=True
95+
)
96+
97+
branches = [b.strip() for b in result.stdout.strip().split('\n') if b.strip()]
98+
# Filter to find the most likely branch (prefer default branch, then develop, then others)
99+
for branch_line in branches:
100+
branch = branch_line.replace('origin/', '').strip()
101+
if branch and branch == default_branch:
102+
print(f"Found trigger branch from remote branches: {branch}")
103+
return branch
104+
105+
# If default branch not found, use the first one
106+
if branches:
107+
branch = branches[0].replace('origin/', '').strip()
108+
if branch:
109+
print(f"Found trigger branch from remote branches: {branch}")
110+
return branch
111+
except Exception as e:
112+
print(f"Warning: Could not determine branch from remote branches: {e}")
113+
114+
# Method 3: Fall back to default branch
115+
print(f"Warning: Could not determine trigger branch, falling back to default branch: {default_branch}")
116+
return default_branch
117+
44118
def create_release_branch(config: ReleaseConfig):
45119
"""
46120
Creates a new branch with the specified name, performs specified action, commits the current changes and pushes it to the repo.
@@ -56,7 +130,16 @@ def create_release_branch(config: ReleaseConfig):
56130
raise Exception(f"Branch '{config.release_branch_name}' already exists.")
57131

58132
repo = get_local_repo()
59-
trigger_branch = repo.active_branch.name
133+
trigger_branch = get_trigger_branch(repo, config.default_repo_branch)
134+
print(f"\nTrigger branch: {trigger_branch}")
135+
136+
# If we're in detached HEAD state, checkout the trigger branch first
137+
try:
138+
repo.active_branch.name
139+
except (TypeError, ValueError):
140+
# HEAD is detached, checkout the trigger branch
141+
print(f"HEAD is detached, checking out trigger branch '{trigger_branch}'...")
142+
repo.git.checkout(trigger_branch)
60143

61144
# Stash any uncommitted changes to allow pull
62145
has_uncommitted_changes = repo.is_dirty()

Tools/scripts/Utils/verifyReleaseConditions.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import datetime
2525
import re
2626
from release_config import ReleaseConfig
27-
from Utils.git_utils import get_local_repo
27+
from Utils.git_utils import get_local_repo, get_trigger_branch
2828

2929
def get_yamato_trigger_type():
3030
"""
@@ -102,8 +102,17 @@ def verifyReleaseConditions(config: ReleaseConfig):
102102
# Pull latest changes from the trigger branch to ensure we're checking the latest state
103103
# The release branch will be created from this trigger branch, and the PR will target this trigger branch
104104
repo = get_local_repo()
105-
trigger_branch = repo.active_branch.name
105+
trigger_branch = get_trigger_branch(repo, config.default_repo_branch)
106106
print(f"\nTrigger branch: {trigger_branch}")
107+
108+
# If we're in detached HEAD state, checkout the trigger branch first
109+
try:
110+
repo.active_branch.name
111+
except (TypeError, ValueError):
112+
# HEAD is detached, checkout the trigger branch
113+
print(f"HEAD is detached, checking out trigger branch '{trigger_branch}'...")
114+
repo.git.checkout(trigger_branch)
115+
107116
print(f"Pulling latest changes from '{trigger_branch}' to verify changelog state...")
108117

109118
# Stash any uncommitted changes to allow pull

0 commit comments

Comments
 (0)