taskfile 0.3.74__tar.gz → 0.3.76__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.
- {taskfile-0.3.74/src/taskfile.egg-info → taskfile-0.3.76}/PKG-INFO +1 -1
- {taskfile-0.3.74 → taskfile-0.3.76}/pyproject.toml +1 -1
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/__init__.py +1 -1
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/interactive/wizards.py +109 -100
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/diagnostics/checks.py +210 -455
- taskfile-0.3.76/src/taskfile/diagnostics/checks_ports.py +157 -0
- taskfile-0.3.76/src/taskfile/diagnostics/checks_ssh.py +230 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/models.py +8 -2
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/runner/commands.py +71 -53
- {taskfile-0.3.74 → taskfile-0.3.76/src/taskfile.egg-info}/PKG-INFO +1 -1
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile.egg-info/SOURCES.txt +2 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_dsl_commands.py +13 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/LICENSE +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/README.md +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/setup.cfg +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/__main__.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/addons/__init__.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/addons/monitoring.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/addons/postgres.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/addons/redis_addon.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/api/__init__.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/api/app.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/api/models.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cache.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cigen/__init__.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cigen/base.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cigen/drone.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cigen/gitea.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cigen/github.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cigen/gitlab.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cigen/jenkins.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cigen/makefile.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cirunner.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/__init__.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/api_cmd.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/auth.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/cache_cmds.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/ci.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/click_compat.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/completion.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/deploy.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/diagnostics.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/docker_cmds.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/e2e_cmd.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/explain_cmd.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/fleet.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/health.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/import_export.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/info_cmd.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/interactive/__init__.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/interactive/menu.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/main.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/quadlet.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/registry_cmds.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/release.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/setup.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/cli/version.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/compose.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/converters.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/deploy_recipes.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/deploy_utils.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/diagnostics/__init__.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/diagnostics/fixes.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/diagnostics/llm_repair.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/diagnostics/models.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/diagnostics/report.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/fleet.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/graph.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/health.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/importer.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/landing.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/notifications.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/parser.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/provisioner.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/quadlet.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/registry.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/runner/__init__.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/runner/core.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/runner/error_presenter.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/runner/explainer.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/runner/functions.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/runner/resolver.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/runner/ssh.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/__init__.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/codereview.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/full.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/minimal.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/multiplatform.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/podman.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/publish.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/codereview.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/full.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/iot.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/kubernetes.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/minimal.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/multiplatform.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/podman.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/publish.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/saas.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/terraform.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/templates/web.yml +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/scaffold/web.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/ssh.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/watch.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/webui/__init__.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/webui/dashboard.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/webui/handlers.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile/webui/server.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile.egg-info/dependency_links.txt +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile.egg-info/entry_points.txt +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile.egg-info/requires.txt +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/src/taskfile.egg-info/top_level.txt +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_api.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_auth.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_cigen.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_cli.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_compose.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_diagnostics.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_docker_e2e.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_doctor_e2e.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_e2e_examples.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_fleet.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_health.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_landing.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_models.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_parser.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_provisioner.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_quadlet.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_release.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_resolver.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_runner.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_scaffold.py +0 -0
- {taskfile-0.3.74 → taskfile-0.3.76}/tests/test_setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: taskfile
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.76
|
|
4
4
|
Summary: Universal Taskfile runner with multi-environment deploy support. CI/CD agnostic — run locally or from any pipeline.
|
|
5
5
|
Author-email: Tom Sapletta <tom@sapletta.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "taskfile"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.76"
|
|
8
8
|
description = "Universal Taskfile runner with multi-environment deploy support. CI/CD agnostic — run locally or from any pipeline."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -342,126 +342,135 @@ def _run_llm_assist(diagnostics: ProjectDiagnostics) -> None:
|
|
|
342
342
|
console.print(f" 💡 {suggestion}")
|
|
343
343
|
|
|
344
344
|
|
|
345
|
-
def
|
|
346
|
-
"""
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
345
|
+
def _ssh_run(ssh_cmd: str, remote_cmd: str, timeout: int = 10) -> subprocess.CompletedProcess | None:
|
|
346
|
+
"""Run a single command over SSH. Returns CompletedProcess or None on timeout/error."""
|
|
347
|
+
try:
|
|
348
|
+
return subprocess.run(
|
|
349
|
+
f"{ssh_cmd} '{remote_cmd}'",
|
|
350
|
+
shell=True, capture_output=True, text=True, timeout=timeout,
|
|
351
|
+
)
|
|
352
|
+
except (subprocess.TimeoutExpired, OSError):
|
|
353
|
+
return None
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _check_remote_podman(ssh_cmd: str, host: str) -> None:
|
|
357
|
+
"""Check podman version on remote host."""
|
|
358
|
+
result = _ssh_run(ssh_cmd, 'podman --version 2>/dev/null || echo "NOT_INSTALLED"')
|
|
359
|
+
if result and result.returncode == 0 and "NOT_INSTALLED" not in result.stdout:
|
|
360
|
+
version = result.stdout.strip().replace("podman version ", "").replace("Version: ", "")
|
|
361
|
+
console.print(f"[green] ✓ Podman: {version}[/]")
|
|
362
|
+
else:
|
|
363
|
+
console.print(f"[yellow] ⚠ Podman not installed on {host}[/]")
|
|
350
364
|
|
|
351
|
-
console.print("\n[bold cyan]🔍 Remote Server Diagnostics[/]")
|
|
352
365
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
366
|
+
def _check_remote_disk(ssh_cmd: str) -> None:
|
|
367
|
+
"""Check disk space on remote host."""
|
|
368
|
+
result = _ssh_run(ssh_cmd, 'df -h / | tail -1')
|
|
369
|
+
if result and result.returncode == 0:
|
|
370
|
+
parts = result.stdout.strip().split()
|
|
371
|
+
if len(parts) >= 5:
|
|
372
|
+
console.print(f"[dim] Disk: {parts[4]} used ({parts[2]} / {parts[1]})[/]")
|
|
359
373
|
|
|
360
|
-
|
|
374
|
+
|
|
375
|
+
def _check_remote_memory(ssh_cmd: str) -> None:
|
|
376
|
+
"""Check memory on remote host."""
|
|
377
|
+
result = _ssh_run(ssh_cmd, 'free -h 2>/dev/null | grep Mem || echo "N/A"')
|
|
378
|
+
if result and result.returncode == 0 and result.stdout.strip() != "N/A":
|
|
379
|
+
mem_parts = result.stdout.strip().split()
|
|
380
|
+
if len(mem_parts) >= 4:
|
|
381
|
+
console.print(f"[dim] Memory: {mem_parts[2]} used / {mem_parts[1]} total[/]")
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _check_remote_containers(ssh_cmd: str) -> None:
|
|
385
|
+
"""Check running container and image counts on remote host."""
|
|
386
|
+
result = _ssh_run(ssh_cmd, 'podman ps -q 2>/dev/null | wc -l')
|
|
387
|
+
if result and result.returncode == 0:
|
|
388
|
+
console.print(f"[dim] Running containers: {result.stdout.strip()}[/]")
|
|
389
|
+
|
|
390
|
+
result = _ssh_run(ssh_cmd, 'podman images -q 2>/dev/null | wc -l')
|
|
391
|
+
if result and result.returncode == 0:
|
|
392
|
+
console.print(f"[dim] Stored images: {result.stdout.strip()}[/]")
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def _resolve_selected_env() -> str | None:
|
|
396
|
+
"""Read --env flag from parent click context (taskfile --env prod doctor --remote)."""
|
|
361
397
|
ctx = click.get_current_context(silent=True)
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
398
|
+
if not ctx:
|
|
399
|
+
return None
|
|
400
|
+
parent = ctx.parent
|
|
401
|
+
while parent:
|
|
402
|
+
selected = (parent.params or {}).get("env_name")
|
|
403
|
+
if selected:
|
|
404
|
+
return selected
|
|
405
|
+
parent = parent.parent
|
|
406
|
+
return None
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def _collect_remote_envs(config, selected_env: str | None) -> list[tuple] | None:
|
|
410
|
+
"""Build list of remote environments to check. Returns None on user-facing error."""
|
|
373
411
|
if selected_env:
|
|
374
412
|
env_obj = config.environments.get(selected_env)
|
|
375
413
|
if env_obj and env_obj.is_remote:
|
|
376
|
-
|
|
377
|
-
|
|
414
|
+
return [(selected_env, env_obj)]
|
|
415
|
+
if env_obj:
|
|
378
416
|
console.print(f"[yellow] Environment '{selected_env}' is not remote (no ssh_host)[/]")
|
|
379
|
-
return
|
|
380
417
|
else:
|
|
381
418
|
console.print(f"[yellow] Environment '{selected_env}' not found in Taskfile.yml[/]")
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if not remote_envs:
|
|
419
|
+
return None
|
|
420
|
+
envs = [(name, env) for name, env in config.environments.items() if env.is_remote]
|
|
421
|
+
if not envs:
|
|
387
422
|
console.print("[yellow] No remote environments configured in Taskfile.yml[/]")
|
|
388
|
-
return
|
|
423
|
+
return None
|
|
424
|
+
return envs
|
|
389
425
|
|
|
390
|
-
taskfile_dir = tf.parent
|
|
391
426
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
427
|
+
def _diagnose_single_remote_env(env_name, env, taskfile_dir) -> None:
|
|
428
|
+
"""Run all remote diagnostics for a single environment."""
|
|
429
|
+
from taskfile.deploy_utils import test_ssh_connection
|
|
430
|
+
from taskfile.diagnostics.checks import _resolve_env_fields
|
|
395
431
|
|
|
396
|
-
|
|
432
|
+
_resolve_env_fields(env, taskfile_dir)
|
|
433
|
+
console.print(f"\n[bold]Environment: {env_name}[/] ({env.ssh_host})")
|
|
397
434
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
if
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
continue
|
|
435
|
+
ssh_result = test_ssh_connection(env.ssh_host, env.ssh_user, env.ssh_port)
|
|
436
|
+
if not ssh_result.success:
|
|
437
|
+
detail = ssh_result.output.strip() if ssh_result.output and ssh_result.output.strip() else f"exit code {ssh_result.exit_code}"
|
|
438
|
+
console.print(f"[red] ✗ SSH connection failed: {detail}[/]")
|
|
439
|
+
console.print(f"[dim] ssh {env.ssh_opts} {env.ssh_user}@{env.ssh_host} 'echo ok'[/]")
|
|
440
|
+
return
|
|
405
441
|
|
|
406
|
-
|
|
442
|
+
console.print(f"[green] ✓ SSH connected to {env.ssh_host}[/]")
|
|
443
|
+
ssh_cmd = f"ssh {env.ssh_opts} {env.ssh_user}@{env.ssh_host}"
|
|
407
444
|
|
|
408
|
-
|
|
409
|
-
ssh_cmd
|
|
445
|
+
try:
|
|
446
|
+
_check_remote_podman(ssh_cmd, env.ssh_host)
|
|
447
|
+
_check_remote_disk(ssh_cmd)
|
|
448
|
+
_check_remote_memory(ssh_cmd)
|
|
449
|
+
_check_remote_containers(ssh_cmd)
|
|
450
|
+
except Exception as e:
|
|
451
|
+
console.print(f"[yellow] Remote diagnostics error: {e}[/]")
|
|
410
452
|
|
|
411
|
-
try:
|
|
412
|
-
# Check podman version
|
|
413
|
-
result = subprocess.run(
|
|
414
|
-
f"{ssh_cmd} 'podman --version 2>/dev/null || echo \"NOT_INSTALLED\"'",
|
|
415
|
-
shell=True, capture_output=True, text=True, timeout=10
|
|
416
|
-
)
|
|
417
|
-
if result.returncode == 0 and "NOT_INSTALLED" not in result.stdout:
|
|
418
|
-
version = result.stdout.strip().replace("podman version ", "").replace("Version: ", "")
|
|
419
|
-
console.print(f"[green] ✓ Podman: {version}[/]")
|
|
420
|
-
else:
|
|
421
|
-
console.print(f"[yellow] ⚠ Podman not installed on {env.ssh_host}[/]")
|
|
422
453
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
shell=True, capture_output=True, text=True, timeout=10
|
|
427
|
-
)
|
|
428
|
-
if result.returncode == 0:
|
|
429
|
-
parts = result.stdout.strip().split()
|
|
430
|
-
if len(parts) >= 5:
|
|
431
|
-
console.print(f"[dim] Disk: {parts[4]} used ({parts[2]} / {parts[1]})[/]")
|
|
432
|
-
|
|
433
|
-
# Check memory
|
|
434
|
-
result = subprocess.run(
|
|
435
|
-
f"{ssh_cmd} 'free -h 2>/dev/null | grep Mem || echo \"N/A\"'",
|
|
436
|
-
shell=True, capture_output=True, text=True, timeout=10
|
|
437
|
-
)
|
|
438
|
-
if result.returncode == 0 and result.stdout.strip() != "N/A":
|
|
439
|
-
mem_parts = result.stdout.strip().split()
|
|
440
|
-
if len(mem_parts) >= 4:
|
|
441
|
-
console.print(f"[dim] Memory: {mem_parts[2]} used / {mem_parts[1]} total[/]")
|
|
442
|
-
|
|
443
|
-
# Check running containers count
|
|
444
|
-
result = subprocess.run(
|
|
445
|
-
f"{ssh_cmd} 'podman ps -q 2>/dev/null | wc -l'",
|
|
446
|
-
shell=True, capture_output=True, text=True, timeout=10
|
|
447
|
-
)
|
|
448
|
-
if result.returncode == 0:
|
|
449
|
-
container_count = result.stdout.strip()
|
|
450
|
-
console.print(f"[dim] Running containers: {container_count}[/]")
|
|
451
|
-
|
|
452
|
-
# Check images
|
|
453
|
-
result = subprocess.run(
|
|
454
|
-
f"{ssh_cmd} 'podman images -q 2>/dev/null | wc -l'",
|
|
455
|
-
shell=True, capture_output=True, text=True, timeout=10
|
|
456
|
-
)
|
|
457
|
-
if result.returncode == 0:
|
|
458
|
-
image_count = result.stdout.strip()
|
|
459
|
-
console.print(f"[dim] Stored images: {image_count}[/]")
|
|
454
|
+
def _run_remote_diagnostics(diagnostics: ProjectDiagnostics) -> None:
|
|
455
|
+
"""Extended remote diagnostics — check containers, images, system status on remote server."""
|
|
456
|
+
from taskfile.parser import find_taskfile, load_taskfile
|
|
460
457
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
458
|
+
console.print("\n[bold cyan]🔍 Remote Server Diagnostics[/]")
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
tf = find_taskfile()
|
|
462
|
+
config = load_taskfile(tf)
|
|
463
|
+
except Exception:
|
|
464
|
+
console.print("[yellow] Could not load Taskfile config for remote diagnostics[/]")
|
|
465
|
+
return
|
|
466
|
+
|
|
467
|
+
selected_env = _resolve_selected_env()
|
|
468
|
+
remote_envs = _collect_remote_envs(config, selected_env)
|
|
469
|
+
if remote_envs is None:
|
|
470
|
+
return
|
|
471
|
+
|
|
472
|
+
for env_name, env in remote_envs:
|
|
473
|
+
_diagnose_single_remote_env(env_name, env, tf.parent)
|
|
465
474
|
|
|
466
475
|
|
|
467
476
|
class _nullcontext:
|