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/compat/__init__.py ADDED
@@ -0,0 +1,241 @@
1
+ """
2
+ Backward compatibility layer for IC package.
3
+
4
+ This module provides compatibility shims for existing import paths and functionality
5
+ to ensure smooth migration from the old structure to the new src/ layout.
6
+ """
7
+
8
+ import warnings
9
+ from typing import Any, Dict, Optional
10
+ import os
11
+ from pathlib import Path
12
+
13
+ # Import new modules
14
+ from ..config.manager import ConfigManager
15
+ from ..config.security import SecurityManager
16
+ from ..core.logging import ICLogger
17
+ from ..core.session import AWSSessionManager
18
+ from ..core.mcp_manager import MCPManager
19
+
20
+ # Global compatibility instances
21
+ _config_manager: Optional[ConfigManager] = None
22
+ _logger: Optional[ICLogger] = None
23
+ _aws_session_manager: Optional[AWSSessionManager] = None
24
+
25
+
26
+ def get_config_manager() -> ConfigManager:
27
+ """Get or create global ConfigManager instance."""
28
+ global _config_manager
29
+ if _config_manager is None:
30
+ security_manager = SecurityManager()
31
+ _config_manager = ConfigManager(security_manager=security_manager)
32
+ # Load configuration with .env fallback
33
+ _config_manager.load_config()
34
+ return _config_manager
35
+
36
+
37
+ def get_logger() -> ICLogger:
38
+ """Get or create global ICLogger instance."""
39
+ global _logger
40
+ if _logger is None:
41
+ config_manager = get_config_manager()
42
+ config = config_manager.get_config()
43
+ _logger = ICLogger(config)
44
+ return _logger
45
+
46
+
47
+ def get_aws_session_manager() -> AWSSessionManager:
48
+ """Get or create global AWSSessionManager instance."""
49
+ global _aws_session_manager
50
+ if _aws_session_manager is None:
51
+ config_manager = get_config_manager()
52
+ config = config_manager.get_config()
53
+ _aws_session_manager = AWSSessionManager(config)
54
+ return _aws_session_manager
55
+
56
+
57
+ def warn_deprecated(old_path: str, new_path: str, version: str = "2.0.0") -> None:
58
+ """Issue deprecation warning for old import paths."""
59
+ warnings.warn(
60
+ f"'{old_path}' is deprecated and will be removed in version {version}. "
61
+ f"Please use '{new_path}' instead.",
62
+ DeprecationWarning,
63
+ stacklevel=3
64
+ )
65
+
66
+
67
+ class CompatibilityConfig:
68
+ """
69
+ Compatibility wrapper for configuration access.
70
+
71
+ Provides backward compatibility for .env file access while encouraging
72
+ migration to the new YAML-based configuration system.
73
+ """
74
+
75
+ def __init__(self):
76
+ self._config_manager = get_config_manager()
77
+ self._env_loaded = False
78
+ self._load_env_if_needed()
79
+
80
+ def _load_env_if_needed(self):
81
+ """Load .env file if it exists and hasn't been loaded yet."""
82
+ if not self._env_loaded:
83
+ env_file = Path('.env')
84
+ if env_file.exists():
85
+ try:
86
+ from dotenv import load_dotenv
87
+ load_dotenv()
88
+ self._env_loaded = True
89
+
90
+ # Issue deprecation warning
91
+ warn_deprecated(
92
+ ".env file usage",
93
+ "YAML configuration files (config.yaml)",
94
+ "2.0.0"
95
+ )
96
+ except ImportError:
97
+ pass
98
+
99
+ def get(self, key: str, default: Any = None) -> Any:
100
+ """
101
+ Get configuration value with .env fallback.
102
+
103
+ Args:
104
+ key: Configuration key (supports dot notation)
105
+ default: Default value if key not found
106
+
107
+ Returns:
108
+ Configuration value
109
+ """
110
+ # Try new config system first
111
+ value = self._config_manager.get_config_value(key, None)
112
+ if value is not None:
113
+ return value
114
+
115
+ # Fallback to environment variable
116
+ env_key = key.upper().replace('.', '_')
117
+ env_value = os.getenv(env_key)
118
+ if env_value is not None:
119
+ return env_value
120
+
121
+ # Legacy environment variable mappings
122
+ legacy_mappings = {
123
+ 'aws.accounts': 'AWS_ACCOUNTS',
124
+ 'aws.regions': 'AWS_REGIONS',
125
+ 'aws.cross_account_role': 'AWS_CROSS_ACCOUNT_ROLE',
126
+ 'azure.subscription_id': 'AZURE_SUBSCRIPTION_ID',
127
+ 'azure.tenant_id': 'AZURE_TENANT_ID',
128
+ 'azure.client_id': 'AZURE_CLIENT_ID',
129
+ 'azure.client_secret': 'AZURE_CLIENT_SECRET',
130
+ 'gcp.project_id': 'GCP_PROJECT_ID',
131
+ 'gcp.service_account_key_path': 'GCP_SERVICE_ACCOUNT_KEY_PATH',
132
+ 'cloudflare.email': 'CLOUDFLARE_EMAIL',
133
+ 'cloudflare.api_token': 'CLOUDFLARE_API_TOKEN',
134
+ 'slack.webhook_url': 'SLACK_WEBHOOK_URL',
135
+ }
136
+
137
+ legacy_env_key = legacy_mappings.get(key)
138
+ if legacy_env_key:
139
+ legacy_value = os.getenv(legacy_env_key)
140
+ if legacy_value is not None:
141
+ return legacy_value
142
+
143
+ return default
144
+
145
+ def get_all(self) -> Dict[str, Any]:
146
+ """Get all configuration as dictionary."""
147
+ return self._config_manager.get_config()
148
+
149
+ def reload(self) -> None:
150
+ """Reload configuration from all sources."""
151
+ self._config_manager.load_config()
152
+ self._env_loaded = False
153
+ self._load_env_if_needed()
154
+
155
+
156
+ # Global compatibility config instance
157
+ compat_config = CompatibilityConfig()
158
+
159
+
160
+ def get_env_value(key: str, default: Any = None) -> Any:
161
+ """
162
+ Backward compatibility function for environment variable access.
163
+
164
+ Args:
165
+ key: Environment variable key
166
+ default: Default value if not found
167
+
168
+ Returns:
169
+ Environment variable value or default
170
+ """
171
+ warn_deprecated(
172
+ f"get_env_value('{key}')",
173
+ f"compat_config.get('{key.lower().replace('_', '.')}')",
174
+ "2.0.0"
175
+ )
176
+ return os.getenv(key, default)
177
+
178
+
179
+ def load_dotenv_compat():
180
+ """
181
+ Backward compatibility function for loading .env files.
182
+ """
183
+ warn_deprecated(
184
+ "load_dotenv_compat()",
185
+ "ConfigManager.load_config() with YAML files",
186
+ "2.0.0"
187
+ )
188
+ try:
189
+ from dotenv import load_dotenv
190
+ load_dotenv()
191
+ except ImportError:
192
+ pass
193
+
194
+
195
+ # Compatibility aliases for common functions
196
+ def get_aws_accounts() -> list:
197
+ """Get AWS accounts from configuration."""
198
+ accounts = compat_config.get('aws.accounts', [])
199
+ if isinstance(accounts, str):
200
+ return [acc.strip() for acc in accounts.split(',') if acc.strip()]
201
+ return accounts or []
202
+
203
+
204
+ def get_aws_regions() -> list:
205
+ """Get AWS regions from configuration."""
206
+ regions = compat_config.get('aws.regions', ['ap-northeast-2'])
207
+ if isinstance(regions, str):
208
+ return [reg.strip() for reg in regions.split(',') if reg.strip()]
209
+ return regions
210
+
211
+
212
+ def get_azure_subscription_id() -> Optional[str]:
213
+ """Get Azure subscription ID from configuration."""
214
+ return compat_config.get('azure.subscription_id')
215
+
216
+
217
+ def get_gcp_project_id() -> Optional[str]:
218
+ """Get GCP project ID from configuration."""
219
+ return compat_config.get('gcp.project_id')
220
+
221
+
222
+ def get_slack_webhook_url() -> Optional[str]:
223
+ """Get Slack webhook URL from configuration."""
224
+ return compat_config.get('slack.webhook_url')
225
+
226
+
227
+ # Export compatibility functions
228
+ __all__ = [
229
+ 'get_config_manager',
230
+ 'get_logger',
231
+ 'get_aws_session_manager',
232
+ 'compat_config',
233
+ 'get_env_value',
234
+ 'load_dotenv_compat',
235
+ 'get_aws_accounts',
236
+ 'get_aws_regions',
237
+ 'get_azure_subscription_id',
238
+ 'get_gcp_project_id',
239
+ 'get_slack_webhook_url',
240
+ 'warn_deprecated',
241
+ ]
ic/compat/cli.py ADDED
@@ -0,0 +1,289 @@
1
+ """
2
+ Backward compatibility layer for CLI functionality.
3
+
4
+ This module ensures that existing CLI commands continue to work while
5
+ providing migration path to new configuration system.
6
+ """
7
+
8
+ import os
9
+ import sys
10
+ import warnings
11
+ from typing import Any, Dict, Optional
12
+ from pathlib import Path
13
+
14
+ # Import compatibility layer
15
+ from . import warn_deprecated, compat_config, get_logger
16
+
17
+
18
+ def ensure_env_compatibility():
19
+ """
20
+ Ensure environment variable compatibility for CLI commands.
21
+
22
+ This function checks for .env files and loads them if the new
23
+ configuration system hasn't been set up yet.
24
+ """
25
+ # Check if new config exists
26
+ config_paths = [
27
+ Path("ic.yaml"),
28
+ Path(".ic/config.yaml"),
29
+ Path("config/config.yaml"),
30
+ Path.home() / ".ic" / "config.yaml",
31
+ ]
32
+
33
+ has_new_config = any(path.exists() for path in config_paths)
34
+
35
+ # If no new config exists, ensure .env is loaded
36
+ if not has_new_config:
37
+ env_file = Path('.env')
38
+ if env_file.exists():
39
+ try:
40
+ from dotenv import load_dotenv
41
+ load_dotenv()
42
+
43
+ # Issue one-time warning about migration
44
+ if not os.getenv('IC_MIGRATION_WARNING_SHOWN'):
45
+ logger = get_logger()
46
+ logger.log_info_file_only(
47
+ "Using .env file for configuration. "
48
+ "Consider migrating to YAML configuration with 'ic config migrate'"
49
+ )
50
+ os.environ['IC_MIGRATION_WARNING_SHOWN'] = '1'
51
+
52
+ except ImportError:
53
+ warnings.warn(
54
+ "python-dotenv not installed. .env file cannot be loaded. "
55
+ "Install with: pip install python-dotenv",
56
+ ImportWarning
57
+ )
58
+
59
+
60
+ def wrap_command_function(original_func):
61
+ """
62
+ Decorator to wrap existing command functions with compatibility layer.
63
+
64
+ Args:
65
+ original_func: Original command function
66
+
67
+ Returns:
68
+ Wrapped function with compatibility features
69
+ """
70
+ def wrapper(args):
71
+ # Ensure environment compatibility
72
+ ensure_env_compatibility()
73
+
74
+ # Add compatibility attributes to args if needed
75
+ if not hasattr(args, '_ic_compat_wrapped'):
76
+ args._ic_compat_wrapped = True
77
+
78
+ # Add configuration access to args
79
+ args.config = compat_config
80
+
81
+ # Add logger access to args
82
+ args.logger = get_logger()
83
+
84
+ # Call original function
85
+ return original_func(args)
86
+
87
+ return wrapper
88
+
89
+
90
+ def get_legacy_env_vars() -> Dict[str, str]:
91
+ """
92
+ Get legacy environment variables that might be needed for compatibility.
93
+
94
+ Returns:
95
+ Dictionary of legacy environment variables
96
+ """
97
+ legacy_vars = {}
98
+
99
+ # AWS legacy variables
100
+ aws_vars = [
101
+ 'AWS_PROFILE', 'AWS_REGION', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY',
102
+ 'AWS_SESSION_TOKEN', 'AWS_ACCOUNTS', 'AWS_REGIONS', 'AWS_CROSS_ACCOUNT_ROLE'
103
+ ]
104
+
105
+ # Azure legacy variables
106
+ azure_vars = [
107
+ 'AZURE_SUBSCRIPTION_ID', 'AZURE_TENANT_ID', 'AZURE_CLIENT_ID',
108
+ 'AZURE_CLIENT_SECRET', 'AZURE_SUBSCRIPTIONS', 'AZURE_LOCATIONS'
109
+ ]
110
+
111
+ # GCP legacy variables
112
+ gcp_vars = [
113
+ 'GCP_PROJECT_ID', 'GCP_PROJECTS', 'GCP_REGIONS', 'GCP_ZONES',
114
+ 'GCP_SERVICE_ACCOUNT_KEY_PATH', 'GOOGLE_APPLICATION_CREDENTIALS'
115
+ ]
116
+
117
+ # CloudFlare legacy variables
118
+ cf_vars = [
119
+ 'CLOUDFLARE_EMAIL', 'CLOUDFLARE_API_TOKEN', 'CLOUDFLARE_ACCOUNTS', 'CLOUDFLARE_ZONES'
120
+ ]
121
+
122
+ # Other legacy variables
123
+ other_vars = [
124
+ 'SLACK_WEBHOOK_URL', 'SSH_CONFIG_FILE', 'SSH_KEY_DIR', 'OCI_CONFIG_PATH'
125
+ ]
126
+
127
+ all_vars = aws_vars + azure_vars + gcp_vars + cf_vars + other_vars
128
+
129
+ for var in all_vars:
130
+ value = os.getenv(var)
131
+ if value:
132
+ legacy_vars[var] = value
133
+
134
+ return legacy_vars
135
+
136
+
137
+ def migrate_env_to_config_hint():
138
+ """
139
+ Provide hint about migrating from .env to config files.
140
+ """
141
+ env_file = Path('.env')
142
+ if env_file.exists():
143
+ config_paths = [
144
+ Path("ic.yaml"),
145
+ Path(".ic/config.yaml"),
146
+ Path("config/config.yaml"),
147
+ ]
148
+
149
+ has_config = any(path.exists() for path in config_paths)
150
+
151
+ if not has_config:
152
+ logger = get_logger()
153
+ logger.log_info_file_only(
154
+ "💡 Tip: Migrate from .env to YAML configuration for better security and features. "
155
+ "Run 'ic config migrate' to get started."
156
+ )
157
+
158
+
159
+ def check_deprecated_imports():
160
+ """
161
+ Check for deprecated import patterns in the current execution.
162
+ """
163
+ # This is called during CLI startup to check for deprecated usage
164
+ frame = sys._getframe(1)
165
+
166
+ # Check if we're being imported from old paths
167
+ if frame and frame.f_code:
168
+ filename = frame.f_code.co_filename
169
+ if 'common/log.py' in filename or 'common/gather_env.py' in filename:
170
+ warn_deprecated(
171
+ "importing from common.* modules",
172
+ "importing from ic.compat or using new configuration system",
173
+ "2.0.0"
174
+ )
175
+
176
+
177
+ def setup_cli_compatibility():
178
+ """
179
+ Set up CLI compatibility features.
180
+
181
+ This function should be called early in CLI initialization to ensure
182
+ backward compatibility features are available.
183
+ """
184
+ # Ensure environment compatibility
185
+ ensure_env_compatibility()
186
+
187
+ # Check for deprecated imports
188
+ check_deprecated_imports()
189
+
190
+ # Provide migration hints
191
+ migrate_env_to_config_hint()
192
+
193
+ # Set up global compatibility state
194
+ if not hasattr(sys.modules[__name__], '_cli_compat_initialized'):
195
+ sys.modules[__name__]._cli_compat_initialized = True
196
+
197
+ # Log compatibility mode activation
198
+ logger = get_logger()
199
+ logger.log_info_file_only("CLI compatibility layer activated")
200
+
201
+
202
+ def get_command_config(command_name: str) -> Dict[str, Any]:
203
+ """
204
+ Get configuration for a specific command with backward compatibility.
205
+
206
+ Args:
207
+ command_name: Name of the command
208
+
209
+ Returns:
210
+ Configuration dictionary for the command
211
+ """
212
+ # Get base configuration
213
+ config = compat_config.get_all()
214
+
215
+ # Add command-specific compatibility mappings
216
+ if command_name.startswith('aws'):
217
+ # Ensure AWS configuration is available
218
+ if not config.get('aws', {}).get('accounts'):
219
+ accounts_env = os.getenv('AWS_ACCOUNTS')
220
+ if accounts_env:
221
+ if 'aws' not in config:
222
+ config['aws'] = {}
223
+ config['aws']['accounts'] = [acc.strip() for acc in accounts_env.split(',')]
224
+
225
+ elif command_name.startswith('azure'):
226
+ # Ensure Azure configuration is available
227
+ if not config.get('azure', {}).get('subscription_id'):
228
+ sub_id = os.getenv('AZURE_SUBSCRIPTION_ID')
229
+ if sub_id:
230
+ if 'azure' not in config:
231
+ config['azure'] = {}
232
+ config['azure']['subscription_id'] = sub_id
233
+
234
+ elif command_name.startswith('gcp'):
235
+ # Ensure GCP configuration is available
236
+ if not config.get('gcp', {}).get('project_id'):
237
+ project_id = os.getenv('GCP_PROJECT_ID')
238
+ if project_id:
239
+ if 'gcp' not in config:
240
+ config['gcp'] = {}
241
+ config['gcp']['project_id'] = project_id
242
+
243
+ return config
244
+
245
+
246
+ def handle_missing_config(service: str) -> None:
247
+ """
248
+ Handle missing configuration for a service with helpful error messages.
249
+
250
+ Args:
251
+ service: Name of the service (aws, azure, gcp, etc.)
252
+ """
253
+ logger = get_logger()
254
+
255
+ error_messages = {
256
+ 'aws': (
257
+ "AWS configuration not found. Please either:\n"
258
+ "1. Create a YAML config file with AWS settings, or\n"
259
+ "2. Set AWS_ACCOUNTS environment variable, or\n"
260
+ "3. Run 'ic config init' to set up configuration"
261
+ ),
262
+ 'azure': (
263
+ "Azure configuration not found. Please either:\n"
264
+ "1. Create a YAML config file with Azure settings, or\n"
265
+ "2. Set AZURE_SUBSCRIPTION_ID environment variable, or\n"
266
+ "3. Run 'ic config init' to set up configuration"
267
+ ),
268
+ 'gcp': (
269
+ "GCP configuration not found. Please either:\n"
270
+ "1. Create a YAML config file with GCP settings, or\n"
271
+ "2. Set GCP_PROJECT_ID environment variable, or\n"
272
+ "3. Run 'ic config init' to set up configuration"
273
+ ),
274
+ }
275
+
276
+ message = error_messages.get(service, f"{service} configuration not found")
277
+ logger.log_error(message)
278
+
279
+
280
+ # Export compatibility functions
281
+ __all__ = [
282
+ 'ensure_env_compatibility',
283
+ 'wrap_command_function',
284
+ 'get_legacy_env_vars',
285
+ 'migrate_env_to_config_hint',
286
+ 'setup_cli_compatibility',
287
+ 'get_command_config',
288
+ 'handle_missing_config',
289
+ ]