utils_devops 0.1.157__tar.gz → 0.1.158__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.157 → utils_devops-0.1.158}/PKG-INFO +1 -1
- {utils_devops-0.1.157 → utils_devops-0.1.158}/pyproject.toml +1 -1
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/docker_ops.py +227 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/README.md +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/__init__.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/core/__init__.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/core/datetimes.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/core/envs.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/core/files.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/core/logs.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/core/script_helpers.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/core/strings.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/core/systems.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/__init__.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/aws_ops.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/git_ops.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/interaction_ops.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/metrics_ops.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/network_ops.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/nginx_ops.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/notification_ops.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/performance_ops.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/src/utils_devops/extras/ssh_ops.py +0 -0
- {utils_devops-0.1.157 → utils_devops-0.1.158}/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.158
|
|
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.158" # 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"
|
|
@@ -2200,6 +2200,233 @@ def push_compose(
|
|
|
2200
2200
|
logger.error(f"💥 Push {push_id} failed: {e}")
|
|
2201
2201
|
return result
|
|
2202
2202
|
|
|
2203
|
+
def playwright_test_compose(
|
|
2204
|
+
compose_file: str,
|
|
2205
|
+
playwright_compose_file: str,
|
|
2206
|
+
# Environment configuration
|
|
2207
|
+
env_file: str = '.env',
|
|
2208
|
+
update_version: bool = False,
|
|
2209
|
+
version_source: str = 'git',
|
|
2210
|
+
# Services selection
|
|
2211
|
+
services: Optional[List[str]] = None,
|
|
2212
|
+
playwright_services: Optional[List[str]] = None,
|
|
2213
|
+
# Health / timing
|
|
2214
|
+
health_timeout: int = 500,
|
|
2215
|
+
health_interval: int = 10,
|
|
2216
|
+
# Logging
|
|
2217
|
+
capture_logs: bool = True,
|
|
2218
|
+
log_tail: int = 200,
|
|
2219
|
+
# Compose startup options (delegated to test_compose)
|
|
2220
|
+
pull_missing: bool = True,
|
|
2221
|
+
no_build: bool = True,
|
|
2222
|
+
no_pull: bool = True,
|
|
2223
|
+
keep_up: bool = False,
|
|
2224
|
+
# Keep services up after each test_compose call so we can sequence them
|
|
2225
|
+
dry_run: bool = False,
|
|
2226
|
+
project_name: Optional[str] = None,
|
|
2227
|
+
logger: Optional[logger] = None,
|
|
2228
|
+
) -> Dict[str, Any]:
|
|
2229
|
+
"""
|
|
2230
|
+
Specialized workflow to run a Playwright-driven integration test that depends on an
|
|
2231
|
+
application compose and a separate Playwright compose. This function reuses the
|
|
2232
|
+
existing `test_compose` function to perform validation, startup and health checks for
|
|
2233
|
+
the application compose.
|
|
2234
|
+
|
|
2235
|
+
Behavior summary:
|
|
2236
|
+
1. Validate inputs and (optionally) update version as in test_compose.
|
|
2237
|
+
2. Set STATIC_CAPTCHA=true in the system process environment (without modifying the file).
|
|
2238
|
+
3. Call test_compose(...) against `compose_file` with keep_compose_up=True
|
|
2239
|
+
so the primary application stack is up and healthy.
|
|
2240
|
+
4. Run compose_up on the Playwright compose file to start the test runner.
|
|
2241
|
+
5. Follow logs (simulating -f) for the Playwright service(s) while polling the compose state
|
|
2242
|
+
to detect completion, and determine pass/fail from exit status and log patterns.
|
|
2243
|
+
6. Return a detailed result dict similar in structure to test_compose.
|
|
2244
|
+
|
|
2245
|
+
Notes:
|
|
2246
|
+
- Reuses `test_compose` for application startup and health checks.
|
|
2247
|
+
- Sets STATIC_CAPTCHA in the process environment only (os.environ).
|
|
2248
|
+
- Does not automatically bring services down; that is left to the caller.
|
|
2249
|
+
- Success is determined by all containers exiting with code 0 and/or presence of success patterns in logs.
|
|
2250
|
+
|
|
2251
|
+
Returns:
|
|
2252
|
+
Dict with keys: test_id, success, steps, app_result, logs_captured, duration, error
|
|
2253
|
+
"""
|
|
2254
|
+
logger = logger or DEFAULT_LOGGER
|
|
2255
|
+
test_id = str(uuid.uuid4())[:8]
|
|
2256
|
+
start_time = dt_ops.current_datetime()
|
|
2257
|
+
result = {
|
|
2258
|
+
"test_id": test_id,
|
|
2259
|
+
"success": False,
|
|
2260
|
+
"steps": {},
|
|
2261
|
+
"app_result": None,
|
|
2262
|
+
"logs_captured": {},
|
|
2263
|
+
"duration": 0.0,
|
|
2264
|
+
"error": None,
|
|
2265
|
+
}
|
|
2266
|
+
try:
|
|
2267
|
+
logger.info(f"▶️ Playwright test workflow starting ({test_id})")
|
|
2268
|
+
logger.info(f" Compose files: app={compose_file} playwright={playwright_compose_file}")
|
|
2269
|
+
# Step 0: Basic validation
|
|
2270
|
+
logger.info("Step 0: validating compose and env files")
|
|
2271
|
+
if not files.file_exists(compose_file):
|
|
2272
|
+
raise DockerOpsError(f"Application compose file not found: {compose_file}")
|
|
2273
|
+
if not files.file_exists(playwright_compose_file):
|
|
2274
|
+
raise DockerOpsError(f"Playwright compose file not found: {playwright_compose_file}")
|
|
2275
|
+
# Step 1: Optional version update
|
|
2276
|
+
if update_version and not dry_run:
|
|
2277
|
+
logger.info("Step 1: updating version in env (requested)")
|
|
2278
|
+
new_version = _update_env_version(env_file=env_file, source=version_source, logger=logger)
|
|
2279
|
+
if new_version:
|
|
2280
|
+
logger.info(f" Version updated to {new_version}")
|
|
2281
|
+
# make sure runtime env sees new values
|
|
2282
|
+
envs.import_env_to_system(get_env_compose(env_file))
|
|
2283
|
+
elif update_version and dry_run:
|
|
2284
|
+
logger.info("Step 1: version update skipped (dry run)")
|
|
2285
|
+
else:
|
|
2286
|
+
logger.info("Step 1: version update not requested")
|
|
2287
|
+
# Step 2: Set STATIC_CAPTCHA=true in system process env
|
|
2288
|
+
logger.info("Step 2: setting STATIC_CAPTCHA=true in process environment")
|
|
2289
|
+
if not dry_run:
|
|
2290
|
+
envs.set_system_env(key='STATIC_CAPTCHA', value='true')
|
|
2291
|
+
else:
|
|
2292
|
+
logger.info("Step 2: env set skipped (dry run)")
|
|
2293
|
+
# Step 3: Start application compose using test_compose but keep services up
|
|
2294
|
+
logger.info("Step 3: bringing up application stack (health checks via test_compose)")
|
|
2295
|
+
if not dry_run:
|
|
2296
|
+
app_result = test_compose(
|
|
2297
|
+
compose_file=compose_file,
|
|
2298
|
+
env_file=env_file,
|
|
2299
|
+
update_version=False, # already handled
|
|
2300
|
+
services=services,
|
|
2301
|
+
health_timeout=health_timeout,
|
|
2302
|
+
health_interval=health_interval,
|
|
2303
|
+
capture_logs=capture_logs,
|
|
2304
|
+
log_tail=log_tail,
|
|
2305
|
+
pull_missing=pull_missing,
|
|
2306
|
+
no_build=no_build,
|
|
2307
|
+
no_pull=no_pull,
|
|
2308
|
+
keep_compose_up=True, # DO NOT tear down
|
|
2309
|
+
project_name=project_name,
|
|
2310
|
+
dry_run=dry_run,
|
|
2311
|
+
logger=logger,
|
|
2312
|
+
)
|
|
2313
|
+
result['app_result'] = app_result
|
|
2314
|
+
if not app_result.get('success'):
|
|
2315
|
+
raise DockerOpsError("Application compose failed to start or pass health checks")
|
|
2316
|
+
logger.info(" Application stack is up and healthy")
|
|
2317
|
+
else:
|
|
2318
|
+
logger.info("Step 3: application start skipped (dry run)")
|
|
2319
|
+
# Step 4: Start Playwright compose using compose_up
|
|
2320
|
+
logger.info("Step 4: bringing up Playwright runner stack with compose_up")
|
|
2321
|
+
if not dry_run:
|
|
2322
|
+
up_success = compose_up(
|
|
2323
|
+
playwright_compose_file,
|
|
2324
|
+
services=playwright_services,
|
|
2325
|
+
project_name=project_name,
|
|
2326
|
+
# Assume detached by default or add param if needed; help shows no param for detached, perhaps always detached
|
|
2327
|
+
)
|
|
2328
|
+
if not up_success:
|
|
2329
|
+
raise DockerOpsError("Playwright compose_up failed")
|
|
2330
|
+
logger.info(" Playwright stack started - now following logs until completion")
|
|
2331
|
+
else:
|
|
2332
|
+
logger.info("Step 4: playwright start skipped (dry run)")
|
|
2333
|
+
# Step 5: Follow logs and wait for completion (simulate logs -f with polling)
|
|
2334
|
+
if not dry_run and capture_logs:
|
|
2335
|
+
logger.info("Step 5: following Playwright logs and waiting for completion")
|
|
2336
|
+
pw_services = playwright_services or list(read_compose_file(playwright_compose_file, env_file).get('services', {}).keys())
|
|
2337
|
+
logs_acc = {s: [] for s in pw_services}
|
|
2338
|
+
deadline = time.time() + health_timeout
|
|
2339
|
+
finished = False
|
|
2340
|
+
timed_out = False
|
|
2341
|
+
while not finished and not timed_out:
|
|
2342
|
+
# Fetch current log snapshot
|
|
2343
|
+
for svc in pw_services:
|
|
2344
|
+
try:
|
|
2345
|
+
lines = [l.text for l in compose_logs(playwright_compose_file, services=[svc], tail=log_tail)]
|
|
2346
|
+
except Exception as e:
|
|
2347
|
+
lines = [f"Error fetching logs: {str(e)}"]
|
|
2348
|
+
# Append new unique lines
|
|
2349
|
+
for ln in lines:
|
|
2350
|
+
if ln not in logs_acc[svc]:
|
|
2351
|
+
logs_acc[svc].append(ln)
|
|
2352
|
+
logger.info(f"[{svc}] {ln}") # Simulate real-time logging
|
|
2353
|
+
# Check if all services have exited
|
|
2354
|
+
ps = compose_ps(playwright_compose_file, project_name=project_name)
|
|
2355
|
+
running = any(
|
|
2356
|
+
'running' in (c.status or '').lower() or 'up' in (c.status or '').lower()
|
|
2357
|
+
for c in ps if (c.service or '') in pw_services
|
|
2358
|
+
)
|
|
2359
|
+
if not running:
|
|
2360
|
+
finished = True
|
|
2361
|
+
if time.time() > deadline:
|
|
2362
|
+
timed_out = True
|
|
2363
|
+
if not finished and not timed_out:
|
|
2364
|
+
time.sleep(health_interval)
|
|
2365
|
+
result['logs_captured'] = logs_acc
|
|
2366
|
+
result['steps']['playwright_logs'] = {
|
|
2367
|
+
'finished': finished,
|
|
2368
|
+
'timed_out': timed_out,
|
|
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")
|
|
2377
|
+
if not dry_run:
|
|
2378
|
+
# Get exit codes
|
|
2379
|
+
ps = compose_ps(playwright_compose_file, project_name=project_name)
|
|
2380
|
+
exit_codes = {}
|
|
2381
|
+
for c in ps:
|
|
2382
|
+
if (c.service or '') in pw_services:
|
|
2383
|
+
exit_code = getattr(c, 'exit_code', None) or 1 # Assume non-zero if missing
|
|
2384
|
+
exit_codes[c.service] = exit_code
|
|
2385
|
+
all_success = all(code == 0 for code in exit_codes.values())
|
|
2386
|
+
# Log analysis
|
|
2387
|
+
aggregated_logs = '\n'.join(' '.join(v) for v in logs_acc.values())
|
|
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):
|
|
2391
|
+
result['success'] = True
|
|
2392
|
+
logger.info(f"Playwright run successful ({test_id})")
|
|
2393
|
+
else:
|
|
2394
|
+
result['success'] = False
|
|
2395
|
+
result['error'] = 'Playwright tests failed based on exit codes or logs'
|
|
2396
|
+
logger.error(f"Playwright run failed ({test_id})")
|
|
2397
|
+
result['steps']['analysis'] = {
|
|
2398
|
+
'exit_codes': exit_codes,
|
|
2399
|
+
'passed_patterns': passed,
|
|
2400
|
+
'failed_patterns': failed,
|
|
2401
|
+
}
|
|
2402
|
+
else:
|
|
2403
|
+
result['success'] = True # Assume success in dry run
|
|
2404
|
+
result['duration'] = (dt_ops.current_datetime() - start_time).total_seconds()
|
|
2405
|
+
if not keep_up :
|
|
2406
|
+
compose_down(
|
|
2407
|
+
compose_file=playwright_compose_file,
|
|
2408
|
+
)
|
|
2409
|
+
logger.info("Playwright compose down")
|
|
2410
|
+
compose_down(
|
|
2411
|
+
compose_file=compose_file,
|
|
2412
|
+
)
|
|
2413
|
+
logger.info("all servise compose down")
|
|
2414
|
+
return result
|
|
2415
|
+
except Exception as e:
|
|
2416
|
+
result['duration'] = (dt_ops.current_datetime() - start_time).total_seconds()
|
|
2417
|
+
result['error'] = str(e)
|
|
2418
|
+
logger.error(f"Playwright test workflow failed ({test_id}): {e}")
|
|
2419
|
+
if not keep_up :
|
|
2420
|
+
compose_down(
|
|
2421
|
+
compose_file=playwright_compose_file,
|
|
2422
|
+
)
|
|
2423
|
+
logger.info("Playwright compose down")
|
|
2424
|
+
compose_down(
|
|
2425
|
+
compose_file=compose_file,
|
|
2426
|
+
)
|
|
2427
|
+
logger.info("all servise compose down")
|
|
2428
|
+
return result
|
|
2429
|
+
|
|
2203
2430
|
|
|
2204
2431
|
def test_compose(
|
|
2205
2432
|
compose_file: str,
|
|
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
|