Skip to content

Commit 986e48b

Browse files
committed
fix: run bump jobs sequentially
1 parent ac4d1b2 commit 986e48b

4 files changed

Lines changed: 207 additions & 12 deletions

File tree

.github/workflows/bump-package.yml

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,6 @@ jobs:
3535
- name: Install commitizen
3636
run: uv tool install commitizen
3737

38-
- name: Configure git
38+
- name: Bump version for ${{ inputs.package_name }}
3939
run: |
40-
git config --local user.email "action@github.com"
41-
git config --local user.name "GitHub Action"
42-
43-
- name: Create bump and changelog for ${{ inputs.package_name }}
44-
run: |
45-
cd ${{ inputs.package_dir }}
46-
echo "Bumping version for ${{ inputs.package_name }} package..."
47-
uv run cz bump --changelog --yes
48-
49-
# Push changes
50-
git push origin main --follow-tags
40+
uv run python scripts/bump_package.py "${{ inputs.package_name }}" "${{ inputs.package_dir }}"

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ jobs:
4848
strategy:
4949
matrix:
5050
package: ${{ fromJson(needs.detect-changes.outputs.changed-packages) }}
51+
max-parallel: 1 # Process packages sequentially to avoid race conditions
5152
uses: ./.github/workflows/bump-package.yml
5253
with:
5354
package_name: ${{ matrix.package.package_name }}

justfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ changelog-preview BASE_BRANCH="origin/main":
6262
preview-versions FORMAT="markdown":
6363
uv run python scripts/version_preview.py --format {{FORMAT}}
6464

65+
# Bump version for a specific package
66+
bump-package PACKAGE_NAME PACKAGE_DIR:
67+
uv run python scripts/bump_package.py {{PACKAGE_NAME}} {{PACKAGE_DIR}}
68+
6569
# Detect packages with unreleased changes
6670
detect-changes:
6771
uv run python scripts/changelog.py changes --output-format github

scripts/bump_package.py

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Bump package version script.
4+
5+
This script handles version bumping for a specific package using commitizen,
6+
including retry logic for pushing changes to avoid race conditions.
7+
"""
8+
9+
import argparse
10+
import subprocess
11+
import sys
12+
import time
13+
from pathlib import Path
14+
15+
16+
def run_command(cmd: list[str], cwd: str | None = None) -> tuple[int, str, str]:
17+
"""Run a command and return exit code, stdout, and stderr."""
18+
try:
19+
result = subprocess.run(
20+
cmd,
21+
capture_output=True,
22+
text=True,
23+
cwd=cwd,
24+
check=False
25+
)
26+
return result.returncode, result.stdout.strip(), result.stderr.strip()
27+
except Exception as e:
28+
return 1, "", str(e)
29+
30+
31+
def configure_git() -> None:
32+
"""Configure git for automated commits."""
33+
print("Configuring git...")
34+
run_command(["git", "config", "--local", "user.email", "action@github.com"])
35+
run_command(["git", "config", "--local", "user.name", "GitHub Action"])
36+
37+
38+
def pull_latest_changes() -> bool:
39+
"""Pull latest changes from origin/main."""
40+
print("Pulling latest changes from origin/main...")
41+
exit_code, stdout, stderr = run_command(["git", "pull", "origin", "main"])
42+
43+
if exit_code != 0:
44+
print(f"Failed to pull latest changes: {stderr}")
45+
return False
46+
47+
print("Successfully pulled latest changes")
48+
return True
49+
50+
51+
def run_bump(package_dir: str, package_name: str) -> bool:
52+
"""Run commitizen bump in the specified package directory."""
53+
print(f"Running version bump for {package_name} in {package_dir}...")
54+
55+
exit_code, stdout, stderr = run_command(
56+
["uv", "run", "cz", "bump", "--changelog", "--yes"],
57+
cwd=package_dir
58+
)
59+
60+
if exit_code != 0:
61+
print(f"Failed to bump version: {stderr}")
62+
return False
63+
64+
print("Version bump completed successfully")
65+
print(stdout)
66+
return True
67+
68+
69+
def push_changes_with_retry(max_attempts: int = 3) -> bool:
70+
"""Push changes to origin/main with retry logic."""
71+
# First, let's check what tags were created
72+
exit_code, stdout, stderr = run_command(["git", "tag", "--list", "--sort=-version:refname"])
73+
if exit_code == 0 and stdout:
74+
print(f"Local tags found: {stdout}")
75+
# Show the most recent tag
76+
recent_tags = stdout.split('\n')[:3]
77+
print(f"Most recent tags: {recent_tags}")
78+
79+
for attempt in range(1, max_attempts + 1):
80+
print(f"Attempting to push changes (attempt {attempt}/{max_attempts})...")
81+
82+
exit_code, stdout, stderr = run_command(
83+
["git", "push", "origin", "main", "--follow-tags"]
84+
)
85+
86+
if exit_code == 0:
87+
print(f"Successfully pushed changes on attempt {attempt}")
88+
89+
# Explicitly push tags to ensure they're uploaded
90+
print("Explicitly pushing tags...")
91+
tag_exit_code, tag_stdout, tag_stderr = run_command(
92+
["git", "push", "origin", "--tags"]
93+
)
94+
95+
if tag_exit_code == 0:
96+
print("Successfully pushed tags")
97+
else:
98+
print(f"Warning: Failed to push tags: {tag_stderr}")
99+
# Don't fail the whole process for tag push failure
100+
101+
return True
102+
103+
print(f"Push failed on attempt {attempt}: {stderr}")
104+
105+
if attempt < max_attempts:
106+
print("Pulling latest changes and retrying...")
107+
108+
# Pull with rebase to handle any new commits
109+
exit_code, stdout, stderr = run_command(
110+
["git", "pull", "origin", "main", "--rebase"]
111+
)
112+
113+
if exit_code != 0:
114+
print(f"Failed to pull with rebase: {stderr}")
115+
continue
116+
117+
print("Waiting 2 seconds before retry...")
118+
time.sleep(2)
119+
else:
120+
print(f"Failed to push after {max_attempts} attempts")
121+
return False
122+
123+
return False
124+
125+
126+
def bump_package(package_name: str, package_dir: str) -> bool:
127+
"""
128+
Bump version for a specific package.
129+
130+
Args:
131+
package_name: Name of the package (e.g., keycardai-oauth)
132+
package_dir: Directory path of the package (e.g., packages/oauth)
133+
134+
Returns:
135+
True if successful, False otherwise
136+
"""
137+
print(f"Starting version bump for {package_name} package...")
138+
139+
# Ensure package directory exists
140+
if not Path(package_dir).exists():
141+
print(f"Error: Package directory {package_dir} does not exist")
142+
return False
143+
144+
# Configure git for automated commits
145+
configure_git()
146+
147+
# Pull latest changes to avoid conflicts
148+
if not pull_latest_changes():
149+
return False
150+
151+
# Run the version bump
152+
if not run_bump(package_dir, package_name):
153+
return False
154+
155+
# Push changes with retry logic
156+
if not push_changes_with_retry():
157+
return False
158+
159+
print(f"Successfully completed version bump for {package_name}")
160+
return True
161+
162+
163+
def main():
164+
"""Main function with argument parsing."""
165+
parser = argparse.ArgumentParser(
166+
description="Bump package version using commitizen"
167+
)
168+
parser.add_argument(
169+
"package_name",
170+
help="Name of the package to bump (e.g., keycardai-oauth)"
171+
)
172+
parser.add_argument(
173+
"package_dir",
174+
help="Directory of the package (e.g., packages/oauth)"
175+
)
176+
parser.add_argument(
177+
"--max-retry-attempts",
178+
type=int,
179+
default=3,
180+
help="Maximum number of push retry attempts (default: 3)"
181+
)
182+
183+
args = parser.parse_args()
184+
185+
# Set max retry attempts globally
186+
global MAX_RETRY_ATTEMPTS
187+
MAX_RETRY_ATTEMPTS = args.max_retry_attempts
188+
189+
# Run the bump
190+
success = bump_package(args.package_name, args.package_dir)
191+
192+
if not success:
193+
print("Version bump failed")
194+
sys.exit(1)
195+
196+
print("Version bump completed successfully")
197+
198+
199+
if __name__ == "__main__":
200+
main()

0 commit comments

Comments
 (0)