feat: implement modular capability system with automatic discovery
- Move release management to capabilities/release-management/ with complete Makefile - Create automatic capability discovery system in scripts/capability_discovery.mk - Add capability-manager subagent for managing modular architecture - Implement target delegation system enabling capability-name-target patterns - Create Makefiles for markitect-content, markitect-utils, and issue-facade capabilities - Remove legacy release management code and documentation from main project - Update main Makefile to use capability discovery and delegation - Add comprehensive capability status, help, and management targets The capability system provides: - Automatic discovery of capabilities with Makefiles - Clean target delegation without conflicts - Modular architecture following established patterns - Comprehensive help and status reporting - Zero-conflict capability integration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
"""
|
||||
Main CLI entry point for release management.
|
||||
|
||||
This module provides the main CLI interface adapted from the original release.py script.
|
||||
"""
|
||||
|
||||
import click
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from ..core.manager import ReleaseManager
|
||||
from ..utils.version import VersionManager
|
||||
|
||||
|
||||
@click.group(invoke_without_command=True)
|
||||
@click.option('--dry-run', is_flag=True, help='Show what would be done without making changes')
|
||||
@click.option('--force', is_flag=True, help='Force operation even with warnings')
|
||||
@click.option('--project-root', type=click.Path(exists=True, path_type=Path),
|
||||
help='Project root directory')
|
||||
@click.pass_context
|
||||
def main(ctx, dry_run: bool, force: bool, project_root: Optional[Path]):
|
||||
"""Release management CLI for Python projects."""
|
||||
ctx.ensure_object(dict)
|
||||
ctx.obj['dry_run'] = dry_run
|
||||
ctx.obj['force'] = force
|
||||
ctx.obj['project_root'] = project_root
|
||||
|
||||
# If no command specified, show status
|
||||
if ctx.invoked_subcommand is None:
|
||||
ctx.invoke(status)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.pass_context
|
||||
def status(ctx):
|
||||
"""Show current release status and version information."""
|
||||
manager = ReleaseManager(
|
||||
project_root=ctx.obj['project_root'],
|
||||
dry_run=ctx.obj['dry_run'],
|
||||
force=ctx.obj['force']
|
||||
)
|
||||
|
||||
print("🔍 Release Status")
|
||||
print("=" * 60)
|
||||
|
||||
status_info = manager.get_release_status()
|
||||
|
||||
# Version information
|
||||
print(f"Current Version: {status_info['version']}")
|
||||
|
||||
# Git information
|
||||
if status_info.get('is_repo'):
|
||||
print(f"Git Branch: {status_info['branch']}")
|
||||
print(f"Latest Commit: {status_info['latest_commit']}")
|
||||
print(f"Latest Tag: {status_info['latest_tag'] or 'None'}")
|
||||
print(f"Uncommitted Changes: {'Yes' if status_info['has_changes'] else 'No'}")
|
||||
else:
|
||||
print("Git Repository: Not available")
|
||||
|
||||
# Package information
|
||||
packages = status_info['packages']
|
||||
print(f"\\nBuilt Packages: {packages['total_count']} files")
|
||||
if packages['wheels']:
|
||||
print(" Wheels:")
|
||||
for wheel in packages['wheels']:
|
||||
print(f" - {wheel}")
|
||||
if packages['sdists']:
|
||||
print(" Source Distributions:")
|
||||
for sdist in packages['sdists']:
|
||||
print(f" - {sdist}")
|
||||
|
||||
# Validation status
|
||||
validation = status_info['validation']
|
||||
if validation['is_valid']:
|
||||
print("\\n✅ Repository is ready for release")
|
||||
else:
|
||||
print("\\n❌ Release validation issues:")
|
||||
for issue in validation['issues']:
|
||||
print(f" - {issue}")
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.pass_context
|
||||
def validate(ctx):
|
||||
"""Validate repository state for release readiness."""
|
||||
manager = ReleaseManager(
|
||||
project_root=ctx.obj['project_root'],
|
||||
dry_run=ctx.obj['dry_run'],
|
||||
force=ctx.obj['force']
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option('--version', required=True, help='Version to tag (e.g., 0.8.0)')
|
||||
@click.option('--message', help='Tag message')
|
||||
@click.pass_context
|
||||
def tag(ctx, version: str, message: Optional[str]):
|
||||
"""Create git tag for version."""
|
||||
manager = ReleaseManager(
|
||||
project_root=ctx.obj['project_root'],
|
||||
dry_run=ctx.obj['dry_run'],
|
||||
force=ctx.obj['force']
|
||||
)
|
||||
|
||||
if manager.create_tag(version, message):
|
||||
print(f"✅ Successfully created tag for version {version}")
|
||||
else:
|
||||
print(f"❌ Failed to create tag for version {version}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.pass_context
|
||||
def build(ctx):
|
||||
"""Build release packages using setuptools-scm."""
|
||||
manager = ReleaseManager(
|
||||
project_root=ctx.obj['project_root'],
|
||||
dry_run=ctx.obj['dry_run'],
|
||||
force=ctx.obj['force']
|
||||
)
|
||||
|
||||
if manager.build_packages():
|
||||
print("✅ Packages built successfully")
|
||||
else:
|
||||
print("❌ Package build failed")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option('--version', required=True, help='Version to publish (e.g., 0.8.0)')
|
||||
@click.option('--registry', default='gitea', help='Registry type (gitea, pypi, etc.)')
|
||||
@click.option('--skip-build', is_flag=True, help='Skip building and use existing packages')
|
||||
@click.pass_context
|
||||
def publish(ctx, version: str, registry: str, skip_build: bool):
|
||||
"""Complete release workflow: tag, build, and publish."""
|
||||
manager = ReleaseManager(
|
||||
project_root=ctx.obj['project_root'],
|
||||
dry_run=ctx.obj['dry_run'],
|
||||
force=ctx.obj['force']
|
||||
)
|
||||
|
||||
if manager.publish_with_fallback(version, registry, skip_build):
|
||||
print(f"🎉 Release {version} published successfully!")
|
||||
else:
|
||||
print(f"❌ Release {version} failed")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option('--registry', default='gitea', help='Registry type (gitea, pypi, etc.)')
|
||||
@click.pass_context
|
||||
def upload(ctx, registry: str):
|
||||
"""Upload existing packages to registry."""
|
||||
manager = ReleaseManager(
|
||||
project_root=ctx.obj['project_root'],
|
||||
dry_run=ctx.obj['dry_run'],
|
||||
force=ctx.obj['force']
|
||||
)
|
||||
|
||||
if manager.upload_existing_packages(registry):
|
||||
print(f"✅ Packages uploaded to {registry}")
|
||||
else:
|
||||
print(f"❌ Upload to {registry} failed")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@main.command('registry-info')
|
||||
@click.option('--registry', default='gitea', help='Registry type to show info for')
|
||||
@click.pass_context
|
||||
def registry_info(ctx, registry: str):
|
||||
"""Show package registry information and status."""
|
||||
manager = ReleaseManager(
|
||||
project_root=ctx.obj['project_root'],
|
||||
dry_run=ctx.obj['dry_run'],
|
||||
force=ctx.obj['force']
|
||||
)
|
||||
|
||||
info = manager.show_registry_info(registry)
|
||||
|
||||
print(f"📦 {registry.title()} Registry Information")
|
||||
print("=" * 50)
|
||||
|
||||
if 'error' in info:
|
||||
print(f"❌ Error: {info['error']}")
|
||||
return
|
||||
|
||||
for key, value in info.items():
|
||||
if isinstance(value, bool):
|
||||
indicator = "✅" if value else "❌"
|
||||
print(f"{key.replace('_', ' ').title()}: {indicator}")
|
||||
else:
|
||||
print(f"{key.replace('_', ' ').title()}: {value}")
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.pass_context
|
||||
def clean(ctx):
|
||||
"""Clean build artifacts and temporary files."""
|
||||
manager = ReleaseManager(
|
||||
project_root=ctx.obj['project_root'],
|
||||
dry_run=ctx.obj['dry_run'],
|
||||
force=ctx.obj['force']
|
||||
)
|
||||
|
||||
manager.clean_build_artifacts()
|
||||
print("✅ Build artifacts cleaned")
|
||||
|
||||
|
||||
@main.command('version-info')
|
||||
@click.option('--suggest', is_flag=True, help='Suggest next version options')
|
||||
@click.pass_context
|
||||
def version_info(ctx, suggest: bool):
|
||||
"""Show version information and suggestions."""
|
||||
version_manager = VersionManager(ctx.obj['project_root'])
|
||||
|
||||
current = version_manager.get_current_version()
|
||||
print(f"Current Version: {current}")
|
||||
|
||||
if suggest:
|
||||
suggestions = version_manager.suggest_version(current)
|
||||
if 'error' in suggestions:
|
||||
print(f"❌ {suggestions['error']}")
|
||||
if 'suggestion' in suggestions:
|
||||
print(f"💡 {suggestions['suggestion']}")
|
||||
else:
|
||||
print("\\nSuggested next versions:")
|
||||
print(f" Patch: {suggestions['patch']}")
|
||||
print(f" Minor: {suggestions['minor']}")
|
||||
print(f" Major: {suggestions['major']}")
|
||||
|
||||
# Show version components
|
||||
version_data = version_manager.parse_version(current)
|
||||
if 'error' not in version_data:
|
||||
print("\\nVersion Components:")
|
||||
for key, value in version_data.items():
|
||||
if value is not None:
|
||||
print(f" {key.replace('_', ' ').title()}: {value}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user