codemie-test-harness 0.1.168__py3-none-any.whl → 0.1.170__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.
Potentially problematic release.
This version of codemie-test-harness might be problematic. Click here for more details.
- codemie_test_harness/cli/cli.py +18 -74
- codemie_test_harness/cli/commands/assistant_cmd.py +104 -0
- codemie_test_harness/cli/commands/config_cmd.py +610 -20
- codemie_test_harness/cli/commands/workflow_cmd.py +64 -0
- codemie_test_harness/cli/constants.py +385 -6
- codemie_test_harness/cli/utils.py +9 -0
- codemie_test_harness/tests/test_data/assistant_test_data.py +197 -0
- codemie_test_harness/tests/test_data/project_management_test_data.py +1 -1
- codemie_test_harness/tests/ui/assistants/__init__.py +0 -0
- codemie_test_harness/tests/ui/assistants/test_create_assistant.py +408 -0
- codemie_test_harness/tests/ui/conftest.py +23 -3
- codemie_test_harness/tests/ui/pageobject/assistants/assistants_page.py +3 -4
- codemie_test_harness/tests/ui/pageobject/assistants/create_assistant_page.py +689 -0
- codemie_test_harness/tests/ui/pageobject/assistants/generate_with_ai_modal.py +367 -0
- codemie_test_harness/tests/ui/pageobject/base_page.py +2 -2
- codemie_test_harness/tests/ui/pytest.ini +18 -0
- codemie_test_harness/tests/ui/workflows/test_workflows.py +1 -1
- codemie_test_harness/tests/utils/credentials_manager.py +0 -15
- {codemie_test_harness-0.1.168.dist-info → codemie_test_harness-0.1.170.dist-info}/METADATA +2 -2
- {codemie_test_harness-0.1.168.dist-info → codemie_test_harness-0.1.170.dist-info}/RECORD +22 -14
- {codemie_test_harness-0.1.168.dist-info → codemie_test_harness-0.1.170.dist-info}/WHEEL +0 -0
- {codemie_test_harness-0.1.168.dist-info → codemie_test_harness-0.1.170.dist-info}/entry_points.txt +0 -0
|
@@ -1,48 +1,638 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import click
|
|
3
|
-
from
|
|
4
|
-
from
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
from typing import Dict, List
|
|
5
|
+
from ..constants import (
|
|
6
|
+
CONSOLE,
|
|
7
|
+
CREDENTIAL_CATEGORIES,
|
|
8
|
+
INTEGRATION_KEYS,
|
|
9
|
+
mask_sensitive_value,
|
|
10
|
+
is_sensitive_key,
|
|
11
|
+
)
|
|
12
|
+
from ..utils import (
|
|
13
|
+
load_config,
|
|
14
|
+
get_config_value,
|
|
15
|
+
set_config_value,
|
|
16
|
+
save_config,
|
|
17
|
+
unset_config_key,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Helper functions (moved from integrations_manager.py)
|
|
22
|
+
def _get_integration_category(key: str) -> str:
|
|
23
|
+
"""Get the integration category name for a configuration key."""
|
|
24
|
+
for category_id, category_info in CREDENTIAL_CATEGORIES.items():
|
|
25
|
+
if key in category_info["keys"]:
|
|
26
|
+
return category_info["name"]
|
|
27
|
+
return "Unknown"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _show_integration_configs(category: str = None, show_real: bool = False) -> None:
|
|
31
|
+
"""Display current integration configurations in a formatted table."""
|
|
32
|
+
config = load_config()
|
|
33
|
+
if not config:
|
|
34
|
+
CONSOLE.print("[yellow]No integration configurations found[/]")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
# Filter configurations based on category parameter
|
|
38
|
+
if category:
|
|
39
|
+
if category in CREDENTIAL_CATEGORIES:
|
|
40
|
+
keys_to_show = CREDENTIAL_CATEGORIES[category]["keys"]
|
|
41
|
+
filtered_config = {k: v for k, v in config.items() if k in keys_to_show}
|
|
42
|
+
else:
|
|
43
|
+
CONSOLE.print(f"[red]Unknown integration category: {category}[/]")
|
|
44
|
+
return
|
|
45
|
+
else:
|
|
46
|
+
# Show all integration configurations
|
|
47
|
+
filtered_config = {k: v for k, v in config.items() if k in INTEGRATION_KEYS}
|
|
48
|
+
|
|
49
|
+
if not filtered_config:
|
|
50
|
+
CONSOLE.print(
|
|
51
|
+
"[yellow]No integration configurations found for the specified criteria[/]"
|
|
52
|
+
)
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
# Create formatted table for display
|
|
56
|
+
title = "Integration Configurations" + (
|
|
57
|
+
" (Real Values)" if show_real else " (Masked Values)"
|
|
58
|
+
)
|
|
59
|
+
table = Table(title=title)
|
|
60
|
+
table.add_column("Configuration Key", style="cyan")
|
|
61
|
+
table.add_column("Value", style="green")
|
|
62
|
+
table.add_column("Integration Category", style="blue")
|
|
63
|
+
|
|
64
|
+
for key, value in sorted(filtered_config.items()):
|
|
65
|
+
display_value = (
|
|
66
|
+
mask_sensitive_value(str(value), show_real=show_real)
|
|
67
|
+
if is_sensitive_key(key)
|
|
68
|
+
else str(value)
|
|
69
|
+
)
|
|
70
|
+
category_name = _get_integration_category(key)
|
|
71
|
+
table.add_row(key, display_value, category_name)
|
|
72
|
+
|
|
73
|
+
CONSOLE.print(table)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _list_integration_categories() -> None:
|
|
77
|
+
"""Display all available integration categories in a formatted table."""
|
|
78
|
+
table = Table(title="Available Integration Categories")
|
|
79
|
+
table.add_column("Category ID", style="cyan")
|
|
80
|
+
table.add_column("Category Name", style="green")
|
|
81
|
+
table.add_column("Description", style="blue")
|
|
82
|
+
table.add_column("Config Keys", style="dim", justify="right")
|
|
83
|
+
|
|
84
|
+
for category_id, category_info in CREDENTIAL_CATEGORIES.items():
|
|
85
|
+
table.add_row(
|
|
86
|
+
category_id,
|
|
87
|
+
category_info["name"],
|
|
88
|
+
category_info["description"],
|
|
89
|
+
str(len(category_info["keys"])),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
CONSOLE.print(table)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _validate_integrations() -> Dict[str, Dict[str, List[str]]]:
|
|
96
|
+
"""Validate all configured integrations and return detailed status."""
|
|
97
|
+
config = load_config()
|
|
98
|
+
validation_results = {}
|
|
99
|
+
|
|
100
|
+
for category_id, category_info in CREDENTIAL_CATEGORIES.items():
|
|
101
|
+
configured_keys = []
|
|
102
|
+
missing_keys = []
|
|
103
|
+
|
|
104
|
+
for config_key in category_info["keys"]:
|
|
105
|
+
if config and config_key in config and config[config_key]:
|
|
106
|
+
configured_keys.append(config_key)
|
|
107
|
+
else:
|
|
108
|
+
missing_keys.append(config_key)
|
|
109
|
+
|
|
110
|
+
validation_results[category_info["name"]] = {
|
|
111
|
+
"configured": configured_keys,
|
|
112
|
+
"missing": missing_keys,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return validation_results
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _setup_integration_category(category: str) -> None:
|
|
119
|
+
"""Interactively configure all settings for a specific integration category."""
|
|
120
|
+
if category not in CREDENTIAL_CATEGORIES:
|
|
121
|
+
CONSOLE.print(f"[red]Unknown integration category: {category}[/]")
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
category_info = CREDENTIAL_CATEGORIES[category]
|
|
125
|
+
CONSOLE.print(f"\n=== {category_info['name']} Configuration Setup ===")
|
|
126
|
+
CONSOLE.print(f"Description: {category_info['description']}\n")
|
|
127
|
+
CONSOLE.print("[dim]Press Enter to skip a setting, or Ctrl+C to cancel setup[/]\n")
|
|
128
|
+
|
|
129
|
+
config = load_config()
|
|
130
|
+
updated_count = 0
|
|
131
|
+
|
|
132
|
+
for config_key in category_info["keys"]:
|
|
133
|
+
current_value = config.get(config_key, "")
|
|
134
|
+
|
|
135
|
+
# Show current value (masked if sensitive)
|
|
136
|
+
current_display = ""
|
|
137
|
+
if current_value:
|
|
138
|
+
displayed_value = (
|
|
139
|
+
mask_sensitive_value(current_value)
|
|
140
|
+
if is_sensitive_key(config_key)
|
|
141
|
+
else current_value
|
|
142
|
+
)
|
|
143
|
+
current_display = f" (current: {displayed_value})"
|
|
144
|
+
|
|
145
|
+
prompt = f"{config_key}{current_display}: "
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
user_input = input(prompt).strip()
|
|
149
|
+
|
|
150
|
+
if user_input:
|
|
151
|
+
set_config_value(config_key, user_input)
|
|
152
|
+
updated_count += 1
|
|
153
|
+
CONSOLE.print(f"[green]✓ Configured[/] {config_key}")
|
|
154
|
+
else:
|
|
155
|
+
CONSOLE.print(f"[yellow]⚠ Skipped[/] {config_key}")
|
|
156
|
+
|
|
157
|
+
except KeyboardInterrupt:
|
|
158
|
+
CONSOLE.print("\n[yellow]Configuration setup cancelled.[/]")
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
CONSOLE.print(
|
|
162
|
+
f"\n[green]Successfully updated {updated_count} integration setting(s)[/]"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _setup_all_integration_categories() -> None:
|
|
167
|
+
"""Interactively configure all available integration categories."""
|
|
168
|
+
CONSOLE.print("[cyan]Setting up all integration categories...\n[/]")
|
|
169
|
+
|
|
170
|
+
total_categories = len(CREDENTIAL_CATEGORIES)
|
|
171
|
+
current_category = 0
|
|
172
|
+
|
|
173
|
+
for category_id in CREDENTIAL_CATEGORIES.keys():
|
|
174
|
+
current_category += 1
|
|
175
|
+
category_name = CREDENTIAL_CATEGORIES[category_id]["name"]
|
|
176
|
+
|
|
177
|
+
CONSOLE.print(f"\n[dim]({current_category}/{total_categories})[/]")
|
|
178
|
+
proceed = input(f"Configure {category_name}? (y/N): ").strip().lower()
|
|
179
|
+
|
|
180
|
+
if proceed in ["y", "yes"]:
|
|
181
|
+
_setup_integration_category(category_id)
|
|
182
|
+
else:
|
|
183
|
+
CONSOLE.print(f"[yellow]⚠ Skipped {category_name}[/]")
|
|
184
|
+
|
|
185
|
+
CONSOLE.print("\n[green]🎉 All integration categories setup completed![/]")
|
|
5
186
|
|
|
6
187
|
|
|
7
188
|
@click.group(name="config")
|
|
8
189
|
def config_cmd():
|
|
9
|
-
"""Manage configuration for test harness.
|
|
190
|
+
"""Manage configuration and credentials for test harness.
|
|
10
191
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
192
|
+
Supports setting individual credentials, category-based interactive setup,
|
|
193
|
+
validation, and more.
|
|
194
|
+
|
|
195
|
+
Categories:
|
|
196
|
+
- version-control: GitLab, GitHub
|
|
197
|
+
- project-management: JIRA, Confluence
|
|
198
|
+
- cloud-providers: AWS, Azure, GCP
|
|
199
|
+
- development-tools: Azure DevOps, ServiceNow, Keycloak, SonarQube
|
|
200
|
+
- notifications: Email, OAuth, Telegram
|
|
201
|
+
- research-tools: Kubernetes, Report Portal, Elasticsearch
|
|
202
|
+
- data-management: LiteLLM, SQL databases
|
|
19
203
|
"""
|
|
20
204
|
pass
|
|
21
205
|
|
|
22
206
|
|
|
23
207
|
@config_cmd.command(name="list")
|
|
24
|
-
|
|
208
|
+
@click.option(
|
|
209
|
+
"--show-values",
|
|
210
|
+
is_flag=True,
|
|
211
|
+
help="Show real values instead of masked values (use with caution)",
|
|
212
|
+
)
|
|
213
|
+
def config_list(show_values: bool = False):
|
|
214
|
+
"""List all configuration values.
|
|
215
|
+
|
|
216
|
+
Examples:
|
|
217
|
+
codemie-test-harness config list
|
|
218
|
+
codemie-test-harness config list --show-values
|
|
219
|
+
"""
|
|
25
220
|
cfg = load_config()
|
|
26
221
|
if not cfg:
|
|
27
222
|
CONSOLE.print("[yellow]No config set yet[/]")
|
|
28
223
|
else:
|
|
224
|
+
title_suffix = " (Real Values)" if show_values else " (Masked Values)"
|
|
225
|
+
CONSOLE.print(f"[bold cyan]Configuration{title_suffix}[/bold cyan]")
|
|
29
226
|
for k, v in cfg.items():
|
|
30
|
-
|
|
227
|
+
display_value = (
|
|
228
|
+
mask_sensitive_value(str(v), show_real=show_values)
|
|
229
|
+
if is_sensitive_key(k)
|
|
230
|
+
else str(v)
|
|
231
|
+
)
|
|
232
|
+
CONSOLE.print(f"[cyan]{k}[/] = [green]{display_value}[/]")
|
|
31
233
|
|
|
32
234
|
|
|
33
235
|
@config_cmd.command(name="set")
|
|
34
|
-
@click.argument("key")
|
|
35
|
-
@click.argument("value")
|
|
36
|
-
def config_set(key: str, value: str):
|
|
37
|
-
|
|
38
|
-
|
|
236
|
+
@click.argument("key", required=False)
|
|
237
|
+
@click.argument("value", required=False)
|
|
238
|
+
def config_set(key: str = None, value: str = None):
|
|
239
|
+
"""Set configuration values using key-value pairs.
|
|
240
|
+
|
|
241
|
+
Supports all 86+ environment variables across 10 categories.
|
|
242
|
+
|
|
243
|
+
Examples:
|
|
244
|
+
codemie-test-harness config set GITLAB_TOKEN glpat-xxx
|
|
245
|
+
codemie-test-harness config set SONAR_URL https://sonar.example.com
|
|
246
|
+
codemie-test-harness config set KUBERNETES_TOKEN k8s-token-123
|
|
247
|
+
"""
|
|
248
|
+
# If key-value pair provided
|
|
249
|
+
if key and value:
|
|
250
|
+
set_config_value(key, value)
|
|
251
|
+
display_value = mask_sensitive_value(value) if is_sensitive_key(key) else value
|
|
252
|
+
CONSOLE.print(f"[green]Saved[/] {key} = {display_value}")
|
|
253
|
+
else:
|
|
254
|
+
CONSOLE.print(
|
|
255
|
+
"[yellow]Please provide key and value: codemie-test-harness config set KEY VALUE[/yellow]"
|
|
256
|
+
)
|
|
257
|
+
CONSOLE.print("\n[cyan]Examples:[/cyan]")
|
|
258
|
+
CONSOLE.print(" codemie-test-harness config set GITLAB_TOKEN glpat-xxx")
|
|
259
|
+
CONSOLE.print(
|
|
260
|
+
" codemie-test-harness config set SONAR_URL https://sonar.example.com"
|
|
261
|
+
)
|
|
262
|
+
CONSOLE.print(
|
|
263
|
+
" codemie-test-harness config set AWS_ACCESS_KEY_ID AKIA1234567890"
|
|
264
|
+
)
|
|
265
|
+
CONSOLE.print(
|
|
266
|
+
"\n[dim]💡 Use 'codemie-test-harness config vars' to see all available variables[/dim]"
|
|
267
|
+
)
|
|
39
268
|
|
|
40
269
|
|
|
41
270
|
@config_cmd.command(name="get")
|
|
42
271
|
@click.argument("key")
|
|
43
|
-
|
|
272
|
+
@click.option(
|
|
273
|
+
"--show-value",
|
|
274
|
+
is_flag=True,
|
|
275
|
+
help="Show real value instead of masked value (use with caution)",
|
|
276
|
+
)
|
|
277
|
+
def config_get(key: str, show_value: bool = False):
|
|
278
|
+
"""Get a configuration value.
|
|
279
|
+
|
|
280
|
+
Examples:
|
|
281
|
+
codemie-test-harness config get GITLAB_TOKEN
|
|
282
|
+
codemie-test-harness config get GITLAB_TOKEN --show-value
|
|
283
|
+
"""
|
|
44
284
|
val = get_config_value(key)
|
|
45
285
|
if val is None:
|
|
46
286
|
CONSOLE.print(f"[yellow]{key} not set[/]")
|
|
47
287
|
else:
|
|
48
|
-
|
|
288
|
+
display_value = (
|
|
289
|
+
mask_sensitive_value(val, show_real=show_value)
|
|
290
|
+
if is_sensitive_key(key)
|
|
291
|
+
else val
|
|
292
|
+
)
|
|
293
|
+
CONSOLE.print(f"{key} = {display_value}")
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
@config_cmd.command(name="integrations")
|
|
297
|
+
@click.option(
|
|
298
|
+
"--category",
|
|
299
|
+
type=click.Choice(list(CREDENTIAL_CATEGORIES.keys())),
|
|
300
|
+
help="Show credentials for specific category",
|
|
301
|
+
)
|
|
302
|
+
@click.option(
|
|
303
|
+
"--show-values",
|
|
304
|
+
is_flag=True,
|
|
305
|
+
help="Show real values instead of masked values (use with caution)",
|
|
306
|
+
)
|
|
307
|
+
def config_integrations(category: str = None, show_values: bool = False):
|
|
308
|
+
"""Show current credentials.
|
|
309
|
+
|
|
310
|
+
Examples:
|
|
311
|
+
codemie-test-harness config integrations
|
|
312
|
+
codemie-test-harness config integrations --category version-control
|
|
313
|
+
codemie-test-harness config integrations --show-values
|
|
314
|
+
codemie-test-harness config integrations --category cloud --show-values
|
|
315
|
+
"""
|
|
316
|
+
_show_integration_configs(category=category, show_real=show_values)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@config_cmd.command(name="unset")
|
|
320
|
+
@click.option("--keys", help="Comma-separated list of keys to unset (case-insensitive)")
|
|
321
|
+
@click.option(
|
|
322
|
+
"--category",
|
|
323
|
+
type=click.Choice(list(CREDENTIAL_CATEGORIES.keys())),
|
|
324
|
+
help="Category to unset all keys from",
|
|
325
|
+
)
|
|
326
|
+
def config_unset(keys: str = None, category: str = None):
|
|
327
|
+
"""Unset (remove) configuration keys.
|
|
328
|
+
|
|
329
|
+
Supports case-insensitive key removal and category-based removal.
|
|
330
|
+
|
|
331
|
+
Examples:
|
|
332
|
+
codemie-test-harness config unset --keys GITLAB_TOKEN,gitlab_url
|
|
333
|
+
codemie-test-harness config unset --keys some_setting,DEBUG_LEVEL
|
|
334
|
+
codemie-test-harness config unset --category version-control
|
|
335
|
+
"""
|
|
336
|
+
if not keys and not category:
|
|
337
|
+
CONSOLE.print("[yellow]Please specify --keys or --category[/yellow]")
|
|
338
|
+
return
|
|
339
|
+
|
|
340
|
+
config = load_config()
|
|
341
|
+
if not config:
|
|
342
|
+
CONSOLE.print("[yellow]No configuration found[/yellow]")
|
|
343
|
+
return
|
|
344
|
+
|
|
345
|
+
removed_count = 0
|
|
346
|
+
|
|
347
|
+
# Handle category removal
|
|
348
|
+
if category:
|
|
349
|
+
category_info = CREDENTIAL_CATEGORIES[category]
|
|
350
|
+
category_keys = category_info["keys"]
|
|
351
|
+
|
|
352
|
+
for key in category_keys:
|
|
353
|
+
if unset_config_key(key):
|
|
354
|
+
removed_count += 1
|
|
355
|
+
|
|
356
|
+
CONSOLE.print(
|
|
357
|
+
f"[green]category:{category_info['name']}: Removed {removed_count} keys[/green]"
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# Handle specific key removal (case-insensitive)
|
|
361
|
+
if keys:
|
|
362
|
+
key_list = [k.strip() for k in keys.split(",")]
|
|
363
|
+
config = load_config() # Reload config in case category removal happened
|
|
364
|
+
config_keys_lower = {k.lower(): k for k in config.keys()}
|
|
365
|
+
|
|
366
|
+
for key in key_list:
|
|
367
|
+
key_lower = key.lower()
|
|
368
|
+
if key_lower in config_keys_lower:
|
|
369
|
+
actual_key = config_keys_lower[key_lower]
|
|
370
|
+
if unset_config_key(actual_key):
|
|
371
|
+
removed_count += 1
|
|
372
|
+
CONSOLE.print(f"[green]{key}: Removed (was: {actual_key})[/green]")
|
|
373
|
+
else:
|
|
374
|
+
CONSOLE.print(f"[yellow]{key}: Not found[/yellow]")
|
|
375
|
+
|
|
376
|
+
if removed_count > 0:
|
|
377
|
+
CONSOLE.print(f"[green]Total keys removed: {removed_count}[/green]")
|
|
378
|
+
else:
|
|
379
|
+
CONSOLE.print("[yellow]No keys were removed[/yellow]")
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
@config_cmd.command(name="setup")
|
|
383
|
+
@click.option(
|
|
384
|
+
"--category",
|
|
385
|
+
type=click.Choice(list(CREDENTIAL_CATEGORIES.keys())),
|
|
386
|
+
help="Setup specific category interactively",
|
|
387
|
+
)
|
|
388
|
+
@click.option(
|
|
389
|
+
"--all", "setup_all", is_flag=True, help="Setup all categories interactively"
|
|
390
|
+
)
|
|
391
|
+
def config_setup(category: str = None, setup_all: bool = False):
|
|
392
|
+
"""Interactive credential setup.
|
|
393
|
+
|
|
394
|
+
Examples:
|
|
395
|
+
codemie-test-harness config setup --category version-control
|
|
396
|
+
codemie-test-harness config setup --category cloud-providers
|
|
397
|
+
codemie-test-harness config setup --all
|
|
398
|
+
"""
|
|
399
|
+
if setup_all:
|
|
400
|
+
_setup_all_integration_categories()
|
|
401
|
+
elif category:
|
|
402
|
+
_setup_integration_category(category)
|
|
403
|
+
else:
|
|
404
|
+
CONSOLE.print("[yellow]Please specify --category or --all[/yellow]")
|
|
405
|
+
CONSOLE.print("\nAvailable categories:")
|
|
406
|
+
_list_integration_categories()
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
@config_cmd.command(name="validate")
|
|
410
|
+
@click.option(
|
|
411
|
+
"--category",
|
|
412
|
+
type=click.Choice(list(CREDENTIAL_CATEGORIES.keys())),
|
|
413
|
+
help="Validate specific category",
|
|
414
|
+
)
|
|
415
|
+
def config_validate(category: str = None):
|
|
416
|
+
"""Validate configured credentials.
|
|
417
|
+
|
|
418
|
+
Examples:
|
|
419
|
+
codemie-test-harness config validate
|
|
420
|
+
codemie-test-harness config validate --category cloud-providers
|
|
421
|
+
"""
|
|
422
|
+
|
|
423
|
+
validation_results = _validate_integrations()
|
|
424
|
+
|
|
425
|
+
# Display validation results
|
|
426
|
+
if not validation_results:
|
|
427
|
+
CONSOLE.print("[yellow]No credentials configured[/yellow]")
|
|
428
|
+
return
|
|
429
|
+
|
|
430
|
+
# Filter by category if specified
|
|
431
|
+
if category:
|
|
432
|
+
if category in CREDENTIAL_CATEGORIES:
|
|
433
|
+
category_name = CREDENTIAL_CATEGORIES[category]["name"]
|
|
434
|
+
if category_name in validation_results:
|
|
435
|
+
validation_results = {category_name: validation_results[category_name]}
|
|
436
|
+
else:
|
|
437
|
+
validation_results = {}
|
|
438
|
+
else:
|
|
439
|
+
CONSOLE.print(f"[red]Unknown category: {category}[/red]")
|
|
440
|
+
return
|
|
441
|
+
|
|
442
|
+
# Create table
|
|
443
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
444
|
+
table.add_column("Category", style="cyan", no_wrap=True, width=25)
|
|
445
|
+
table.add_column("Credential", style="white", width=35)
|
|
446
|
+
table.add_column("Status", justify="center", width=15)
|
|
447
|
+
|
|
448
|
+
total_configured = 0
|
|
449
|
+
total_missing = 0
|
|
450
|
+
|
|
451
|
+
# Sort categories for consistent display
|
|
452
|
+
sorted_categories = sorted(validation_results.items())
|
|
453
|
+
|
|
454
|
+
for i, (category_name, results) in enumerate(sorted_categories):
|
|
455
|
+
configured = results["configured"]
|
|
456
|
+
missing = results["missing"]
|
|
457
|
+
|
|
458
|
+
total_configured += len(configured)
|
|
459
|
+
total_missing += len(missing)
|
|
460
|
+
|
|
461
|
+
# Add configured credentials
|
|
462
|
+
for j, key in enumerate(sorted(configured)):
|
|
463
|
+
cat_display = category_name if j == 0 else ""
|
|
464
|
+
table.add_row(cat_display, key, "[green]✓ Configured[/green]")
|
|
465
|
+
|
|
466
|
+
# Add missing credentials
|
|
467
|
+
for j, key in enumerate(sorted(missing)):
|
|
468
|
+
cat_display = category_name if j == 0 and not configured else ""
|
|
469
|
+
table.add_row(cat_display, key, "[yellow]⚠ Missing[/yellow]")
|
|
470
|
+
|
|
471
|
+
# Add separator row between categories (except for last category)
|
|
472
|
+
if i < len(sorted_categories) - 1 and (configured or missing):
|
|
473
|
+
table.add_row("", "", "", style="dim")
|
|
474
|
+
|
|
475
|
+
# Display table
|
|
476
|
+
CONSOLE.print("\n[bold blue]Credential Validation Results[/bold blue]")
|
|
477
|
+
CONSOLE.print(table)
|
|
478
|
+
|
|
479
|
+
# Summary
|
|
480
|
+
CONSOLE.print("\n[bold]Summary:[/bold]")
|
|
481
|
+
CONSOLE.print(f" [green]Configured: {total_configured}[/green]")
|
|
482
|
+
CONSOLE.print(f" [yellow]Missing: {total_missing}[/yellow]")
|
|
483
|
+
|
|
484
|
+
if total_missing == 0 and total_configured > 0:
|
|
485
|
+
CONSOLE.print("\n[green]✅ All credentials are configured![/green]")
|
|
486
|
+
elif total_configured == 0:
|
|
487
|
+
CONSOLE.print(
|
|
488
|
+
"\n[yellow]⚠️ No credentials are configured. Use 'codemie-test-harness config setup' to get started.[/yellow]"
|
|
489
|
+
)
|
|
490
|
+
else:
|
|
491
|
+
CONSOLE.print(
|
|
492
|
+
"\n[yellow]⚠️ Some credentials are still missing. Use 'codemie-test-harness config setup' to configure them.[/yellow]"
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
@config_cmd.command(name="categories")
|
|
497
|
+
@click.option(
|
|
498
|
+
"--list-vars",
|
|
499
|
+
"-l",
|
|
500
|
+
is_flag=True,
|
|
501
|
+
help="Show environment variables for each category",
|
|
502
|
+
)
|
|
503
|
+
@click.option("--category", "-c", help="Show variables for specific category only")
|
|
504
|
+
def config_categories(list_vars: bool = False, category: str = None):
|
|
505
|
+
"""List available credential categories and optionally their environment variables."""
|
|
506
|
+
from ..constants import CREDENTIAL_CATEGORIES
|
|
507
|
+
from rich.console import Console
|
|
508
|
+
from rich.table import Table
|
|
509
|
+
|
|
510
|
+
console = Console()
|
|
511
|
+
|
|
512
|
+
if category:
|
|
513
|
+
# Show variables for specific category
|
|
514
|
+
if category not in CREDENTIAL_CATEGORIES:
|
|
515
|
+
click.echo(
|
|
516
|
+
f"❌ Category '{category}' not found. Available categories: {', '.join(CREDENTIAL_CATEGORIES.keys())}"
|
|
517
|
+
)
|
|
518
|
+
return
|
|
519
|
+
|
|
520
|
+
cat_info = CREDENTIAL_CATEGORIES[category]
|
|
521
|
+
click.echo(f"\n=== {cat_info['name']} ===")
|
|
522
|
+
click.echo(f"{cat_info['description']}\n")
|
|
523
|
+
|
|
524
|
+
if list_vars or True: # Always show vars when specific category requested
|
|
525
|
+
click.echo("Environment Variables:")
|
|
526
|
+
for var in sorted(cat_info["keys"]):
|
|
527
|
+
click.echo(f" • {var}")
|
|
528
|
+
return
|
|
529
|
+
|
|
530
|
+
if list_vars:
|
|
531
|
+
# Show all categories with their variables
|
|
532
|
+
for cat_id, cat_info in CREDENTIAL_CATEGORIES.items():
|
|
533
|
+
click.echo(f"\n=== {cat_info['name']} ({cat_id}) ===")
|
|
534
|
+
click.echo(f"{cat_info['description']}")
|
|
535
|
+
click.echo(f"Environment Variables ({len(cat_info['keys'])} total):")
|
|
536
|
+
for var in sorted(cat_info["keys"]):
|
|
537
|
+
click.echo(f" • {var}")
|
|
538
|
+
click.echo()
|
|
539
|
+
else:
|
|
540
|
+
# Show summary table (existing functionality)
|
|
541
|
+
table = Table(title="Available Credential Categories")
|
|
542
|
+
table.add_column("Category", style="cyan")
|
|
543
|
+
table.add_column("Name", style="green")
|
|
544
|
+
table.add_column("Description", style="yellow")
|
|
545
|
+
table.add_column("Keys", justify="right", style="magenta")
|
|
546
|
+
|
|
547
|
+
for cat_id, cat_info in CREDENTIAL_CATEGORIES.items():
|
|
548
|
+
table.add_row(
|
|
549
|
+
cat_id,
|
|
550
|
+
cat_info["name"],
|
|
551
|
+
cat_info["description"],
|
|
552
|
+
str(len(cat_info["keys"])),
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
console.print(table)
|
|
556
|
+
click.echo(
|
|
557
|
+
"\n💡 Use 'codemie-test-harness config vars <category>' to see environment variables for a specific category."
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
@config_cmd.command(name="vars")
|
|
562
|
+
@click.argument("category", required=False)
|
|
563
|
+
def config_vars(category: str = None):
|
|
564
|
+
"""List environment variables for a specific category or all categories."""
|
|
565
|
+
from ..constants import CREDENTIAL_CATEGORIES
|
|
566
|
+
|
|
567
|
+
if category:
|
|
568
|
+
# Show variables for specific category
|
|
569
|
+
if category not in CREDENTIAL_CATEGORIES:
|
|
570
|
+
click.echo(f"❌ Category '{category}' not found.")
|
|
571
|
+
click.echo(
|
|
572
|
+
f"Available categories: {', '.join(CREDENTIAL_CATEGORIES.keys())}"
|
|
573
|
+
)
|
|
574
|
+
return
|
|
575
|
+
|
|
576
|
+
cat_info = CREDENTIAL_CATEGORIES[category]
|
|
577
|
+
click.echo(f"\n=== {cat_info['name']} ===")
|
|
578
|
+
click.echo(f"{cat_info['description']}")
|
|
579
|
+
click.echo(f"\nEnvironment Variables ({len(cat_info['keys'])} total):")
|
|
580
|
+
for var in sorted(cat_info["keys"]):
|
|
581
|
+
click.echo(f" {var}")
|
|
582
|
+
else:
|
|
583
|
+
# Show all categories with their variables
|
|
584
|
+
click.echo("Environment Variables by Category:\n")
|
|
585
|
+
for cat_id, cat_info in CREDENTIAL_CATEGORIES.items():
|
|
586
|
+
click.echo(f"=== {cat_info['name']} ({cat_id}) ===")
|
|
587
|
+
click.echo(f"Variables ({len(cat_info['keys'])} total):")
|
|
588
|
+
for var in sorted(cat_info["keys"]):
|
|
589
|
+
click.echo(f" {var}")
|
|
590
|
+
click.echo()
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
@config_cmd.command(name="clear")
|
|
594
|
+
@click.option("--force", is_flag=True, help="Skip confirmation prompt")
|
|
595
|
+
def config_clear(force: bool = False):
|
|
596
|
+
"""Clear ALL configuration including credentials and settings.
|
|
597
|
+
|
|
598
|
+
This will remove EVERYTHING from the configuration file. Use with caution!
|
|
599
|
+
|
|
600
|
+
Examples:
|
|
601
|
+
codemie-test-harness config clear
|
|
602
|
+
codemie-test-harness config clear --force
|
|
603
|
+
"""
|
|
604
|
+
from ..utils import load_config
|
|
605
|
+
from ..constants import CONSOLE
|
|
606
|
+
|
|
607
|
+
# Load current config to check if there's anything to clear
|
|
608
|
+
current_config = load_config()
|
|
609
|
+
if not current_config:
|
|
610
|
+
CONSOLE.print("[yellow]No configuration found to clear.[/yellow]")
|
|
611
|
+
return
|
|
612
|
+
|
|
613
|
+
# Count all items that will be removed
|
|
614
|
+
total_count = len(current_config)
|
|
615
|
+
|
|
616
|
+
if not force:
|
|
617
|
+
CONSOLE.print(
|
|
618
|
+
f"[yellow]⚠️ WARNING: This will remove ALL {total_count} configuration items![/yellow]"
|
|
619
|
+
)
|
|
620
|
+
CONSOLE.print("[yellow]This action cannot be undone.[/yellow]")
|
|
621
|
+
|
|
622
|
+
# Show what will be cleared
|
|
623
|
+
CONSOLE.print("\n[cyan]Items that will be cleared:[/cyan]")
|
|
624
|
+
for key in sorted(current_config.keys()):
|
|
625
|
+
CONSOLE.print(f" • {key}")
|
|
626
|
+
|
|
627
|
+
confirm = click.confirm("\nAre you sure you want to clear ALL configuration?")
|
|
628
|
+
if not confirm:
|
|
629
|
+
CONSOLE.print("[green]Operation cancelled.[/green]")
|
|
630
|
+
return
|
|
631
|
+
|
|
632
|
+
# Clear everything
|
|
633
|
+
save_config({})
|
|
634
|
+
|
|
635
|
+
CONSOLE.print(
|
|
636
|
+
f"[green]✅ Successfully cleared ALL {total_count} configuration items.[/green]"
|
|
637
|
+
)
|
|
638
|
+
CONSOLE.print("[cyan]Configuration file is now empty.[/cyan]")
|