utils_devops 0.1.159__py3-none-any.whl → 0.1.161__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.
@@ -2229,6 +2229,7 @@ def playwright_test_compose(
2229
2229
  application compose and a separate Playwright compose. This function reuses the
2230
2230
  existing `test_compose` function to perform validation, startup and health checks for
2231
2231
  the application compose.
2232
+
2232
2233
  Behavior summary:
2233
2234
  1. Validate inputs and (optionally) update version as in test_compose.
2234
2235
  2. Set STATIC_CAPTCHA=true in the system process environment (without modifying the file).
@@ -2238,17 +2239,34 @@ def playwright_test_compose(
2238
2239
  5. Follow logs (simulating -f) for the Playwright service(s) while polling the compose state
2239
2240
  to detect completion, and determine pass/fail from exit status and log patterns.
2240
2241
  6. Return a detailed result dict similar in structure to test_compose.
2242
+
2241
2243
  Notes:
2242
2244
  - Reuses `test_compose` for application startup and health checks.
2243
2245
  - Sets STATIC_CAPTCHA in the process environment only (os.environ).
2244
2246
  - Does not automatically bring services down; that is left to the caller.
2245
2247
  - Success is determined by all containers exiting with code 0 and/or presence of success patterns in logs.
2248
+
2246
2249
  Returns:
2247
2250
  Dict with keys: test_id, success, steps, app_result, logs_captured, duration, error
2248
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
+
2249
2266
  logger = logger or DEFAULT_LOGGER
2250
2267
  test_id = str(uuid.uuid4())[:8]
2251
2268
  start_time = dt_ops.current_datetime()
2269
+
2252
2270
  result = {
2253
2271
  "test_id": test_id,
2254
2272
  "success": False,
@@ -2258,42 +2276,50 @@ def playwright_test_compose(
2258
2276
  "duration": 0.0,
2259
2277
  "error": None,
2260
2278
  }
2261
- #project_name_playwright = f"playwright-{test_id[:8]}"
2279
+
2262
2280
  project_name_playwright = project_name
2281
+
2263
2282
  try:
2264
- logger.info(f"▶️ Playwright test workflow starting ({test_id})")
2265
- logger.info(f" Compose files: app={compose_file} playwright={playwright_compose_file}")
2266
- # Step 0: Basic validation
2267
- logger.info("Step 0: validating compose and env files")
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")
2268
2293
  if not files.file_exists(compose_file):
2269
2294
  raise DockerOpsError(f"Application compose file not found: {compose_file}")
2270
2295
  if not files.file_exists(playwright_compose_file):
2271
2296
  raise DockerOpsError(f"Playwright compose file not found: {playwright_compose_file}")
2272
- # Step 1: Optional version update
2273
- if update_version and not dry_run:
2274
- logger.info("Step 1: updating version in env (requested)")
2275
- new_version = _update_env_version(env_file=env_file, source=version_source, logger=logger)
2276
- if new_version:
2277
- logger.info(f" Version updated to {new_version}")
2278
- # make sure runtime env sees new values
2279
- envs.import_env_to_system(get_env_compose(env_file))
2280
- elif update_version and dry_run:
2281
- logger.info("Step 1: version update skipped (dry run)")
2282
- else:
2283
- logger.info("Step 1: version update not requested")
2284
- # Step 2: Set STATIC_CAPTCHA=true in system process env
2285
- logger.info("Step 2: setting STATIC_CAPTCHA=true in process environment")
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
+ else:
2308
+ logger.info(f"{STICKERS['info']} Version update skipped (dry run)")
2309
+ logger.info("")
2310
+
2311
+ # STEP 2: Environment
2286
2312
  if not dry_run:
2287
- envs.set_system_env(key='STATIC_CAPTCHA', value='true')
2288
- else:
2289
- logger.info("Step 2: env set skipped (dry run)")
2290
- # Step 3: Start application compose using test_compose but keep services up
2291
- logger.info("Step 3: bringing up application stack (health checks via test_compose)")
2313
+ envs.dotenv_set_key(env_file,"STATIC_CAPTCHA","true")
2314
+
2315
+
2316
+ # STEP 3: Application stack
2317
+ logger.info(f"{STICKERS['info']} STEP 3: Starting application stack")
2292
2318
  if not dry_run:
2293
2319
  app_result = test_compose(
2294
2320
  compose_file=compose_file,
2295
2321
  env_file=env_file,
2296
- update_version=False, # already handled
2322
+ update_version=False,
2297
2323
  services=services,
2298
2324
  health_timeout=health_timeout,
2299
2325
  health_interval=health_interval,
@@ -2302,128 +2328,216 @@ def playwright_test_compose(
2302
2328
  pull_missing=pull_missing,
2303
2329
  no_build=no_build,
2304
2330
  no_pull=no_pull,
2305
- keep_compose_up=True, # DO NOT tear down
2331
+ keep_compose_up=True,
2306
2332
  project_name=project_name,
2307
2333
  dry_run=dry_run,
2308
2334
  logger=logger,
2309
2335
  )
2336
+
2310
2337
  result['app_result'] = app_result
2311
2338
  if not app_result.get('success'):
2312
- raise DockerOpsError("Application compose failed to start or pass health checks")
2313
- logger.info(" Application stack is up and healthy")
2339
+ raise DockerOpsError("Application stack failed")
2340
+
2341
+ logger.info(f"{STICKERS['success']} Application stack ready")
2342
+ logger.info("")
2314
2343
  else:
2315
- logger.info("Step 3: application start skipped (dry run)")
2316
- # Step 4: Start Playwright compose using compose_up with unique project name
2317
- logger.info("Step 4: bringing up Playwright runner stack with compose_up")
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")
2318
2349
  if not dry_run:
2350
+ logger.info(f"{STICKERS['info']} Project: {project_name_playwright}")
2351
+ resolve_conflicts(playwright_compose_file,remove_conflicting_containers=True)
2319
2352
  up_success = compose_up(
2320
2353
  playwright_compose_file,
2321
2354
  services=playwright_services,
2322
2355
  project_name=project_name_playwright,
2323
2356
  )
2357
+
2324
2358
  if not up_success:
2325
2359
  raise DockerOpsError("Playwright compose_up failed")
2326
- logger.info(" Playwright stack started - now following logs until completion")
2360
+
2361
+ logger.info(f"{STICKERS['success']} Playwright started")
2362
+ logger.info(f"{STICKERS['waiting']} Following logs until completion...")
2363
+ logger.info("")
2327
2364
  else:
2328
- logger.info("Step 4: playwright start skipped (dry run)")
2329
- # Step 5: Follow logs and wait for completion (simulate logs -f with polling)
2365
+ logger.info(f"{STICKERS['info']} Playwright start skipped (dry run)")
2366
+ logger.info("")
2367
+
2368
+ # STEP 5: Log capture
2369
+ pw_services = []
2370
+ logs_acc = {}
2371
+
2330
2372
  if not dry_run and capture_logs:
2331
- logger.info("Step 5: following Playwright logs and waiting for completion")
2332
- pw_services = playwright_services or list(read_compose_file(playwright_compose_file, env_file).get('services', {}).keys())
2333
- logs_acc = {s: [] for s in pw_services}
2373
+ logger.info(f"{STICKERS['logs']} STEP 5: Capturing logs")
2374
+
2375
+ # Determine services to monitor
2376
+ compose_config = read_compose_file(playwright_compose_file, env_file)
2377
+ all_services = list(compose_config.get('services', {}).keys())
2378
+ pw_services = playwright_services or all_services
2379
+
2380
+ logger.info(f"{STICKERS['info']} Services: {', '.join(pw_services)}")
2381
+ logger.info(f"{STICKERS['info']} Tail: {log_tail} lines, Timeout: {health_timeout}s")
2382
+ logger.info("")
2383
+
2384
+ logs_acc = {service: [] for service in pw_services}
2334
2385
  deadline = time.time() + health_timeout
2335
2386
  finished = False
2336
- timed_out = False
2337
- time.sleep(5) # Give time for container to start producing logs
2338
- while not finished and not timed_out:
2339
- # Fetch current log snapshot
2340
- for svc in pw_services:
2387
+
2388
+ # Wait for containers to initialize
2389
+ time.sleep(3)
2390
+
2391
+ while not finished:
2392
+ current_time = time.time()
2393
+ if current_time > deadline:
2394
+ raise DockerOpsError(f"Playwright runner timed out after {health_timeout}s")
2395
+
2396
+ for service in pw_services:
2341
2397
  try:
2342
- lines = [l.message for l in compose_logs(playwright_compose_file, services=[svc], tail=log_tail, project_name=project_name_playwright,follow=True)]
2398
+ # Get fresh logs for this service
2399
+ log_lines = compose_logs(
2400
+ playwright_compose_file,
2401
+ services=[service],
2402
+ tail=log_tail,
2403
+ project_name=project_name_playwright,
2404
+ follow=True,
2405
+
2406
+ )
2407
+
2408
+ for log_line in log_lines:
2409
+ if hasattr(log_line, 'message'):
2410
+ log_message = log_line.message
2411
+ # Add only new log lines
2412
+ if log_message not in logs_acc[service]:
2413
+ logs_acc[service].append(log_message)
2414
+ # Print with clean formatting: only service name at the beginning
2415
+ logger.info(f"{STICKERS['info']} {service}")
2416
+ logger.info(log_message)
2343
2417
  except Exception as e:
2344
- lines = [f"Error fetching logs: {str(e)}"]
2345
- # Append new unique lines
2346
- for ln in lines:
2347
- if ln not in logs_acc[svc]:
2348
- logs_acc[svc].append(ln)
2349
- logger.info(f"[{svc}] {ln}") # Simulate real-time logging
2350
- # Check if all services have exited
2418
+ error_msg = f"Error fetching logs: {str(e)}"
2419
+ logger.error(f"{STICKERS['error']} {service} - {error_msg}")
2420
+
2421
+ # Check if all services have finished
2351
2422
  ps = compose_ps(playwright_compose_file, project_name=project_name_playwright)
2352
- running = any(
2353
- 'running' in (c.status or '').lower() or 'up' in (c.status or '').lower()
2354
- for c in ps if (c.service or '') in pw_services
2355
- )
2356
- if not running:
2423
+ running_count = 0
2424
+
2425
+ for container in ps:
2426
+ service_name = container.service or ''
2427
+ if service_name in pw_services:
2428
+ status = (container.status or '').lower()
2429
+ if 'running' in status or 'up' in status:
2430
+ running_count += 1
2431
+
2432
+ if running_count == 0:
2357
2433
  finished = True
2358
- if time.time() > deadline:
2359
- timed_out = True
2360
- if not finished and not timed_out:
2434
+ logger.info(f"{STICKERS['complete']} All services completed")
2435
+ logger.info("")
2436
+ else:
2361
2437
  time.sleep(health_interval)
2438
+
2362
2439
  result['logs_captured'] = logs_acc
2363
- result['steps']['playwright_logs'] = {
2364
- 'finished': finished,
2365
- 'timed_out': timed_out,
2366
- }
2367
- if timed_out:
2368
- raise DockerOpsError("Playwright runner timed out")
2369
- else:
2370
- if dry_run:
2371
- logger.info("Step 5: log following skipped (dry run)")
2372
- # Step 6: Analyze outcome
2373
- logger.info("Step 6: analyzing Playwright runner outcome")
2440
+
2441
+ # STEP 6: Analysis
2442
+ logger.info(f"{STICKERS['analysis']} STEP 6: Analyzing results")
2443
+
2374
2444
  if not dry_run:
2375
2445
  # Get exit codes
2376
2446
  ps = compose_ps(playwright_compose_file, project_name=project_name_playwright)
2377
2447
  exit_codes = {}
2378
- for c in ps:
2379
- if (c.service or '') in pw_services:
2380
- exit_code = getattr(c, 'exit_code', None) or 1 # Assume non-zero if missing
2381
- exit_codes[c.service] = exit_code
2448
+
2449
+ for container in ps:
2450
+ service_name = container.service or ''
2451
+ if service_name in pw_services:
2452
+ exit_code = getattr(container, 'exit_code', None) or 1
2453
+ exit_codes[service_name] = exit_code
2454
+
2455
+ # Check overall success
2382
2456
  all_success = all(code == 0 for code in exit_codes.values())
2383
- # Log analysis
2384
- aggregated_logs = '\n'.join(' '.join(v) for v in logs_acc.values())
2385
- failed = any(re.search(pat, aggregated_logs, re.I) for pat in ['FAILED', 'FAIL', 'ERROR', 'AssertionError', 'Test Failed'])
2386
- passed = any(re.search(pat, aggregated_logs, re.I) for pat in ['All tests passed', 'Tests passed', 'PASS'])
2387
- if all_success and (passed or not failed):
2457
+
2458
+ if all_success:
2388
2459
  result['success'] = True
2389
- logger.info(f"Playwright run successful ({test_id})")
2460
+ logger.info(f"{STICKERS['success']} " + "="*50)
2461
+ logger.info(f"{STICKERS['success']} TESTS PASSED")
2462
+ logger.info(f"{STICKERS['success']} Test ID: {test_id}")
2463
+ logger.info(f"{STICKERS['success']} " + "="*50)
2390
2464
  else:
2391
2465
  result['success'] = False
2392
- result['error'] = 'Playwright tests failed based on exit codes or logs'
2393
- logger.error(f"Playwright run failed ({test_id})")
2394
- result['steps']['analysis'] = {
2395
- 'exit_codes': exit_codes,
2396
- 'passed_patterns': passed,
2397
- 'failed_patterns': failed,
2398
- }
2466
+ result['error'] = 'Playwright tests failed'
2467
+ logger.error(f"{STICKERS['error']} " + "="*50)
2468
+ logger.error(f"{STICKERS['error']} TESTS FAILED")
2469
+ logger.error(f"{STICKERS['error']} Exit codes: {exit_codes}")
2470
+ logger.error(f"{STICKERS['error']} " + "="*50)
2399
2471
  else:
2400
- result['success'] = True # Assume success in dry run
2472
+ result['success'] = True
2473
+ logger.info(f"{STICKERS['info']} Analysis skipped (dry run)")
2474
+
2475
+ # Final timing
2401
2476
  result['duration'] = (dt_ops.current_datetime() - start_time).total_seconds()
2402
- if not keep_compose_up :
2477
+ logger.info(f"{STICKERS['info']} Duration: {result['duration']:.2f}s")
2478
+ logger.info("")
2479
+
2480
+ # Cleanup
2481
+ if not keep_compose_up and not dry_run:
2482
+ logger.info(f"{STICKERS['cleanup']} Cleaning up")
2483
+ logger.info(f"{STICKERS['info']} Stopping Playwright stack...")
2403
2484
  compose_down(
2404
2485
  compose_file=playwright_compose_file,
2405
2486
  project_name=project_name_playwright
2406
2487
  )
2407
- logger.info("Playwright compose down")
2488
+ logger.info(f"{STICKERS['success']} Playwright stopped")
2489
+
2490
+ logger.info(f"{STICKERS['info']} Stopping Application stack...")
2408
2491
  compose_down(
2409
2492
  compose_file=compose_file,
2493
+ project_name=project_name
2410
2494
  )
2411
- logger.info("all servise compose down")
2495
+ logger.info(f"{STICKERS['success']} Application stopped")
2496
+ logger.info(f"{STICKERS['success']} Cleanup complete")
2497
+ logger.info("")
2498
+
2499
+ # Final summary
2500
+ if result['success']:
2501
+ logger.info(f"{STICKERS['complete']} " + "="*70)
2502
+ logger.info(f"{STICKERS['complete']} TEST COMPLETE: SUCCESS")
2503
+ logger.info(f"{STICKERS['complete']} Duration: {result['duration']:.2f}s")
2504
+ logger.info(f"{STICKERS['complete']} Test ID: {test_id}")
2505
+ logger.info(f"{STICKERS['complete']} " + "="*70)
2506
+ else:
2507
+ logger.error(f"{STICKERS['error']} " + "="*70)
2508
+ logger.error(f"{STICKERS['error']} TEST COMPLETE: FAILED")
2509
+ logger.error(f"{STICKERS['error']} Duration: {result['duration']:.2f}s")
2510
+ logger.error(f"{STICKERS['error']} Test ID: {test_id}")
2511
+ logger.error(f"{STICKERS['error']} " + "="*70)
2512
+
2412
2513
  return result
2514
+
2413
2515
  except Exception as e:
2414
2516
  result['duration'] = (dt_ops.current_datetime() - start_time).total_seconds()
2415
2517
  result['error'] = str(e)
2416
- logger.error(f"Playwright test workflow failed ({test_id}): {e}")
2417
- if not keep_compose_up :
2418
- compose_down(
2419
- compose_file=playwright_compose_file,
2420
- project_name=project_name_playwright
2421
- )
2422
- logger.info("Playwright compose down")
2423
- compose_down(
2424
- compose_file=compose_file,
2425
- )
2426
- logger.info("all servise compose down")
2518
+
2519
+ logger.error(f"{STICKERS['error']} Test failed: {e}")
2520
+ logger.error(f"{STICKERS['info']} Duration: {result['duration']:.2f}s")
2521
+
2522
+ # Emergency cleanup
2523
+ if not keep_compose_up and not dry_run:
2524
+ logger.info(f"{STICKERS['cleanup']} Emergency cleanup")
2525
+ try:
2526
+ compose_down(
2527
+ compose_file=playwright_compose_file,
2528
+ project_name=project_name_playwright
2529
+ )
2530
+ except:
2531
+ pass
2532
+
2533
+ try:
2534
+ compose_down(
2535
+ compose_file=compose_file,
2536
+ project_name=project_name
2537
+ )
2538
+ except:
2539
+ pass
2540
+
2427
2541
  return result
2428
2542
 
2429
2543
 
@@ -4515,7 +4629,7 @@ def resolve_conflicts(
4515
4629
  remove_conflicting_containers: bool = False,
4516
4630
  remove_conflicting_networks: bool = False,
4517
4631
  remove_conflicting_volumes: bool = False,
4518
- force: bool = False,
4632
+ force: bool = True,
4519
4633
  project_name: Optional[str] = None,
4520
4634
  env_file: Optional[str] = None
4521
4635
  ) -> Dict[str, Any]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: utils_devops
3
- Version: 0.1.159
3
+ Version: 0.1.161
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=4sq_rD3HXSv4o7FYB1cLSaifh156Dd4NQFis63zRM-w,191911
12
+ utils_devops/extras/docker_ops.py,sha256=1ZyK3rUJXd9mI48wXZykrkiNFmQfxzk2i40swqWoN0s,195192
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.159.dist-info/METADATA,sha256=NoF2tuKHpXxO8NkbhJwNjQaD21PV_uNBiVOsWQxzzks,1903
23
- utils_devops-0.1.159.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
24
- utils_devops-0.1.159.dist-info/entry_points.txt,sha256=ei3B6ZL5yu6dOq-U1r8wsBdkXeg63RAyV7m8_ADaE6k,53
25
- utils_devops-0.1.159.dist-info/RECORD,,
22
+ utils_devops-0.1.161.dist-info/METADATA,sha256=K8OwtBHqkMBqahNYxrRqfZooJOsor_o6jbE9um0Jd9c,1903
23
+ utils_devops-0.1.161.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
24
+ utils_devops-0.1.161.dist-info/entry_points.txt,sha256=ei3B6ZL5yu6dOq-U1r8wsBdkXeg63RAyV7m8_ADaE6k,53
25
+ utils_devops-0.1.161.dist-info/RECORD,,