Skip to content

Commit 94a41ab

Browse files
committed
Add release scripts
1 parent 9986c48 commit 94a41ab

4 files changed

Lines changed: 552 additions & 0 deletions

File tree

scripts/release.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env python3
2+
import re
3+
import subprocess
4+
import sys
5+
from pathlib import Path
6+
7+
import requests
8+
from packaging import version
9+
10+
11+
def get_current_version():
12+
with open("src/pycodify/__init__.py", "r") as f:
13+
for line in f:
14+
if line.startswith("__version__"):
15+
return line.split("=")[1].strip().strip('"\'')
16+
return None
17+
18+
def get_pypi_version():
19+
try:
20+
response = requests.get("https://pypi.org/pypi/pycodify/json")
21+
if response.status_code == 200:
22+
return response.json()["info"]["version"]
23+
except:
24+
pass
25+
return None
26+
27+
def main():
28+
# Get current version
29+
current_version = get_current_version()
30+
if not current_version:
31+
print("Error: Could not find version in __init__.py")
32+
sys.exit(1)
33+
34+
# Get PyPI version
35+
pypi_version = get_pypi_version()
36+
print(f"Current package version: {current_version}")
37+
print(f"Current PyPI version: {pypi_version}")
38+
39+
if pypi_version and version.parse(current_version) <= version.parse(pypi_version):
40+
print(f"Error: Current version ({current_version}) must be greater than PyPI version ({pypi_version})")
41+
sys.exit(1)
42+
43+
# Confirm with user
44+
response = input(f"Create release for v{current_version}? [y/N] ")
45+
if response.lower() != 'y':
46+
print("Aborted.")
47+
return
48+
49+
try:
50+
# Create and push tag
51+
subprocess.run(['git', 'tag', '-a', f'v{current_version}', '-m', f'Release version {current_version}'], check=True)
52+
subprocess.run(['git', 'push', 'origin', f'v{current_version}'], check=True)
53+
54+
print(f"\nSuccessfully created and pushed tag v{current_version}")
55+
print("GitHub Actions workflow should start automatically.")
56+
print("Monitor progress at: https://github.com/OpenHCSDev/pycodify/actions")
57+
58+
except subprocess.CalledProcessError as e:
59+
print(f"Error during release process: {e}")
60+
sys.exit(1)
61+
62+
if __name__ == "__main__":
63+
main()

scripts/update_and_release.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env python3
2+
import re
3+
import subprocess
4+
import sys
5+
from pathlib import Path
6+
7+
from packaging import version
8+
9+
10+
def run_command(command, check=True):
11+
"""Run a command and return its output"""
12+
try:
13+
result = subprocess.run(command, check=check, capture_output=True, text=True)
14+
return result.stdout.strip()
15+
except subprocess.CalledProcessError as e:
16+
print(f"Error running command {' '.join(command)}: {e}")
17+
print(f"Error output: {e.stderr}")
18+
sys.exit(1)
19+
20+
def get_modified_files():
21+
"""Get list of modified files"""
22+
# Get both staged and unstaged changes
23+
staged = run_command(['git', 'diff', '--staged', '--name-only']).split('\n')
24+
unstaged = run_command(['git', 'diff', '--name-only']).split('\n')
25+
# Combine and remove duplicates while preserving order
26+
all_files = []
27+
for f in staged + unstaged:
28+
if f and f not in all_files:
29+
all_files.append(f)
30+
return all_files
31+
32+
def check_unstaged_changes():
33+
"""Check for unstaged changes"""
34+
result = subprocess.run(['git', 'diff', '--quiet'], capture_output=True)
35+
return result.returncode != 0
36+
37+
def commit_changes(files, message):
38+
"""Commit specified files with a message"""
39+
try:
40+
# Add files explicitly first
41+
for file in files:
42+
print(f"Adding file: {file}")
43+
subprocess.run(['git', 'add', file], check=True)
44+
45+
# Show what's being committed
46+
print("\nFiles staged for commit:")
47+
subprocess.run(['git', 'status', '--short'], check=True)
48+
49+
# Commit
50+
print(f"\nCommitting with message: {message}")
51+
subprocess.run(['git', 'commit', '-m', message], check=True)
52+
53+
# Now pull with rebase (after changes are committed)
54+
print("\nPulling latest changes...")
55+
subprocess.run(['git', 'pull', '--rebase', 'origin', 'main'], check=True)
56+
57+
# Push
58+
print("\nPushing to origin main...")
59+
subprocess.run(['git', 'push', 'origin', 'main'], check=True)
60+
61+
print("Changes committed and pushed successfully!")
62+
63+
except subprocess.CalledProcessError as e:
64+
print(f"Git operation failed: {e}")
65+
print(f"Error output: {e.stderr if e.stderr else 'None'}")
66+
sys.exit(1)
67+
68+
def update_version():
69+
"""Run the version update script"""
70+
try:
71+
subprocess.run([sys.executable, 'scripts/update_version.py'], check=True)
72+
except subprocess.CalledProcessError as e:
73+
print(f"Error updating version: {e}")
74+
sys.exit(1)
75+
76+
def create_release():
77+
"""Run the release script"""
78+
try:
79+
subprocess.run([sys.executable, 'scripts/release.py'], check=True)
80+
except subprocess.CalledProcessError as e:
81+
print(f"Error creating release: {e}")
82+
sys.exit(1)
83+
84+
def main():
85+
# 1. Get modified files
86+
modified_files = [f for f in get_modified_files() if f]
87+
88+
# Handle existing changes if any
89+
if modified_files:
90+
print("\nModified files:")
91+
for f in modified_files:
92+
print(f"- {f}")
93+
94+
response = input("\nCommit these changes before release? [y/N] ")
95+
if response.lower() == 'y':
96+
commit_message = input("\nEnter commit message: ")
97+
if not commit_message:
98+
print("Commit message cannot be empty.")
99+
sys.exit(1)
100+
101+
print("\nCommitting changes...")
102+
commit_changes(modified_files, commit_message)
103+
104+
# Always proceed with version update and release
105+
print("\nUpdating version...")
106+
update_version()
107+
108+
print("\nCreating release...")
109+
create_release()
110+
111+
print("\nProcess completed successfully!")
112+
113+
if __name__ == "__main__":
114+
main()

scripts/update_version.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import re
4+
import subprocess
5+
import sys
6+
from pathlib import Path
7+
8+
from packaging import version
9+
10+
11+
def get_current_version():
12+
"""Get the current version from __init__.py"""
13+
init_file = Path("src/pycodify/__init__.py")
14+
content = init_file.read_text()
15+
match = re.search(r'__version__\s*=\s*["\']([^"\']+)["\']', content)
16+
return match.group(1) if match else None
17+
18+
def validate_version(new_version):
19+
"""Validate the new version string"""
20+
try:
21+
v = version.parse(new_version)
22+
if not isinstance(v, version.Version):
23+
raise ValueError("Invalid version format")
24+
return True
25+
except version.InvalidVersion:
26+
raise ValueError("Invalid version format. Use semantic versioning (e.g., 1.2.3)")
27+
28+
def check_for_uncommitted_changes():
29+
"""Check if there are any uncommitted changes in tracked files"""
30+
# Check for staged and unstaged changes in tracked files
31+
staged = subprocess.run(['git', 'diff', '--staged', '--quiet'])
32+
unstaged = subprocess.run(['git', 'diff', '--quiet'])
33+
return staged.returncode != 0 or unstaged.returncode != 0
34+
35+
def update_version(new_version=None):
36+
"""Update version in files and create git commit"""
37+
try:
38+
# Get current version first
39+
current_version = get_current_version()
40+
if not current_version:
41+
print("Error: Could not find current version in __init__.py")
42+
sys.exit(1)
43+
44+
if new_version is None:
45+
# Auto-increment patch version if no version specified
46+
v = version.parse(current_version)
47+
new_version = f"{v.major}.{v.minor}.{v.micro + 1}"
48+
49+
# Validate new version
50+
validate_version(new_version)
51+
if version.parse(new_version) <= version.parse(current_version):
52+
print(f"Error: New version ({new_version}) must be greater than current version ({current_version})")
53+
sys.exit(1)
54+
55+
# Check for uncommitted changes
56+
if check_for_uncommitted_changes():
57+
print("Error: You have uncommitted changes. Please commit or stash them first.")
58+
sys.exit(1)
59+
60+
# Pull latest changes
61+
try:
62+
subprocess.run(['git', 'pull', 'origin', 'main'], check=True)
63+
except subprocess.CalledProcessError as e:
64+
print(f"Error: Failed to pull latest changes: {e}")
65+
sys.exit(1)
66+
67+
# Update version in __init__.py
68+
init_file = Path("src/pycodify/__init__.py")
69+
content = init_file.read_text()
70+
new_content = re.sub(
71+
r'(__version__\s*=\s*[\'"])[^\'"]+([\'"])',
72+
rf'\g<1>{new_version}\2',
73+
content
74+
)
75+
init_file.write_text(new_content)
76+
77+
# Commit and push changes
78+
subprocess.run(['git', 'add', 'src/pycodify/__init__.py'], check=True)
79+
subprocess.run(['git', 'commit', '-m', f'bump version to {new_version}'], check=True)
80+
subprocess.run(['git', 'push', 'origin', 'main'], check=True)
81+
82+
print(f"Successfully updated version to {new_version}")
83+
print("Now run: python scripts/release.py")
84+
85+
except subprocess.CalledProcessError as e:
86+
print(f"Error during version update: {e}")
87+
sys.exit(1)
88+
except ValueError as e:
89+
print(f"Error: {e}")
90+
sys.exit(1)
91+
92+
def main():
93+
parser = argparse.ArgumentParser(description='Update pycodify version number')
94+
parser.add_argument('--version', '-v',
95+
help='New version number (e.g., 1.2.3). If not provided, will increment patch version.')
96+
args = parser.parse_args()
97+
98+
update_version(args.version)
99+
100+
if __name__ == "__main__":
101+
main()

0 commit comments

Comments
 (0)