diff --git a/release_simplified.py b/release_simplified.py deleted file mode 100644 index 1cb4bcf3..00000000 --- a/release_simplified.py +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/env python3 -""" -MarkiTect Release Management Tool (setuptools-scm version) - -This simplified script works with setuptools-scm for automatic version management. -Versions are automatically derived from git tags - no manual version bumping needed. - -Usage: - python release_simplified.py [command] [options] - -Commands: - status Show current release status - validate Validate current state for release - tag Create git tag for version (e.g., v0.8.0) - build Build release packages - publish Complete release workflow (tag + build + distribute) - -Options: - --version VERSION Git tag version (e.g., 0.8.0, 1.0.0-rc1) - --dry-run Show what would be done without making changes - --force Force operation even with warnings -""" - -import subprocess -import argparse -import sys -from pathlib import Path -from datetime import datetime -from typing import Dict, List, Optional, Tuple - - -class SimpleReleaseManager: - """Simplified release manager using setuptools-scm.""" - - def __init__(self, dry_run=False, force=False): - self.dry_run = dry_run - self.force = force - self.project_root = Path(__file__).parent.absolute() - - def run_command(self, cmd: List[str], capture=True, check=True, skip_dry_run=False) -> subprocess.CompletedProcess: - """Run a command with optional dry-run support.""" - if self.dry_run and not skip_dry_run: - print(f"[DRY RUN] Would run: {' '.join(cmd)}") - return subprocess.CompletedProcess(cmd, 0, "", "") - return subprocess.run(cmd, capture_output=capture, text=True, check=check, cwd=self.project_root) - - def get_current_version_from_scm(self) -> str: - """Get current version using setuptools-scm.""" - try: - result = self.run_command(['python', '-m', 'setuptools_scm'], skip_dry_run=True) - return result.stdout.strip() - except subprocess.CalledProcessError: - return "unknown" - - def get_git_status(self) -> Dict[str, any]: - """Get current git repository status.""" - try: - # Get current branch - branch_result = self.run_command(['git', 'branch', '--show-current'], skip_dry_run=True) - current_branch = branch_result.stdout.strip() - - # Check for uncommitted changes - status_result = self.run_command(['git', 'status', '--porcelain'], skip_dry_run=True) - has_changes = bool(status_result.stdout.strip()) - - # Get latest commit - commit_result = self.run_command(['git', 'rev-parse', '--short', 'HEAD'], skip_dry_run=True) - latest_commit = commit_result.stdout.strip() - - # Get latest tag - try: - tag_result = self.run_command(['git', 'describe', '--tags', '--abbrev=0'], skip_dry_run=True) - latest_tag = tag_result.stdout.strip() - except subprocess.CalledProcessError: - latest_tag = None - - return { - 'is_repo': True, - 'branch': current_branch, - 'has_changes': has_changes, - 'latest_commit': latest_commit, - 'latest_tag': latest_tag - } - except subprocess.CalledProcessError: - return {'is_repo': False} - - def validate_release_state(self) -> Tuple[bool, List[str]]: - """Validate that the repository is ready for release.""" - issues = [] - git_status = self.get_git_status() - - if not git_status['is_repo']: - issues.append("Not in a git repository") - else: - if git_status['has_changes'] and not self.force: - issues.append("Repository has uncommitted changes") - if git_status['branch'] != 'main' and not self.force: - issues.append(f"Not on main branch (currently on {git_status['branch']})") - - return len(issues) == 0, issues - - def create_git_tag(self, version: str, message: str = None): - """Create and push git tag.""" - if not version.startswith('v'): - tag_name = f"v{version}" - else: - tag_name = version - - tag_message = message or f"Release {version}" - print(f"🏷️ Creating git tag {tag_name}") - - # Create annotated tag - self.run_command(['git', 'tag', '-a', tag_name, '-m', tag_message]) - print(f"✅ Tag {tag_name} created") - - # Optionally push tag (can be done manually) - try: - print(f"📤 Pushing tag to origin...") - self.run_command(['git', 'push', 'origin', tag_name]) - print(f"✅ Tag pushed to origin") - except subprocess.CalledProcessError as e: - print(f"⚠️ Could not push tag to origin: {e}") - print("You can push it manually with: git push origin " + tag_name) - - def build_packages(self): - """Build release packages using setuptools-scm.""" - print(f"📦 Building packages (version will be auto-determined by setuptools-scm)") - - # Clean previous builds - for pattern in ['build', 'dist', '*.egg-info']: - try: - self.run_command(['rm', '-rf', pattern]) - except subprocess.CalledProcessError: - pass - - # Build source distribution and wheel - print("Building packages...") - self.run_command(['python', '-m', 'build'], capture=False) - print("✅ Packages built successfully") - - def show_status(self): - """Show current release status.""" - print("🔍 MarkiTect Release Status (setuptools-scm)") - print("=" * 60) - - # Get version from setuptools-scm - scm_version = self.get_current_version_from_scm() - print(f"Current Version (setuptools-scm): {scm_version}") - - git_status = self.get_git_status() - if git_status['is_repo']: - print(f"Git Branch: {git_status['branch']}") - print(f"Latest Commit: {git_status['latest_commit']}") - print(f"Latest Tag: {git_status['latest_tag'] or 'None'}") - print(f"Uncommitted Changes: {'Yes' if git_status['has_changes'] else 'No'}") - else: - print("Git Repository: Not available") - - # Check build tools - print("\nBuild Tools:") - try: - self.run_command(['python', '-m', 'build', '--help'], skip_dry_run=True) - print("✅ build module available") - except (subprocess.CalledProcessError, FileNotFoundError): - print("❌ build module not available (pip install build)") - - try: - self.run_command(['python', '-m', 'setuptools_scm'], skip_dry_run=True) - print("✅ setuptools-scm available") - except (subprocess.CalledProcessError, FileNotFoundError): - print("❌ setuptools-scm not available") - - # Check existing packages - dist_dir = self.project_root / "dist" - if dist_dir.exists(): - packages = list(dist_dir.glob("*")) - print(f"\nExisting Packages: {len(packages)} files in dist/") - for pkg in packages[-5:]: # Show last 5 - print(f" - {pkg.name}") - else: - print("\nExisting Packages: None") - - def publish_release(self, version: str): - """Complete release workflow.""" - print(f"🚀 Publishing release {version}") - - # Validate state - is_valid, issues = self.validate_release_state() - if not is_valid and not self.force: - print("❌ Cannot publish release:") - for issue in issues: - print(f" - {issue}") - return False - - # Create git tag (this determines the version for setuptools-scm) - self.create_git_tag(version) - - # Build packages (setuptools-scm will use the tag for version) - self.build_packages() - - print(f"✅ Release {version} completed!") - print("📦 Packages available in dist/") - print(f"🏷️ Git tag v{version} created") - return True - - -def main(): - parser = argparse.ArgumentParser( - description="MarkiTect Release Management Tool (setuptools-scm)", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=__doc__.split('\n\n')[1] - ) - - parser.add_argument('command', choices=['status', 'validate', 'tag', 'build', 'publish'], - help='Release command to execute') - parser.add_argument('--version', type=str, help='Target version for git tag (e.g., 0.8.0)') - parser.add_argument('--dry-run', action='store_true', help='Show what would be done') - parser.add_argument('--force', action='store_true', help='Force operation despite warnings') - - args = parser.parse_args() - manager = SimpleReleaseManager(dry_run=args.dry_run, force=args.force) - - try: - if args.command == 'status': - manager.show_status() - - elif args.command == 'validate': - is_valid, issues = manager.validate_release_state() - if is_valid: - print("✅ Repository is ready for release") - else: - print("❌ Release validation failed:") - for issue in issues: - print(f" - {issue}") - sys.exit(1) - - elif args.command == 'tag': - if not args.version: - print("❌ --version is required for tag command") - sys.exit(1) - manager.create_git_tag(args.version) - - elif args.command == 'build': - manager.build_packages() - - elif args.command == 'publish': - if not args.version: - print("❌ --version is required for publish command") - sys.exit(1) - manager.publish_release(args.version) - - except Exception as e: - print(f"❌ Error: {e}") - sys.exit(1) - - -if __name__ == "__main__": - main() \ No newline at end of file