utils_devops 0.1.158__tar.gz → 0.1.160__tar.gz
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-0.1.158 → utils_devops-0.1.160}/PKG-INFO +1 -1
- {utils_devops-0.1.158 → utils_devops-0.1.160}/pyproject.toml +1 -1
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/docker_ops.py +221 -109
- {utils_devops-0.1.158 → utils_devops-0.1.160}/README.md +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/__init__.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/core/__init__.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/core/datetimes.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/core/envs.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/core/files.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/core/logs.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/core/script_helpers.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/core/strings.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/core/systems.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/__init__.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/aws_ops.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/git_ops.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/interaction_ops.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/metrics_ops.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/network_ops.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/nginx_ops.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/notification_ops.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/performance_ops.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/ssh_ops.py +0 -0
- {utils_devops-0.1.158 → utils_devops-0.1.160}/src/utils_devops/extras/vault_ops.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: utils_devops
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.160
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "utils_devops"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.160" # Bumped for new string features + diffing
|
|
4
4
|
description = "Lightweight DevOps utilities for automation scripts: config editing (YAML/JSON/INI/.env), templating, diffing, and CLI tools"
|
|
5
5
|
authors = ["Hamed Sheikhan <sh.sheikhan.m@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -8,7 +8,6 @@ import json
|
|
|
8
8
|
import os
|
|
9
9
|
import re
|
|
10
10
|
from logging import Logger
|
|
11
|
-
import re
|
|
12
11
|
import sys
|
|
13
12
|
import tarfile
|
|
14
13
|
import tempfile
|
|
@@ -618,7 +617,7 @@ def compose_restart(compose_file: str, remove_volumes: bool = False,
|
|
|
618
617
|
|
|
619
618
|
def compose_ps(compose_file: str, project_name: Optional[str] = None, env_file: Optional[str] = None) -> List[ContainerInfo]:
|
|
620
619
|
"""List compose services status using detected compose command"""
|
|
621
|
-
result = run_compose_command(compose_file, ["ps", "--format", "json"], project_name , env_file)
|
|
620
|
+
result = run_compose_command(compose_file, ["ps" ,"-a", "--format", "json"], project_name , env_file)
|
|
622
621
|
|
|
623
622
|
containers = []
|
|
624
623
|
try:
|
|
@@ -909,7 +908,7 @@ def check_health_from_compose_ps(compose_file: str) -> Dict[str, Any]:
|
|
|
909
908
|
"""
|
|
910
909
|
try:
|
|
911
910
|
# Run docker compose ps with JSON format
|
|
912
|
-
result = run_compose_command(compose_file, ["ps", "--format", "json"])
|
|
911
|
+
result = run_compose_command(compose_file, ["ps","-a", "--format", "json"])
|
|
913
912
|
if result.rc != 0:
|
|
914
913
|
return {"healthy": False, "error": f"docker compose ps failed: {result.stderr}"}
|
|
915
914
|
|
|
@@ -2220,8 +2219,7 @@ def playwright_test_compose(
|
|
|
2220
2219
|
pull_missing: bool = True,
|
|
2221
2220
|
no_build: bool = True,
|
|
2222
2221
|
no_pull: bool = True,
|
|
2223
|
-
|
|
2224
|
-
# Keep services up after each test_compose call so we can sequence them
|
|
2222
|
+
keep_compose_up: bool = False,
|
|
2225
2223
|
dry_run: bool = False,
|
|
2226
2224
|
project_name: Optional[str] = None,
|
|
2227
2225
|
logger: Optional[logger] = None,
|
|
@@ -2231,7 +2229,7 @@ def playwright_test_compose(
|
|
|
2231
2229
|
application compose and a separate Playwright compose. This function reuses the
|
|
2232
2230
|
existing `test_compose` function to perform validation, startup and health checks for
|
|
2233
2231
|
the application compose.
|
|
2234
|
-
|
|
2232
|
+
|
|
2235
2233
|
Behavior summary:
|
|
2236
2234
|
1. Validate inputs and (optionally) update version as in test_compose.
|
|
2237
2235
|
2. Set STATIC_CAPTCHA=true in the system process environment (without modifying the file).
|
|
@@ -2241,19 +2239,34 @@ def playwright_test_compose(
|
|
|
2241
2239
|
5. Follow logs (simulating -f) for the Playwright service(s) while polling the compose state
|
|
2242
2240
|
to detect completion, and determine pass/fail from exit status and log patterns.
|
|
2243
2241
|
6. Return a detailed result dict similar in structure to test_compose.
|
|
2244
|
-
|
|
2242
|
+
|
|
2245
2243
|
Notes:
|
|
2246
2244
|
- Reuses `test_compose` for application startup and health checks.
|
|
2247
2245
|
- Sets STATIC_CAPTCHA in the process environment only (os.environ).
|
|
2248
2246
|
- Does not automatically bring services down; that is left to the caller.
|
|
2249
2247
|
- Success is determined by all containers exiting with code 0 and/or presence of success patterns in logs.
|
|
2250
|
-
|
|
2248
|
+
|
|
2251
2249
|
Returns:
|
|
2252
2250
|
Dict with keys: test_id, success, steps, app_result, logs_captured, duration, error
|
|
2253
2251
|
"""
|
|
2252
|
+
|
|
2253
|
+
# Minimal sticker set - only what's necessary
|
|
2254
|
+
STICKERS = {
|
|
2255
|
+
'start': '🎬',
|
|
2256
|
+
'success': '✅',
|
|
2257
|
+
'error': '❌',
|
|
2258
|
+
'info': 'ℹ️',
|
|
2259
|
+
'waiting': '⏳',
|
|
2260
|
+
'complete': '🏁',
|
|
2261
|
+
'logs': '📝',
|
|
2262
|
+
'analysis': '📊',
|
|
2263
|
+
'cleanup': '🧹',
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2254
2266
|
logger = logger or DEFAULT_LOGGER
|
|
2255
2267
|
test_id = str(uuid.uuid4())[:8]
|
|
2256
2268
|
start_time = dt_ops.current_datetime()
|
|
2269
|
+
|
|
2257
2270
|
result = {
|
|
2258
2271
|
"test_id": test_id,
|
|
2259
2272
|
"success": False,
|
|
@@ -2263,40 +2276,50 @@ def playwright_test_compose(
|
|
|
2263
2276
|
"duration": 0.0,
|
|
2264
2277
|
"error": None,
|
|
2265
2278
|
}
|
|
2279
|
+
|
|
2280
|
+
project_name_playwright = project_name
|
|
2281
|
+
|
|
2266
2282
|
try:
|
|
2267
|
-
|
|
2268
|
-
logger.info(f"
|
|
2269
|
-
|
|
2270
|
-
logger.info("
|
|
2283
|
+
# Start header - clean and professional
|
|
2284
|
+
logger.info(f"{STICKERS['start']} Playwright test starting (ID: {test_id})")
|
|
2285
|
+
logger.info(f"{STICKERS['info']} App: {compose_file}")
|
|
2286
|
+
logger.info(f"{STICKERS['info']} Playwright: {playwright_compose_file}")
|
|
2287
|
+
if dry_run:
|
|
2288
|
+
logger.info(f"{STICKERS['info']} Dry run mode")
|
|
2289
|
+
logger.info("")
|
|
2290
|
+
|
|
2291
|
+
# STEP 0: Validation
|
|
2292
|
+
logger.info(f"{STICKERS['info']} STEP 0: Validating files")
|
|
2271
2293
|
if not files.file_exists(compose_file):
|
|
2272
2294
|
raise DockerOpsError(f"Application compose file not found: {compose_file}")
|
|
2273
2295
|
if not files.file_exists(playwright_compose_file):
|
|
2274
2296
|
raise DockerOpsError(f"Playwright compose file not found: {playwright_compose_file}")
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2297
|
+
logger.info(f"{STICKERS['success']} Validation complete")
|
|
2298
|
+
logger.info("")
|
|
2299
|
+
|
|
2300
|
+
# STEP 1: Version update
|
|
2301
|
+
if update_version:
|
|
2302
|
+
logger.info(f"{STICKERS['info']} STEP 1: Updating version")
|
|
2303
|
+
if not dry_run:
|
|
2304
|
+
new_version = _update_env_version(env_file=env_file, source=version_source, logger=logger)
|
|
2305
|
+
if new_version:
|
|
2306
|
+
logger.info(f"{STICKERS['success']} Version updated to {new_version}")
|
|
2307
|
+
envs.set_system_env(key='STATIC_CAPTCHA', value='true')
|
|
2308
|
+
else:
|
|
2309
|
+
logger.info(f"{STICKERS['info']} Version update skipped (dry run)")
|
|
2310
|
+
logger.info("")
|
|
2311
|
+
|
|
2312
|
+
# STEP 2: Environment
|
|
2289
2313
|
if not dry_run:
|
|
2290
2314
|
envs.set_system_env(key='STATIC_CAPTCHA', value='true')
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
logger.info("Step 3: bringing up application stack (health checks via test_compose)")
|
|
2315
|
+
|
|
2316
|
+
# STEP 3: Application stack
|
|
2317
|
+
logger.info(f"{STICKERS['info']} STEP 3: Starting application stack")
|
|
2295
2318
|
if not dry_run:
|
|
2296
2319
|
app_result = test_compose(
|
|
2297
2320
|
compose_file=compose_file,
|
|
2298
2321
|
env_file=env_file,
|
|
2299
|
-
update_version=False,
|
|
2322
|
+
update_version=False,
|
|
2300
2323
|
services=services,
|
|
2301
2324
|
health_timeout=health_timeout,
|
|
2302
2325
|
health_interval=health_interval,
|
|
@@ -2305,126 +2328,215 @@ def playwright_test_compose(
|
|
|
2305
2328
|
pull_missing=pull_missing,
|
|
2306
2329
|
no_build=no_build,
|
|
2307
2330
|
no_pull=no_pull,
|
|
2308
|
-
keep_compose_up=True,
|
|
2331
|
+
keep_compose_up=True,
|
|
2309
2332
|
project_name=project_name,
|
|
2310
2333
|
dry_run=dry_run,
|
|
2311
2334
|
logger=logger,
|
|
2312
2335
|
)
|
|
2336
|
+
|
|
2313
2337
|
result['app_result'] = app_result
|
|
2314
2338
|
if not app_result.get('success'):
|
|
2315
|
-
raise DockerOpsError("Application
|
|
2316
|
-
|
|
2339
|
+
raise DockerOpsError("Application stack failed")
|
|
2340
|
+
|
|
2341
|
+
logger.info(f"{STICKERS['success']} Application stack ready")
|
|
2342
|
+
logger.info("")
|
|
2317
2343
|
else:
|
|
2318
|
-
logger.info("
|
|
2319
|
-
|
|
2320
|
-
|
|
2344
|
+
logger.info(f"{STICKERS['info']} Application start skipped (dry run)")
|
|
2345
|
+
logger.info("")
|
|
2346
|
+
|
|
2347
|
+
# STEP 4: Playwright stack
|
|
2348
|
+
logger.info(f"{STICKERS['info']} STEP 4: Starting Playwright runner")
|
|
2321
2349
|
if not dry_run:
|
|
2350
|
+
logger.info(f"{STICKERS['info']} Project: {project_name_playwright}")
|
|
2322
2351
|
up_success = compose_up(
|
|
2323
2352
|
playwright_compose_file,
|
|
2324
2353
|
services=playwright_services,
|
|
2325
|
-
project_name=
|
|
2326
|
-
# Assume detached by default or add param if needed; help shows no param for detached, perhaps always detached
|
|
2354
|
+
project_name=project_name_playwright,
|
|
2327
2355
|
)
|
|
2356
|
+
|
|
2328
2357
|
if not up_success:
|
|
2329
2358
|
raise DockerOpsError("Playwright compose_up failed")
|
|
2330
|
-
|
|
2359
|
+
|
|
2360
|
+
logger.info(f"{STICKERS['success']} Playwright started")
|
|
2361
|
+
logger.info(f"{STICKERS['waiting']} Following logs until completion...")
|
|
2362
|
+
logger.info("")
|
|
2331
2363
|
else:
|
|
2332
|
-
logger.info("
|
|
2333
|
-
|
|
2364
|
+
logger.info(f"{STICKERS['info']} Playwright start skipped (dry run)")
|
|
2365
|
+
logger.info("")
|
|
2366
|
+
|
|
2367
|
+
# STEP 5: Log capture
|
|
2368
|
+
pw_services = []
|
|
2369
|
+
logs_acc = {}
|
|
2370
|
+
|
|
2334
2371
|
if not dry_run and capture_logs:
|
|
2335
|
-
logger.info("
|
|
2336
|
-
|
|
2337
|
-
|
|
2372
|
+
logger.info(f"{STICKERS['logs']} STEP 5: Capturing logs")
|
|
2373
|
+
|
|
2374
|
+
# Determine services to monitor
|
|
2375
|
+
compose_config = read_compose_file(playwright_compose_file, env_file)
|
|
2376
|
+
all_services = list(compose_config.get('services', {}).keys())
|
|
2377
|
+
pw_services = playwright_services or all_services
|
|
2378
|
+
|
|
2379
|
+
logger.info(f"{STICKERS['info']} Services: {', '.join(pw_services)}")
|
|
2380
|
+
logger.info(f"{STICKERS['info']} Tail: {log_tail} lines, Timeout: {health_timeout}s")
|
|
2381
|
+
logger.info("")
|
|
2382
|
+
|
|
2383
|
+
logs_acc = {service: [] for service in pw_services}
|
|
2338
2384
|
deadline = time.time() + health_timeout
|
|
2339
2385
|
finished = False
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2386
|
+
|
|
2387
|
+
# Wait for containers to initialize
|
|
2388
|
+
time.sleep(3)
|
|
2389
|
+
|
|
2390
|
+
while not finished:
|
|
2391
|
+
current_time = time.time()
|
|
2392
|
+
if current_time > deadline:
|
|
2393
|
+
raise DockerOpsError(f"Playwright runner timed out after {health_timeout}s")
|
|
2394
|
+
|
|
2395
|
+
for service in pw_services:
|
|
2344
2396
|
try:
|
|
2345
|
-
|
|
2397
|
+
# Get fresh logs for this service
|
|
2398
|
+
log_lines = compose_logs(
|
|
2399
|
+
playwright_compose_file,
|
|
2400
|
+
services=[service],
|
|
2401
|
+
tail=log_tail,
|
|
2402
|
+
project_name=project_name_playwright,
|
|
2403
|
+
follow=True,
|
|
2404
|
+
|
|
2405
|
+
)
|
|
2406
|
+
|
|
2407
|
+
for log_line in log_lines:
|
|
2408
|
+
if hasattr(log_line, 'message'):
|
|
2409
|
+
log_message = log_line.message
|
|
2410
|
+
# Add only new log lines
|
|
2411
|
+
if log_message not in logs_acc[service]:
|
|
2412
|
+
logs_acc[service].append(log_message)
|
|
2413
|
+
# Print with clean formatting: only service name at the beginning
|
|
2414
|
+
logger.info(f"{STICKERS['info']} {service}")
|
|
2415
|
+
logger.info(log_message)
|
|
2346
2416
|
except Exception as e:
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2417
|
+
error_msg = f"Error fetching logs: {str(e)}"
|
|
2418
|
+
logger.error(f"{STICKERS['error']} {service} - {error_msg}")
|
|
2419
|
+
|
|
2420
|
+
# Check if all services have finished
|
|
2421
|
+
ps = compose_ps(playwright_compose_file, project_name=project_name_playwright)
|
|
2422
|
+
running_count = 0
|
|
2423
|
+
|
|
2424
|
+
for container in ps:
|
|
2425
|
+
service_name = container.service or ''
|
|
2426
|
+
if service_name in pw_services:
|
|
2427
|
+
status = (container.status or '').lower()
|
|
2428
|
+
if 'running' in status or 'up' in status:
|
|
2429
|
+
running_count += 1
|
|
2430
|
+
|
|
2431
|
+
if running_count == 0:
|
|
2360
2432
|
finished = True
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2433
|
+
logger.info(f"{STICKERS['complete']} All services completed")
|
|
2434
|
+
logger.info("")
|
|
2435
|
+
else:
|
|
2364
2436
|
time.sleep(health_interval)
|
|
2437
|
+
|
|
2365
2438
|
result['logs_captured'] = logs_acc
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
if timed_out:
|
|
2371
|
-
raise DockerOpsError("Playwright runner timed out")
|
|
2372
|
-
else:
|
|
2373
|
-
if dry_run:
|
|
2374
|
-
logger.info("Step 5: log following skipped (dry run)")
|
|
2375
|
-
# Step 6: Analyze outcome
|
|
2376
|
-
logger.info("Step 6: analyzing Playwright runner outcome")
|
|
2439
|
+
|
|
2440
|
+
# STEP 6: Analysis
|
|
2441
|
+
logger.info(f"{STICKERS['analysis']} STEP 6: Analyzing results")
|
|
2442
|
+
|
|
2377
2443
|
if not dry_run:
|
|
2378
2444
|
# Get exit codes
|
|
2379
|
-
ps = compose_ps(playwright_compose_file, project_name=
|
|
2445
|
+
ps = compose_ps(playwright_compose_file, project_name=project_name_playwright)
|
|
2380
2446
|
exit_codes = {}
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2447
|
+
|
|
2448
|
+
for container in ps:
|
|
2449
|
+
service_name = container.service or ''
|
|
2450
|
+
if service_name in pw_services:
|
|
2451
|
+
exit_code = getattr(container, 'exit_code', None) or 1
|
|
2452
|
+
exit_codes[service_name] = exit_code
|
|
2453
|
+
|
|
2454
|
+
# Check overall success
|
|
2385
2455
|
all_success = all(code == 0 for code in exit_codes.values())
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
failed = any(re.search(pat, aggregated_logs, re.I) for pat in ['FAILED', 'FAIL', 'ERROR', 'AssertionError', 'Test Failed'])
|
|
2389
|
-
passed = any(re.search(pat, aggregated_logs, re.I) for pat in ['All tests passed', 'Tests passed', 'PASS'])
|
|
2390
|
-
if all_success and (passed or not failed):
|
|
2456
|
+
|
|
2457
|
+
if all_success:
|
|
2391
2458
|
result['success'] = True
|
|
2392
|
-
logger.info(f"
|
|
2459
|
+
logger.info(f"{STICKERS['success']} " + "="*50)
|
|
2460
|
+
logger.info(f"{STICKERS['success']} TESTS PASSED")
|
|
2461
|
+
logger.info(f"{STICKERS['success']} Test ID: {test_id}")
|
|
2462
|
+
logger.info(f"{STICKERS['success']} " + "="*50)
|
|
2393
2463
|
else:
|
|
2394
2464
|
result['success'] = False
|
|
2395
|
-
result['error'] = 'Playwright tests failed
|
|
2396
|
-
logger.error(f"
|
|
2397
|
-
|
|
2398
|
-
'
|
|
2399
|
-
'
|
|
2400
|
-
'failed_patterns': failed,
|
|
2401
|
-
}
|
|
2465
|
+
result['error'] = 'Playwright tests failed'
|
|
2466
|
+
logger.error(f"{STICKERS['error']} " + "="*50)
|
|
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)
|
|
2402
2470
|
else:
|
|
2403
|
-
result['success'] = True
|
|
2471
|
+
result['success'] = True
|
|
2472
|
+
logger.info(f"{STICKERS['info']} Analysis skipped (dry run)")
|
|
2473
|
+
|
|
2474
|
+
# Final timing
|
|
2404
2475
|
result['duration'] = (dt_ops.current_datetime() - start_time).total_seconds()
|
|
2405
|
-
|
|
2476
|
+
logger.info(f"{STICKERS['info']} Duration: {result['duration']:.2f}s")
|
|
2477
|
+
logger.info("")
|
|
2478
|
+
|
|
2479
|
+
# Cleanup
|
|
2480
|
+
if not keep_compose_up and not dry_run:
|
|
2481
|
+
logger.info(f"{STICKERS['cleanup']} Cleaning up")
|
|
2482
|
+
logger.info(f"{STICKERS['info']} Stopping Playwright stack...")
|
|
2406
2483
|
compose_down(
|
|
2407
2484
|
compose_file=playwright_compose_file,
|
|
2485
|
+
project_name=project_name_playwright
|
|
2408
2486
|
)
|
|
2409
|
-
logger.info("Playwright
|
|
2487
|
+
logger.info(f"{STICKERS['success']} Playwright stopped")
|
|
2488
|
+
|
|
2489
|
+
logger.info(f"{STICKERS['info']} Stopping Application stack...")
|
|
2410
2490
|
compose_down(
|
|
2411
2491
|
compose_file=compose_file,
|
|
2492
|
+
project_name=project_name
|
|
2412
2493
|
)
|
|
2413
|
-
logger.info("
|
|
2494
|
+
logger.info(f"{STICKERS['success']} Application stopped")
|
|
2495
|
+
logger.info(f"{STICKERS['success']} Cleanup complete")
|
|
2496
|
+
logger.info("")
|
|
2497
|
+
|
|
2498
|
+
# Final summary
|
|
2499
|
+
if result['success']:
|
|
2500
|
+
logger.info(f"{STICKERS['complete']} " + "="*70)
|
|
2501
|
+
logger.info(f"{STICKERS['complete']} TEST COMPLETE: SUCCESS")
|
|
2502
|
+
logger.info(f"{STICKERS['complete']} Duration: {result['duration']:.2f}s")
|
|
2503
|
+
logger.info(f"{STICKERS['complete']} Test ID: {test_id}")
|
|
2504
|
+
logger.info(f"{STICKERS['complete']} " + "="*70)
|
|
2505
|
+
else:
|
|
2506
|
+
logger.error(f"{STICKERS['error']} " + "="*70)
|
|
2507
|
+
logger.error(f"{STICKERS['error']} TEST COMPLETE: FAILED")
|
|
2508
|
+
logger.error(f"{STICKERS['error']} Duration: {result['duration']:.2f}s")
|
|
2509
|
+
logger.error(f"{STICKERS['error']} Test ID: {test_id}")
|
|
2510
|
+
logger.error(f"{STICKERS['error']} " + "="*70)
|
|
2511
|
+
|
|
2414
2512
|
return result
|
|
2513
|
+
|
|
2415
2514
|
except Exception as e:
|
|
2416
2515
|
result['duration'] = (dt_ops.current_datetime() - start_time).total_seconds()
|
|
2417
2516
|
result['error'] = str(e)
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2517
|
+
|
|
2518
|
+
logger.error(f"{STICKERS['error']} Test failed: {e}")
|
|
2519
|
+
logger.error(f"{STICKERS['info']} Duration: {result['duration']:.2f}s")
|
|
2520
|
+
|
|
2521
|
+
# Emergency cleanup
|
|
2522
|
+
if not keep_compose_up and not dry_run:
|
|
2523
|
+
logger.info(f"{STICKERS['cleanup']} Emergency cleanup")
|
|
2524
|
+
try:
|
|
2525
|
+
compose_down(
|
|
2526
|
+
compose_file=playwright_compose_file,
|
|
2527
|
+
project_name=project_name_playwright
|
|
2528
|
+
)
|
|
2529
|
+
except:
|
|
2530
|
+
pass
|
|
2531
|
+
|
|
2532
|
+
try:
|
|
2533
|
+
compose_down(
|
|
2534
|
+
compose_file=compose_file,
|
|
2535
|
+
project_name=project_name
|
|
2536
|
+
)
|
|
2537
|
+
except:
|
|
2538
|
+
pass
|
|
2539
|
+
|
|
2428
2540
|
return result
|
|
2429
2541
|
|
|
2430
2542
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|