Coverage for src / moai_adk / core / migration / version_detector.py: 21.57%
51 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
1"""
2Version detection module for MoAI-ADK projects
4Detects the current version of a project and determines
5which migrations are needed.
6"""
8import json
9import logging
10from pathlib import Path
11from typing import Dict, List, Optional
13logger = logging.getLogger(__name__)
16class VersionDetector:
17 """Detects project version and migration requirements"""
19 def __init__(self, project_root: Path):
20 """
21 Initialize version detector
23 Args:
24 project_root: Root directory of the project
25 """
26 self.project_root = Path(project_root)
27 self.old_config = self.project_root / ".moai" / "config.json"
28 self.new_config = self.project_root / ".moai" / "config" / "config.json"
29 self.old_statusline = self.project_root / ".claude" / "statusline-config.yaml"
30 self.new_statusline = (
31 self.project_root / ".moai" / "config" / "statusline-config.yaml"
32 )
34 def detect_version(self) -> str:
35 """
36 Detect current project version based on file structure
38 Returns:
39 Version string (e.g., "0.23.0", "0.24.0+", "unknown")
40 """
41 # Check if already migrated to v0.24.0+
42 if self.new_config.exists():
43 return "0.24.0+"
45 # Check if v0.23.0 or earlier
46 if self.old_config.exists():
47 try:
48 with open(self.old_config, "r") as f:
49 config_data = json.load(f)
50 # Try to get version from config
51 if "moai_version" in config_data:
52 return config_data["moai_version"]
53 return "0.23.0"
54 except Exception as e:
55 logger.warning(f"Failed to read old config: {e}")
56 return "0.23.0"
58 return "unknown"
60 def needs_migration(self) -> bool:
61 """
62 Check if project needs migration
64 Returns:
65 True if migration is needed, False otherwise
66 """
67 version = self.detect_version()
68 if version == "unknown":
69 logger.info("Unknown version, assuming no migration needed")
70 return False
72 if version == "0.24.0+":
73 logger.info("Project already on v0.24.0+")
74 return False
76 # Version 0.23.0 or earlier needs migration
77 return True
79 def get_migration_plan(self) -> Dict[str, List[Dict[str, str]]]:
80 """
81 Get detailed migration plan
83 Returns:
84 Dictionary with migration actions:
85 {
86 "move": [{"from": "...", "to": "..."}],
87 "create": ["directory1", "directory2"],
88 "cleanup": ["old_file1", "old_file2"]
89 }
90 """
91 plan = {"move": [], "create": [], "cleanup": []}
93 if not self.needs_migration():
94 return plan
96 # Create config directory
97 plan["create"].append(str(self.project_root / ".moai" / "config"))
99 # Move config.json
100 if self.old_config.exists() and not self.new_config.exists():
101 plan["move"].append(
102 {
103 "from": str(self.old_config.relative_to(self.project_root)),
104 "to": str(self.new_config.relative_to(self.project_root)),
105 "description": "Main configuration file",
106 }
107 )
109 # Move statusline-config.yaml
110 if self.old_statusline.exists() and not self.new_statusline.exists():
111 plan["move"].append(
112 {
113 "from": str(self.old_statusline.relative_to(self.project_root)),
114 "to": str(self.new_statusline.relative_to(self.project_root)),
115 "description": "Statusline configuration",
116 }
117 )
119 # Cleanup old files (after successful migration)
120 if self.old_config.exists():
121 plan["cleanup"].append(str(self.old_config.relative_to(self.project_root)))
122 if self.old_statusline.exists():
123 plan["cleanup"].append(
124 str(self.old_statusline.relative_to(self.project_root))
125 )
127 return plan
129 def get_version_info(self) -> Dict[str, Optional[str]]:
130 """
131 Get detailed version information
133 Returns:
134 Dictionary with version details
135 """
136 return {
137 "detected_version": self.detect_version(),
138 "needs_migration": self.needs_migration(),
139 "has_old_config": self.old_config.exists(),
140 "has_new_config": self.new_config.exists(),
141 "has_old_statusline": self.old_statusline.exists(),
142 "has_new_statusline": self.new_statusline.exists(),
143 }