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

1""" 

2Version detection module for MoAI-ADK projects 

3 

4Detects the current version of a project and determines 

5which migrations are needed. 

6""" 

7 

8import json 

9import logging 

10from pathlib import Path 

11from typing import Dict, List, Optional 

12 

13logger = logging.getLogger(__name__) 

14 

15 

16class VersionDetector: 

17 """Detects project version and migration requirements""" 

18 

19 def __init__(self, project_root: Path): 

20 """ 

21 Initialize version detector 

22 

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 ) 

33 

34 def detect_version(self) -> str: 

35 """ 

36 Detect current project version based on file structure 

37 

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+" 

44 

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" 

57 

58 return "unknown" 

59 

60 def needs_migration(self) -> bool: 

61 """ 

62 Check if project needs migration 

63 

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 

71 

72 if version == "0.24.0+": 

73 logger.info("Project already on v0.24.0+") 

74 return False 

75 

76 # Version 0.23.0 or earlier needs migration 

77 return True 

78 

79 def get_migration_plan(self) -> Dict[str, List[Dict[str, str]]]: 

80 """ 

81 Get detailed migration plan 

82 

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": []} 

92 

93 if not self.needs_migration(): 

94 return plan 

95 

96 # Create config directory 

97 plan["create"].append(str(self.project_root / ".moai" / "config")) 

98 

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 ) 

108 

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 ) 

118 

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 ) 

126 

127 return plan 

128 

129 def get_version_info(self) -> Dict[str, Optional[str]]: 

130 """ 

131 Get detailed version information 

132 

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 }