utils_devops 0.1.160__py3-none-any.whl → 0.1.166__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.
- utils_devops/extras/docker_ops.py +132 -68
- {utils_devops-0.1.160.dist-info → utils_devops-0.1.166.dist-info}/METADATA +1 -1
- {utils_devops-0.1.160.dist-info → utils_devops-0.1.166.dist-info}/RECORD +5 -5
- {utils_devops-0.1.160.dist-info → utils_devops-0.1.166.dist-info}/WHEEL +0 -0
- {utils_devops-0.1.160.dist-info → utils_devops-0.1.166.dist-info}/entry_points.txt +0 -0
|
@@ -82,6 +82,7 @@ class ContainerInfo:
|
|
|
82
82
|
status: str
|
|
83
83
|
service: Optional[str] = None
|
|
84
84
|
ports: Dict[str, Any] = field(default_factory=dict)
|
|
85
|
+
exit_code: Optional[int] = None
|
|
85
86
|
|
|
86
87
|
# Core Docker operations
|
|
87
88
|
def get_docker_client():
|
|
@@ -615,36 +616,82 @@ def compose_restart(compose_file: str, remove_volumes: bool = False,
|
|
|
615
616
|
|
|
616
617
|
return None
|
|
617
618
|
|
|
619
|
+
import json
|
|
620
|
+
import re
|
|
621
|
+
from typing import List, Optional
|
|
622
|
+
|
|
623
|
+
_EXITED_RE = re.compile(r'Exited\s*\((\d+)\)', re.I)
|
|
624
|
+
|
|
618
625
|
def compose_ps(compose_file: str, project_name: Optional[str] = None, env_file: Optional[str] = None) -> List[ContainerInfo]:
|
|
619
|
-
"""List compose services status using detected compose command
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
+
"""List compose services status using detected compose command. Returns ContainerInfo items
|
|
627
|
+
and attempts to populate `exit_code` when available (JSON ExitCode or parsed from Status)."""
|
|
628
|
+
result = run_compose_command(compose_file, ["ps", "-a", "--format", "json"], project_name, env_file)
|
|
629
|
+
logger.info(f"DEBUG: compose_ps stdout: {result.stdout}") # Use logger instead of print if available
|
|
630
|
+
containers: List[ContainerInfo] = []
|
|
631
|
+
lines = result.stdout.splitlines()
|
|
632
|
+
items = []
|
|
633
|
+
for line in lines:
|
|
634
|
+
if line.strip():
|
|
635
|
+
try:
|
|
636
|
+
items.append(json.loads(line))
|
|
637
|
+
except json.JSONDecodeError:
|
|
638
|
+
pass # ignore bad lines
|
|
639
|
+
if items:
|
|
640
|
+
for item in items:
|
|
641
|
+
state_str = item.get('State') or ''
|
|
642
|
+
full_status = item.get('Status') or state_str or ''
|
|
643
|
+
exit_code = item.get('ExitCode') or item.get('Exit')
|
|
644
|
+
if exit_code is not None:
|
|
645
|
+
try:
|
|
646
|
+
exit_code = int(exit_code)
|
|
647
|
+
except ValueError:
|
|
648
|
+
exit_code = None
|
|
649
|
+
if exit_code is None:
|
|
650
|
+
m = _EXITED_RE.search(full_status)
|
|
651
|
+
if m:
|
|
652
|
+
try:
|
|
653
|
+
exit_code = int(m.group(1))
|
|
654
|
+
except ValueError:
|
|
655
|
+
exit_code = None
|
|
626
656
|
containers.append(ContainerInfo(
|
|
627
|
-
id=item.get('ID', ''),
|
|
628
|
-
name=item.get('Name', ''),
|
|
629
|
-
image=item.get('Image', ''),
|
|
630
|
-
status=
|
|
631
|
-
service=item.get('Service', '')
|
|
657
|
+
id=item.get('ID', '') or item.get('Id', ''),
|
|
658
|
+
name=item.get('Name', '') or item.get('Names', ''),
|
|
659
|
+
image=item.get('Image', '') or item.get('ImageName', ''),
|
|
660
|
+
status=state_str,
|
|
661
|
+
service=item.get('Service', '') or item.get('Labels', {}).get('com.docker.compose.service', ''),
|
|
662
|
+
exit_code=exit_code
|
|
632
663
|
))
|
|
633
|
-
|
|
634
|
-
# Fallback to text parsing
|
|
635
|
-
lines
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
664
|
+
else:
|
|
665
|
+
# Fallback to text parsing (if not JSON format)
|
|
666
|
+
if len(lines) > 0 and 'NAME' in lines[0].upper(): # Check for header
|
|
667
|
+
for line in lines[1:]: # skip header
|
|
668
|
+
parts = re.split(r'\s{2,}', line.strip())
|
|
669
|
+
if len(parts) >= 6:
|
|
670
|
+
name = parts[0]
|
|
671
|
+
image = parts[1]
|
|
672
|
+
command = parts[2]
|
|
673
|
+
service = parts[3]
|
|
674
|
+
created = parts[4]
|
|
675
|
+
status = parts[5]
|
|
676
|
+
ports = ' '.join(parts[6:]) if len(parts) > 6 else ''
|
|
677
|
+
exit_code = None
|
|
678
|
+
m = _EXITED_RE.search(status)
|
|
679
|
+
if m:
|
|
680
|
+
try:
|
|
681
|
+
exit_code = int(m.group(1))
|
|
682
|
+
except ValueError:
|
|
683
|
+
exit_code = None
|
|
684
|
+
containers.append(ContainerInfo(
|
|
685
|
+
id='', # ID not in text
|
|
686
|
+
name=name,
|
|
687
|
+
image=image,
|
|
688
|
+
status=status,
|
|
689
|
+
service=service,
|
|
690
|
+
exit_code=exit_code
|
|
691
|
+
))
|
|
646
692
|
return containers
|
|
647
693
|
|
|
694
|
+
|
|
648
695
|
# Add a function to check compose version
|
|
649
696
|
def get_compose_version() -> Dict[str, str]:
|
|
650
697
|
"""Get detected compose command and version"""
|
|
@@ -2222,6 +2269,7 @@ def playwright_test_compose(
|
|
|
2222
2269
|
keep_compose_up: bool = False,
|
|
2223
2270
|
dry_run: bool = False,
|
|
2224
2271
|
project_name: Optional[str] = None,
|
|
2272
|
+
ui: Optional[bool] = False,
|
|
2225
2273
|
logger: Optional[logger] = None,
|
|
2226
2274
|
) -> Dict[str, Any]:
|
|
2227
2275
|
"""
|
|
@@ -2304,14 +2352,14 @@ def playwright_test_compose(
|
|
|
2304
2352
|
new_version = _update_env_version(env_file=env_file, source=version_source, logger=logger)
|
|
2305
2353
|
if new_version:
|
|
2306
2354
|
logger.info(f"{STICKERS['success']} Version updated to {new_version}")
|
|
2307
|
-
envs.set_system_env(key='STATIC_CAPTCHA', value='true')
|
|
2308
2355
|
else:
|
|
2309
2356
|
logger.info(f"{STICKERS['info']} Version update skipped (dry run)")
|
|
2310
2357
|
logger.info("")
|
|
2311
2358
|
|
|
2312
2359
|
# STEP 2: Environment
|
|
2313
2360
|
if not dry_run:
|
|
2314
|
-
envs.
|
|
2361
|
+
envs.dotenv_set_key(env_file,"STATIC_CAPTCHA","true")
|
|
2362
|
+
|
|
2315
2363
|
|
|
2316
2364
|
# STEP 3: Application stack
|
|
2317
2365
|
logger.info(f"{STICKERS['info']} STEP 3: Starting application stack")
|
|
@@ -2348,6 +2396,7 @@ def playwright_test_compose(
|
|
|
2348
2396
|
logger.info(f"{STICKERS['info']} STEP 4: Starting Playwright runner")
|
|
2349
2397
|
if not dry_run:
|
|
2350
2398
|
logger.info(f"{STICKERS['info']} Project: {project_name_playwright}")
|
|
2399
|
+
resolve_conflicts(playwright_compose_file,remove_conflicting_containers=True)
|
|
2351
2400
|
up_success = compose_up(
|
|
2352
2401
|
playwright_compose_file,
|
|
2353
2402
|
services=playwright_services,
|
|
@@ -2367,7 +2416,8 @@ def playwright_test_compose(
|
|
|
2367
2416
|
# STEP 5: Log capture
|
|
2368
2417
|
pw_services = []
|
|
2369
2418
|
logs_acc = {}
|
|
2370
|
-
|
|
2419
|
+
if ui :
|
|
2420
|
+
capture_logs = False
|
|
2371
2421
|
if not dry_run and capture_logs:
|
|
2372
2422
|
logger.info(f"{STICKERS['logs']} STEP 5: Capturing logs")
|
|
2373
2423
|
|
|
@@ -2419,54 +2469,66 @@ def playwright_test_compose(
|
|
|
2419
2469
|
|
|
2420
2470
|
# Check if all services have finished
|
|
2421
2471
|
ps = compose_ps(playwright_compose_file, project_name=project_name_playwright)
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2472
|
+
|
|
2473
|
+
running = []
|
|
2474
|
+
exited = []
|
|
2475
|
+
|
|
2476
|
+
for c in ps:
|
|
2477
|
+
if c.service in pw_services:
|
|
2478
|
+
status = (c.status or '').lower()
|
|
2428
2479
|
if 'running' in status or 'up' in status:
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
logger.info("")
|
|
2435
|
-
else:
|
|
2480
|
+
running.append(c)
|
|
2481
|
+
elif 'exited' in status:
|
|
2482
|
+
exited.append(c)
|
|
2483
|
+
|
|
2484
|
+
if running:
|
|
2436
2485
|
time.sleep(health_interval)
|
|
2486
|
+
else:
|
|
2487
|
+
finished = True
|
|
2488
|
+
|
|
2437
2489
|
|
|
2438
2490
|
result['logs_captured'] = logs_acc
|
|
2439
2491
|
|
|
2440
2492
|
# STEP 6: Analysis
|
|
2441
2493
|
logger.info(f"{STICKERS['analysis']} STEP 6: Analyzing results")
|
|
2442
|
-
|
|
2494
|
+
|
|
2443
2495
|
if not dry_run:
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2496
|
+
if not ui :
|
|
2497
|
+
# Get exit codes
|
|
2498
|
+
ps = compose_ps(playwright_compose_file, project_name=project_name_playwright)
|
|
2499
|
+
exit_codes = {}
|
|
2500
|
+
|
|
2501
|
+
for container in ps:
|
|
2502
|
+
service_name = container.service or ''
|
|
2503
|
+
if service_name in pw_services:
|
|
2504
|
+
exit_code = container.exit_code if container.exit_code is not None else 1 # Default to 1 if unknown
|
|
2505
|
+
exit_codes[service_name] = exit_code
|
|
2506
|
+
|
|
2507
|
+
if not exit_codes:
|
|
2508
|
+
logger.error("No Playwright containers found during analysis — assuming failure")
|
|
2509
|
+
exit_codes = {pw_services[0]: 1} if pw_services else {'unknown': 1}
|
|
2510
|
+
result['error'] = 'No containers found for analysis - likely exited with error'
|
|
2511
|
+
|
|
2512
|
+
# Check overall success
|
|
2513
|
+
all_success = all(code == 0 for code in exit_codes.values())
|
|
2514
|
+
|
|
2515
|
+
if all_success:
|
|
2516
|
+
result['success'] = True
|
|
2517
|
+
logger.info(f"{STICKERS['success']} " + "="*50)
|
|
2518
|
+
logger.info(f"{STICKERS['success']} TESTS PASSED")
|
|
2519
|
+
logger.info(f"{STICKERS['success']} Test ID: {test_id}")
|
|
2520
|
+
logger.info(f"{STICKERS['success']} " + "="*50)
|
|
2521
|
+
else:
|
|
2522
|
+
result['success'] = False
|
|
2523
|
+
result['error'] = 'Playwright tests failed' if 'error' not in result else result['error']
|
|
2524
|
+
logger.error(f"{STICKERS['error']} " + "="*50)
|
|
2525
|
+
logger.error(f"{STICKERS['error']} TESTS FAILED")
|
|
2526
|
+
logger.error(f"{STICKERS['error']} Exit codes: {exit_codes}")
|
|
2527
|
+
logger.error(f"{STICKERS['error']} " + "="*50)
|
|
2463
2528
|
else:
|
|
2464
|
-
result['success'] =
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
logger.error(f"{STICKERS['error']} TESTS FAILED")
|
|
2468
|
-
logger.error(f"{STICKERS['error']} Exit codes: {exit_codes}")
|
|
2469
|
-
logger.error(f"{STICKERS['error']} " + "="*50)
|
|
2529
|
+
result['success'] = True
|
|
2530
|
+
logger.info(f"{STICKERS['info']} Ui conteiner is Up")
|
|
2531
|
+
|
|
2470
2532
|
else:
|
|
2471
2533
|
result['success'] = True
|
|
2472
2534
|
logger.info(f"{STICKERS['info']} Analysis skipped (dry run)")
|
|
@@ -2477,6 +2539,8 @@ def playwright_test_compose(
|
|
|
2477
2539
|
logger.info("")
|
|
2478
2540
|
|
|
2479
2541
|
# Cleanup
|
|
2542
|
+
if ui :
|
|
2543
|
+
keep_compose_up = True
|
|
2480
2544
|
if not keep_compose_up and not dry_run:
|
|
2481
2545
|
logger.info(f"{STICKERS['cleanup']} Cleaning up")
|
|
2482
2546
|
logger.info(f"{STICKERS['info']} Stopping Playwright stack...")
|
|
@@ -4628,7 +4692,7 @@ def resolve_conflicts(
|
|
|
4628
4692
|
remove_conflicting_containers: bool = False,
|
|
4629
4693
|
remove_conflicting_networks: bool = False,
|
|
4630
4694
|
remove_conflicting_volumes: bool = False,
|
|
4631
|
-
force: bool =
|
|
4695
|
+
force: bool = True,
|
|
4632
4696
|
project_name: Optional[str] = None,
|
|
4633
4697
|
env_file: Optional[str] = None
|
|
4634
4698
|
) -> Dict[str, Any]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: utils_devops
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.166
|
|
4
4
|
Summary: Lightweight DevOps utilities for automation scripts: config editing (YAML/JSON/INI/.env), templating, diffing, and CLI tools
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: devops,automation,nginx,cli,jinja2,yaml,config,diff,templating,logging,docker,compose,file-ops
|
|
@@ -9,7 +9,7 @@ utils_devops/core/strings.py,sha256=8s0GSjcyTKwLjJjsJ_XfOJxPtyb549icDlU9SUxSvHI,
|
|
|
9
9
|
utils_devops/core/systems.py,sha256=wNbEFUAvbMPdqWN-iXvTzvj5iE9xaWfjZYYvD0EZAH0,47577
|
|
10
10
|
utils_devops/extras/__init__.py,sha256=ZXHeVLHO3_qiW9AY-UQ_YA9cQzmkLGv54a2UbyvtlM0,3571
|
|
11
11
|
utils_devops/extras/aws_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
utils_devops/extras/docker_ops.py,sha256=
|
|
12
|
+
utils_devops/extras/docker_ops.py,sha256=VGH3SWufYEEr4459Xl_tgJ-rXNu62lFj-hSbtQua39I,197991
|
|
13
13
|
utils_devops/extras/git_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
utils_devops/extras/interaction_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
utils_devops/extras/metrics_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -19,7 +19,7 @@ utils_devops/extras/notification_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
|
19
19
|
utils_devops/extras/performance_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
utils_devops/extras/ssh_ops.py,sha256=hTOYzyWmnZWzOLeZbCoZRLxSJiBmr0QgS_87qks-CYk,76305
|
|
21
21
|
utils_devops/extras/vault_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
utils_devops-0.1.
|
|
23
|
-
utils_devops-0.1.
|
|
24
|
-
utils_devops-0.1.
|
|
25
|
-
utils_devops-0.1.
|
|
22
|
+
utils_devops-0.1.166.dist-info/METADATA,sha256=zkvREOE-NO-qC12A1VoMvJNisJ-rq7MGWZzVsrrKU7k,1903
|
|
23
|
+
utils_devops-0.1.166.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
24
|
+
utils_devops-0.1.166.dist-info/entry_points.txt,sha256=ei3B6ZL5yu6dOq-U1r8wsBdkXeg63RAyV7m8_ADaE6k,53
|
|
25
|
+
utils_devops-0.1.166.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|