ic-code 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ic/__init__.py +29 -0
- ic/cli.py +769 -0
- ic/commands/__init__.py +10 -0
- ic/commands/config.py +699 -0
- ic/compat/__init__.py +241 -0
- ic/compat/cli.py +289 -0
- ic/compat/common.py +243 -0
- ic/config/__init__.py +10 -0
- ic/config/cleanup.py +382 -0
- ic/config/docs_organizer.py +587 -0
- ic/config/external.py +456 -0
- ic/config/manager.py +898 -0
- ic/config/migration.py +628 -0
- ic/config/schema.py +595 -0
- ic/config/secrets.py +437 -0
- ic/config/security.py +462 -0
- ic/core/__init__.py +16 -0
- ic/core/logging.py +311 -0
- ic/core/mcp_manager.py +856 -0
- ic/core/session.py +392 -0
- ic/core/silence_logging.py +67 -0
- ic_code-1.0.0.dist-info/METADATA +354 -0
- ic_code-1.0.0.dist-info/RECORD +27 -0
- ic_code-1.0.0.dist-info/WHEEL +5 -0
- ic_code-1.0.0.dist-info/entry_points.txt +2 -0
- ic_code-1.0.0.dist-info/licenses/LICENSE +21 -0
- ic_code-1.0.0.dist-info/top_level.txt +1 -0
ic/compat/common.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Backward compatibility layer for common module imports.
|
|
3
|
+
|
|
4
|
+
This module provides compatibility shims for the common.* imports that are
|
|
5
|
+
used throughout the existing codebase.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import warnings
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
# Import the compatibility layer
|
|
14
|
+
from . import warn_deprecated, get_logger, compat_config
|
|
15
|
+
|
|
16
|
+
# Add root directory to path for legacy imports
|
|
17
|
+
root_path = Path(__file__).parent.parent.parent.parent
|
|
18
|
+
if str(root_path) not in sys.path:
|
|
19
|
+
sys.path.insert(0, str(root_path))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def log_error(message: str, **kwargs) -> None:
|
|
23
|
+
"""
|
|
24
|
+
Backward compatibility function for log_error.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
message: Error message to log
|
|
28
|
+
**kwargs: Additional keyword arguments (ignored for compatibility)
|
|
29
|
+
"""
|
|
30
|
+
warn_deprecated(
|
|
31
|
+
"common.log.log_error",
|
|
32
|
+
"ICLogger.log_error or logger.error",
|
|
33
|
+
"2.0.0"
|
|
34
|
+
)
|
|
35
|
+
logger = get_logger()
|
|
36
|
+
logger.log_error(message)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def log_env_short(env_dict=None, **kwargs) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Backward compatibility function for log_env_short.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
env_dict: Environment variables dictionary (optional)
|
|
45
|
+
**kwargs: Additional keyword arguments (ignored for compatibility)
|
|
46
|
+
"""
|
|
47
|
+
warn_deprecated(
|
|
48
|
+
"common.log.log_env_short",
|
|
49
|
+
"ICLogger.log_info_file_only",
|
|
50
|
+
"2.0.0"
|
|
51
|
+
)
|
|
52
|
+
logger = get_logger()
|
|
53
|
+
if env_dict:
|
|
54
|
+
logger.log_info_file_only(f"Environment variables loaded: {len(env_dict)} variables")
|
|
55
|
+
else:
|
|
56
|
+
logger.log_info_file_only("Environment variables loaded")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def log_args_short(args: Any) -> None:
|
|
60
|
+
"""
|
|
61
|
+
Backward compatibility function for log_args_short.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
args: Arguments object to log
|
|
65
|
+
"""
|
|
66
|
+
warn_deprecated(
|
|
67
|
+
"common.log.log_args_short",
|
|
68
|
+
"ICLogger.log_args",
|
|
69
|
+
"2.0.0"
|
|
70
|
+
)
|
|
71
|
+
logger = get_logger()
|
|
72
|
+
logger.log_args(args)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def gather_env_for_command(platform: str, service: str = None, command: str = None) -> Dict[str, Any]:
|
|
76
|
+
"""
|
|
77
|
+
Backward compatibility function for gather_env_for_command.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
platform: Platform name (aws, gcp, azure, etc.)
|
|
81
|
+
service: Service name (optional)
|
|
82
|
+
command: Command name (optional)
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Environment configuration dictionary
|
|
86
|
+
"""
|
|
87
|
+
warn_deprecated(
|
|
88
|
+
"common.gather_env.gather_env_for_command",
|
|
89
|
+
"ConfigManager.get_config",
|
|
90
|
+
"2.0.0"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Import the original function for backward compatibility
|
|
94
|
+
try:
|
|
95
|
+
from common.gather_env import gather_env_for_command as original_gather_env
|
|
96
|
+
return original_gather_env(platform, service, command)
|
|
97
|
+
except ImportError:
|
|
98
|
+
# Fallback to config manager
|
|
99
|
+
return compat_config.get_all()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# Legacy log module compatibility
|
|
103
|
+
class LogCompat:
|
|
104
|
+
"""Compatibility class for common.log module."""
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def log_error(message: str, **kwargs) -> None:
|
|
108
|
+
"""Log error message."""
|
|
109
|
+
log_error(message, **kwargs)
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def log_env_short(env_dict=None, **kwargs) -> None:
|
|
113
|
+
"""Log environment variables."""
|
|
114
|
+
log_env_short(env_dict, **kwargs)
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def log_args_short(args: Any) -> None:
|
|
118
|
+
"""Log command arguments."""
|
|
119
|
+
log_args_short(args)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# Legacy gather_env module compatibility
|
|
123
|
+
class GatherEnvCompat:
|
|
124
|
+
"""Compatibility class for common.gather_env module."""
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
def gather_env_for_command(platform: str, service: str = None, command: str = None) -> Dict[str, Any]:
|
|
128
|
+
"""Gather environment for command."""
|
|
129
|
+
return gather_env_for_command(platform, service, command)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# Create module-like objects for backward compatibility
|
|
133
|
+
log_compat = LogCompat()
|
|
134
|
+
gather_env_compat = GatherEnvCompat()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# Utility functions for AWS session compatibility
|
|
138
|
+
def get_aws_session_compat(account_id: str, region: str = None):
|
|
139
|
+
"""
|
|
140
|
+
Backward compatibility function for AWS session creation.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
account_id: AWS account ID
|
|
144
|
+
region: AWS region
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
AWS session object
|
|
148
|
+
"""
|
|
149
|
+
warn_deprecated(
|
|
150
|
+
"manual AWS session creation",
|
|
151
|
+
"AWSSessionManager.create_session",
|
|
152
|
+
"2.0.0"
|
|
153
|
+
)
|
|
154
|
+
from . import get_aws_session_manager
|
|
155
|
+
|
|
156
|
+
session_manager = get_aws_session_manager()
|
|
157
|
+
if region is None:
|
|
158
|
+
region = compat_config.get('aws.regions', ['ap-northeast-2'])[0]
|
|
159
|
+
|
|
160
|
+
return session_manager.create_session(account_id, region)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def get_aws_profiles_compat():
|
|
164
|
+
"""
|
|
165
|
+
Backward compatibility function for AWS profiles.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Dictionary of AWS profiles
|
|
169
|
+
"""
|
|
170
|
+
warn_deprecated(
|
|
171
|
+
"manual AWS profile parsing",
|
|
172
|
+
"AWSSessionManager.get_profiles",
|
|
173
|
+
"2.0.0"
|
|
174
|
+
)
|
|
175
|
+
from . import get_aws_session_manager
|
|
176
|
+
|
|
177
|
+
session_manager = get_aws_session_manager()
|
|
178
|
+
return session_manager.get_profiles()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# Azure compatibility functions
|
|
182
|
+
def get_azure_client_compat(service_type: str):
|
|
183
|
+
"""
|
|
184
|
+
Backward compatibility function for Azure client creation.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
service_type: Type of Azure service client
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Azure client object
|
|
191
|
+
"""
|
|
192
|
+
warn_deprecated(
|
|
193
|
+
"manual Azure client creation",
|
|
194
|
+
"Azure service modules with new configuration",
|
|
195
|
+
"2.0.0"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Import Azure utilities if available
|
|
199
|
+
try:
|
|
200
|
+
from common.azure_utils import get_azure_client
|
|
201
|
+
return get_azure_client(service_type)
|
|
202
|
+
except ImportError:
|
|
203
|
+
raise ImportError("Azure utilities not available. Please install azure dependencies.")
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# GCP compatibility functions
|
|
207
|
+
def get_gcp_client_compat(service_type: str):
|
|
208
|
+
"""
|
|
209
|
+
Backward compatibility function for GCP client creation.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
service_type: Type of GCP service client
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
GCP client object
|
|
216
|
+
"""
|
|
217
|
+
warn_deprecated(
|
|
218
|
+
"manual GCP client creation",
|
|
219
|
+
"GCP service modules with new configuration",
|
|
220
|
+
"2.0.0"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Import GCP utilities if available
|
|
224
|
+
try:
|
|
225
|
+
from common.gcp_utils import get_gcp_client
|
|
226
|
+
return get_gcp_client(service_type)
|
|
227
|
+
except ImportError:
|
|
228
|
+
raise ImportError("GCP utilities not available. Please install google-cloud dependencies.")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# Export compatibility functions
|
|
232
|
+
__all__ = [
|
|
233
|
+
'log_error',
|
|
234
|
+
'log_env_short',
|
|
235
|
+
'log_args_short',
|
|
236
|
+
'gather_env_for_command',
|
|
237
|
+
'log_compat',
|
|
238
|
+
'gather_env_compat',
|
|
239
|
+
'get_aws_session_compat',
|
|
240
|
+
'get_aws_profiles_compat',
|
|
241
|
+
'get_azure_client_compat',
|
|
242
|
+
'get_gcp_client_compat',
|
|
243
|
+
]
|
ic/config/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration management module for IC.
|
|
3
|
+
|
|
4
|
+
This module provides configuration loading, validation, and security features.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .manager import ConfigManager
|
|
8
|
+
from .security import SecurityManager
|
|
9
|
+
|
|
10
|
+
__all__ = ["ConfigManager", "SecurityManager"]
|
ic/config/cleanup.py
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File cleanup and backup management module for IC.
|
|
3
|
+
|
|
4
|
+
This module provides functionality to organize and backup old files.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict, Any, List, Optional
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FileCleanupManager:
|
|
18
|
+
"""
|
|
19
|
+
Manages file cleanup and backup operations.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
"""Initialize FileCleanupManager."""
|
|
24
|
+
self.backup_dir = Path("backup")
|
|
25
|
+
self.olds_dir = self.backup_dir / "olds"
|
|
26
|
+
self.cleanup_history: List[Dict[str, Any]] = []
|
|
27
|
+
|
|
28
|
+
def backup_old_files(self, file_patterns: Optional[List[str]] = None) -> bool:
|
|
29
|
+
"""
|
|
30
|
+
Backup old files to backup/olds directory.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
file_patterns: List of file patterns to backup (default: common old files)
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
True if backup was successful
|
|
37
|
+
"""
|
|
38
|
+
if file_patterns is None:
|
|
39
|
+
file_patterns = [
|
|
40
|
+
"*.env*",
|
|
41
|
+
"*.log",
|
|
42
|
+
"logs/",
|
|
43
|
+
"old_*",
|
|
44
|
+
"*.bak",
|
|
45
|
+
"*.backup",
|
|
46
|
+
"temp_*",
|
|
47
|
+
"tmp_*"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
# Create backup directories
|
|
52
|
+
self.backup_dir.mkdir(exist_ok=True)
|
|
53
|
+
self.olds_dir.mkdir(exist_ok=True)
|
|
54
|
+
|
|
55
|
+
backed_up_files = []
|
|
56
|
+
|
|
57
|
+
for pattern in file_patterns:
|
|
58
|
+
files_found = self._find_files_by_pattern(pattern)
|
|
59
|
+
|
|
60
|
+
for file_path in files_found:
|
|
61
|
+
backup_path = self._backup_single_file(file_path)
|
|
62
|
+
if backup_path:
|
|
63
|
+
backed_up_files.append({
|
|
64
|
+
"original_path": str(file_path),
|
|
65
|
+
"backup_path": str(backup_path),
|
|
66
|
+
"file_type": self._get_file_type(file_path),
|
|
67
|
+
"size": file_path.stat().st_size if file_path.exists() else 0,
|
|
68
|
+
"timestamp": datetime.now().isoformat()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
if backed_up_files:
|
|
72
|
+
self.cleanup_history.extend(backed_up_files)
|
|
73
|
+
logger.info(f"Backed up {len(backed_up_files)} files to {self.olds_dir}")
|
|
74
|
+
return True
|
|
75
|
+
else:
|
|
76
|
+
logger.info("No files found to backup")
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
logger.error(f"Failed to backup old files: {e}")
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
def _find_files_by_pattern(self, pattern: str) -> List[Path]:
|
|
84
|
+
"""Find files matching a pattern."""
|
|
85
|
+
files = []
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
if pattern.endswith('/'):
|
|
89
|
+
# Directory pattern
|
|
90
|
+
dir_name = pattern.rstrip('/')
|
|
91
|
+
dir_path = Path(dir_name)
|
|
92
|
+
if dir_path.exists() and dir_path.is_dir():
|
|
93
|
+
files.append(dir_path)
|
|
94
|
+
else:
|
|
95
|
+
# File pattern
|
|
96
|
+
files.extend(Path('.').glob(pattern))
|
|
97
|
+
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.warning(f"Error finding files with pattern '{pattern}': {e}")
|
|
100
|
+
|
|
101
|
+
return files
|
|
102
|
+
|
|
103
|
+
def _backup_single_file(self, file_path: Path) -> Optional[Path]:
|
|
104
|
+
"""Backup a single file or directory."""
|
|
105
|
+
if not file_path.exists():
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
# Generate backup name with timestamp
|
|
110
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
111
|
+
|
|
112
|
+
if file_path.is_dir():
|
|
113
|
+
backup_name = f"{file_path.name}_{timestamp}"
|
|
114
|
+
backup_path = self.olds_dir / backup_name
|
|
115
|
+
shutil.copytree(file_path, backup_path)
|
|
116
|
+
# Remove original directory
|
|
117
|
+
shutil.rmtree(file_path)
|
|
118
|
+
else:
|
|
119
|
+
backup_name = f"{file_path.stem}_{timestamp}{file_path.suffix}"
|
|
120
|
+
backup_path = self.olds_dir / backup_name
|
|
121
|
+
shutil.copy2(file_path, backup_path)
|
|
122
|
+
# Remove original file
|
|
123
|
+
file_path.unlink()
|
|
124
|
+
|
|
125
|
+
logger.debug(f"Backed up {file_path} to {backup_path}")
|
|
126
|
+
return backup_path
|
|
127
|
+
|
|
128
|
+
except Exception as e:
|
|
129
|
+
logger.warning(f"Failed to backup {file_path}: {e}")
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
def _get_file_type(self, file_path: Path) -> str:
|
|
133
|
+
"""Determine file type for categorization."""
|
|
134
|
+
if file_path.is_dir():
|
|
135
|
+
return "directory"
|
|
136
|
+
|
|
137
|
+
suffix = file_path.suffix.lower()
|
|
138
|
+
|
|
139
|
+
if suffix in ['.env']:
|
|
140
|
+
return "environment_config"
|
|
141
|
+
elif suffix in ['.log']:
|
|
142
|
+
return "log_file"
|
|
143
|
+
elif suffix in ['.bak', '.backup']:
|
|
144
|
+
return "backup_file"
|
|
145
|
+
elif suffix in ['.py']:
|
|
146
|
+
return "python_file"
|
|
147
|
+
elif suffix in ['.yaml', '.yml']:
|
|
148
|
+
return "yaml_config"
|
|
149
|
+
elif suffix in ['.json']:
|
|
150
|
+
return "json_config"
|
|
151
|
+
elif suffix in ['.md']:
|
|
152
|
+
return "documentation"
|
|
153
|
+
else:
|
|
154
|
+
return "other"
|
|
155
|
+
|
|
156
|
+
def create_cleanup_history_document(self) -> bool:
|
|
157
|
+
"""
|
|
158
|
+
Create a document recording all cleanup operations.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
True if document was created successfully
|
|
162
|
+
"""
|
|
163
|
+
try:
|
|
164
|
+
history_content = self._generate_cleanup_history_content()
|
|
165
|
+
|
|
166
|
+
history_path = self.backup_dir / "file_cleanup_history.md"
|
|
167
|
+
with open(history_path, 'w', encoding='utf-8') as f:
|
|
168
|
+
f.write(history_content)
|
|
169
|
+
|
|
170
|
+
logger.info(f"Created cleanup history document at {history_path}")
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.error(f"Failed to create cleanup history document: {e}")
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
def _generate_cleanup_history_content(self) -> str:
|
|
178
|
+
"""Generate cleanup history document content."""
|
|
179
|
+
content = """# IC File Cleanup History
|
|
180
|
+
|
|
181
|
+
This document records all file cleanup and backup operations performed during the IC configuration migration.
|
|
182
|
+
|
|
183
|
+
## Overview
|
|
184
|
+
|
|
185
|
+
During the migration from .env-based configuration to YAML-based configuration, various old files were identified and moved to the backup directory to keep the project clean while preserving important data.
|
|
186
|
+
|
|
187
|
+
## Backup Structure
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
backup/
|
|
191
|
+
├── olds/ # Old files moved during cleanup
|
|
192
|
+
│ ├── .env_YYYYMMDD_HHMMSS # Original environment files
|
|
193
|
+
│ ├── logs_YYYYMMDD_HHMMSS/ # Old log directories
|
|
194
|
+
│ └── ... # Other backed up files
|
|
195
|
+
├── file_cleanup_history.md # This document
|
|
196
|
+
└── migration_history.md # Configuration migration history
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## File Categories
|
|
200
|
+
|
|
201
|
+
### Environment Configuration Files
|
|
202
|
+
- `.env` files and variants
|
|
203
|
+
- Contains original environment variable configurations
|
|
204
|
+
- **Location**: Moved to `backup/olds/`
|
|
205
|
+
- **Purpose**: Preserve original configuration for rollback if needed
|
|
206
|
+
|
|
207
|
+
### Log Files
|
|
208
|
+
- `*.log` files and `logs/` directories
|
|
209
|
+
- Contains historical application logs
|
|
210
|
+
- **Location**: Moved to `backup/olds/`
|
|
211
|
+
- **Purpose**: Archive old logs while implementing fixed log paths
|
|
212
|
+
|
|
213
|
+
### Backup Files
|
|
214
|
+
- `*.bak`, `*.backup` files
|
|
215
|
+
- Previous backup files from other operations
|
|
216
|
+
- **Location**: Consolidated in `backup/olds/`
|
|
217
|
+
- **Purpose**: Centralize all backup files
|
|
218
|
+
|
|
219
|
+
### Temporary Files
|
|
220
|
+
- `temp_*`, `tmp_*`, `old_*` files
|
|
221
|
+
- Temporary files from development or previous operations
|
|
222
|
+
- **Location**: Moved to `backup/olds/`
|
|
223
|
+
- **Purpose**: Clean up workspace while preserving potentially important data
|
|
224
|
+
|
|
225
|
+
## Cleanup Operations
|
|
226
|
+
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
if self.cleanup_history:
|
|
230
|
+
# Group files by type
|
|
231
|
+
files_by_type = {}
|
|
232
|
+
for file_info in self.cleanup_history:
|
|
233
|
+
file_type = file_info['file_type']
|
|
234
|
+
if file_type not in files_by_type:
|
|
235
|
+
files_by_type[file_type] = []
|
|
236
|
+
files_by_type[file_type].append(file_info)
|
|
237
|
+
|
|
238
|
+
for file_type, files in files_by_type.items():
|
|
239
|
+
content += f"### {file_type.replace('_', ' ').title()}\n\n"
|
|
240
|
+
|
|
241
|
+
total_size = sum(f['size'] for f in files)
|
|
242
|
+
content += f"**Total Files**: {len(files)}\n"
|
|
243
|
+
content += f"**Total Size**: {self._format_size(total_size)}\n\n"
|
|
244
|
+
|
|
245
|
+
content += "| Original Path | Backup Path | Size | Timestamp |\n"
|
|
246
|
+
content += "|---------------|-------------|------|----------|\n"
|
|
247
|
+
|
|
248
|
+
for file_info in files:
|
|
249
|
+
content += f"| `{file_info['original_path']}` | `{file_info['backup_path']}` | {self._format_size(file_info['size'])} | {file_info['timestamp'][:19]} |\n"
|
|
250
|
+
|
|
251
|
+
content += "\n"
|
|
252
|
+
else:
|
|
253
|
+
content += "No cleanup operations recorded.\n\n"
|
|
254
|
+
|
|
255
|
+
content += """## File Roles and Purposes
|
|
256
|
+
|
|
257
|
+
### Original .env Files
|
|
258
|
+
- **Role**: Primary configuration source for the old system
|
|
259
|
+
- **Contents**: Environment variables for all services (AWS, GCP, Azure, etc.)
|
|
260
|
+
- **Migration**: Values extracted and categorized into `config/default.yaml` and `config/secrets.yaml`
|
|
261
|
+
- **Backup Reason**: Preserve for rollback and reference
|
|
262
|
+
|
|
263
|
+
### Log Files
|
|
264
|
+
- **Role**: Application runtime logs and debugging information
|
|
265
|
+
- **Contents**: Historical execution logs, error messages, debug information
|
|
266
|
+
- **Migration**: New fixed log path implemented at `~/.ic/logs/`
|
|
267
|
+
- **Backup Reason**: Archive historical data while implementing new logging system
|
|
268
|
+
|
|
269
|
+
### Development Files
|
|
270
|
+
- **Role**: Temporary files created during development
|
|
271
|
+
- **Contents**: Test files, temporary configurations, development artifacts
|
|
272
|
+
- **Migration**: Not migrated, but preserved for reference
|
|
273
|
+
- **Backup Reason**: Clean workspace while preserving potentially useful development data
|
|
274
|
+
|
|
275
|
+
## Recovery Instructions
|
|
276
|
+
|
|
277
|
+
### Restoring Original .env Configuration
|
|
278
|
+
If you need to restore the original .env-based configuration:
|
|
279
|
+
|
|
280
|
+
1. Copy the backed up .env file from `backup/olds/` to the project root
|
|
281
|
+
2. Rename or remove the `config/` directory
|
|
282
|
+
3. Restart the application
|
|
283
|
+
|
|
284
|
+
### Accessing Historical Logs
|
|
285
|
+
Historical logs are preserved in `backup/olds/logs_*/` directories and can be accessed for debugging or audit purposes.
|
|
286
|
+
|
|
287
|
+
### Recovering Specific Files
|
|
288
|
+
All backed up files maintain their original structure and can be restored by copying them back to their original locations.
|
|
289
|
+
|
|
290
|
+
## Cleanup Benefits
|
|
291
|
+
|
|
292
|
+
1. **Cleaner Workspace**: Removed clutter from the project root
|
|
293
|
+
2. **Organized Structure**: All backups centralized in one location
|
|
294
|
+
3. **Preserved Data**: No data loss - everything is backed up
|
|
295
|
+
4. **Clear Migration Path**: Easy to identify what was changed
|
|
296
|
+
5. **Rollback Capability**: Simple restoration process if needed
|
|
297
|
+
|
|
298
|
+
## Maintenance
|
|
299
|
+
|
|
300
|
+
- Backup files are preserved indefinitely
|
|
301
|
+
- Consider periodic cleanup of very old backup files (>1 year)
|
|
302
|
+
- Monitor backup directory size if disk space becomes a concern
|
|
303
|
+
- Backup directory can be excluded from version control
|
|
304
|
+
|
|
305
|
+
"""
|
|
306
|
+
|
|
307
|
+
return content
|
|
308
|
+
|
|
309
|
+
def _format_size(self, size_bytes: int) -> str:
|
|
310
|
+
"""Format file size in human-readable format."""
|
|
311
|
+
if size_bytes == 0:
|
|
312
|
+
return "0 B"
|
|
313
|
+
|
|
314
|
+
for unit in ['B', 'KB', 'MB', 'GB']:
|
|
315
|
+
if size_bytes < 1024:
|
|
316
|
+
return f"{size_bytes:.1f} {unit}"
|
|
317
|
+
size_bytes /= 1024
|
|
318
|
+
|
|
319
|
+
return f"{size_bytes:.1f} TB"
|
|
320
|
+
|
|
321
|
+
def get_backup_summary(self) -> Dict[str, Any]:
|
|
322
|
+
"""
|
|
323
|
+
Get summary of backup operations.
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Dictionary containing backup summary
|
|
327
|
+
"""
|
|
328
|
+
if not self.cleanup_history:
|
|
329
|
+
return {"total_files": 0, "total_size": 0, "file_types": {}}
|
|
330
|
+
|
|
331
|
+
total_files = len(self.cleanup_history)
|
|
332
|
+
total_size = sum(f['size'] for f in self.cleanup_history)
|
|
333
|
+
|
|
334
|
+
file_types = {}
|
|
335
|
+
for file_info in self.cleanup_history:
|
|
336
|
+
file_type = file_info['file_type']
|
|
337
|
+
if file_type not in file_types:
|
|
338
|
+
file_types[file_type] = {"count": 0, "size": 0}
|
|
339
|
+
file_types[file_type]["count"] += 1
|
|
340
|
+
file_types[file_type]["size"] += file_info['size']
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
"total_files": total_files,
|
|
344
|
+
"total_size": total_size,
|
|
345
|
+
"file_types": file_types,
|
|
346
|
+
"backup_location": str(self.olds_dir)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
def validate_backups(self) -> Dict[str, List[str]]:
|
|
350
|
+
"""
|
|
351
|
+
Validate that backed up files exist and are accessible.
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Dictionary of validation results
|
|
355
|
+
"""
|
|
356
|
+
issues = {
|
|
357
|
+
"missing_backups": [],
|
|
358
|
+
"corrupted_backups": [],
|
|
359
|
+
"permission_issues": []
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
for file_info in self.cleanup_history:
|
|
363
|
+
backup_path = Path(file_info['backup_path'])
|
|
364
|
+
|
|
365
|
+
if not backup_path.exists():
|
|
366
|
+
issues["missing_backups"].append(file_info['backup_path'])
|
|
367
|
+
continue
|
|
368
|
+
|
|
369
|
+
try:
|
|
370
|
+
# Try to read a small portion to check if file is accessible
|
|
371
|
+
if backup_path.is_file():
|
|
372
|
+
with open(backup_path, 'rb') as f:
|
|
373
|
+
f.read(1024) # Read first 1KB
|
|
374
|
+
elif backup_path.is_dir():
|
|
375
|
+
list(backup_path.iterdir()) # List directory contents
|
|
376
|
+
|
|
377
|
+
except PermissionError:
|
|
378
|
+
issues["permission_issues"].append(file_info['backup_path'])
|
|
379
|
+
except Exception:
|
|
380
|
+
issues["corrupted_backups"].append(file_info['backup_path'])
|
|
381
|
+
|
|
382
|
+
return issues
|