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:
2025-11-09 01:29:15 +01:00
parent d505c15d40
commit d0ffdc057c
38 changed files with 3978 additions and 1361 deletions

View File

@@ -0,0 +1,230 @@
"""
Release validation utilities.
This module provides validation functions for release readiness.
"""
from pathlib import Path
from typing import List, Tuple, Optional
from ..git.manager import GitManager
class ReleaseValidator:
"""Validates release readiness and requirements."""
def __init__(self, project_root: Optional[Path] = None):
"""Initialize release validator.
Args:
project_root: Root directory of the project
"""
self.project_root = project_root or Path.cwd()
self.git_manager = GitManager(project_root)
def validate_release_state(self, force: bool = False) -> Tuple[bool, List[str]]:
"""Validate that repository is ready for release.
Args:
force: Skip validation checks if True
Returns:
Tuple of (is_valid, list_of_issues)
"""
if force:
return True, []
issues = []
# Git repository validation
git_issues = self._validate_git_state()
issues.extend(git_issues)
# Project structure validation
structure_issues = self._validate_project_structure()
issues.extend(structure_issues)
# Configuration validation
config_issues = self._validate_configuration()
issues.extend(config_issues)
return len(issues) == 0, issues
def _validate_git_state(self) -> List[str]:
"""Validate git repository state.
Returns:
List of git-related issues
"""
issues = []
status = self.git_manager.get_repository_status()
if not status['is_repo']:
issues.append("Not in a git repository")
return issues
if status['has_changes']:
issues.append("Repository has uncommitted changes")
if status['branch'] != 'main':
issues.append(f"Not on main branch (currently on {status['branch']})")
# Check if remote exists
remote_url = self.git_manager.get_remote_url()
if not remote_url:
issues.append("No git remote 'origin' configured")
return issues
def _validate_project_structure(self) -> List[str]:
"""Validate project structure for releases.
Returns:
List of project structure issues
"""
issues = []
# Check for required files
required_files = ['pyproject.toml']
for file_name in required_files:
file_path = self.project_root / file_name
if not file_path.exists():
issues.append(f"Missing required file: {file_name}")
# Check for setuptools-scm configuration
pyproject_path = self.project_root / 'pyproject.toml'
if pyproject_path.exists():
try:
import tomllib
except ImportError:
try:
import tomli as tomllib
except ImportError:
issues.append("Cannot read pyproject.toml (tomllib/tomli not available)")
return issues
try:
with open(pyproject_path, 'rb') as f:
config = tomllib.load(f)
# Check for setuptools-scm configuration
build_system = config.get('build-system', {})
if 'setuptools-scm' not in str(build_system.get('requires', [])):
issues.append("setuptools-scm not found in build-system.requires")
# Check for dynamic version
project_config = config.get('project', {})
if 'version' in project_config:
issues.append("Static version found in project config. Use dynamic versioning with setuptools-scm.")
dynamic = project_config.get('dynamic', [])
if 'version' not in dynamic:
issues.append("'version' not in project.dynamic. Add it for setuptools-scm.")
except Exception as e:
issues.append(f"Error reading pyproject.toml: {e}")
return issues
def _validate_configuration(self) -> List[str]:
"""Validate release configuration.
Returns:
List of configuration issues
"""
issues = []
# Check for environment variables that might be needed
import os
# Check for common auth tokens (warn, don't fail)
auth_vars = ['GITEA_API_TOKEN', 'PYPI_TOKEN', 'GITHUB_TOKEN']
available_auth = [var for var in auth_vars if os.getenv(var)]
if not available_auth:
issues.append("No authentication tokens found in environment. "
"Consider setting GITEA_API_TOKEN, PYPI_TOKEN, or GITHUB_TOKEN "
"for package publishing.")
return issues
def validate_version_string(self, version_string: str) -> Tuple[bool, List[str]]:
"""Validate a version string for release.
Args:
version_string: Version string to validate
Returns:
Tuple of (is_valid, list_of_issues)
"""
issues = []
if not version_string:
issues.append("Version string cannot be empty")
return False, issues
# Check basic format
if not version_string.replace('.', '').replace('-', '').replace('+', '').replace('a', '').replace('b', '').replace('rc', '').isalnum():
issues.append("Version string contains invalid characters")
# Check for development markers in release
dev_markers = ['dev', '.dev', '+dev']
if any(marker in version_string.lower() for marker in dev_markers):
issues.append("Development versions should not be released")
# Check for reasonable version format (semantic versioning)
try:
from packaging import version
version.Version(version_string)
except Exception:
issues.append("Version string is not valid according to PEP 440")
# Check if version already exists as git tag
tag_name = version_string if version_string.startswith('v') else f'v{version_string}'
if self.git_manager.tag_exists(tag_name):
issues.append(f"Git tag {tag_name} already exists")
return len(issues) == 0, issues
def get_validation_summary(self) -> dict:
"""Get a comprehensive validation summary.
Returns:
Dictionary with validation results
"""
is_valid, issues = self.validate_release_state()
return {
'is_valid': is_valid,
'issues': issues,
'git_status': self.git_manager.get_repository_status(),
'recommendations': self._get_recommendations(issues)
}
def _get_recommendations(self, issues: List[str]) -> List[str]:
"""Get recommendations based on validation issues.
Args:
issues: List of validation issues
Returns:
List of recommendations
"""
recommendations = []
if any('uncommitted changes' in issue for issue in issues):
recommendations.append("Commit or stash your changes before releasing")
if any('not on main branch' in issue for issue in issues):
recommendations.append("Switch to main branch: git checkout main")
if any('setuptools-scm' in issue for issue in issues):
recommendations.append("Configure setuptools-scm in pyproject.toml")
if any('authentication' in issue.lower() for issue in issues):
recommendations.append("Set up authentication tokens for package publishing")
if not issues:
recommendations.append("Repository is ready for release!")
return recommendations