feat: cierra issues 0050 y 0052 + commands automáticos

- 0050: jupyter_exec reescrito sin Y.js (REST + KernelClient). Bug raíz adicional: HEAD /api/contents da 405 → cambiado a GET. 9 tests (5 unit + 4 e2e).
- 0052: footprint_aurgi cerrado. Bug fix en setup_geo_stack_docker_pipeline (verify aborta si compose up falla; nombre de contenedor incorrecto).
- Nueva primitiva docker_container_running_py_infra (7 tests).
- /full-git-push y /full-git-pull pasan a modo automático: auto-commit + push sin preguntar, aborta solo si detecta secrets.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 23:34:03 +02:00
parent 1e8ade0ed4
commit 5194de3c04
14 changed files with 684 additions and 342 deletions
@@ -0,0 +1,51 @@
---
name: docker_container_running
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "docker_container_running(name: str, timeout: float = 5.0) -> bool"
description: "Comprueba si un contenedor Docker existe y está corriendo. True solo si docker inspect retorna State.Running=true. Cualquier fallo (docker ausente, contenedor inexistente, daemon caído, timeout) devuelve False sin excepción."
tags: [docker, container, infra, healthcheck]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [subprocess]
params:
- name: name
desc: "Nombre o ID del contenedor a comprobar. Match exacto, no acepta wildcards."
- name: timeout
desc: "Timeout en segundos para `docker inspect`. Default 5.0. Si se supera devuelve False."
output: "True si el contenedor existe y está corriendo. False en cualquier otro caso (no existe, está parado, docker no disponible, timeout)."
tested: true
tests:
- "True para contenedor en ejecución (mock subprocess)"
- "False cuando docker inspect retorna 'false'"
- "False cuando el contenedor no existe (returncode != 0)"
- "False cuando docker no está instalado (FileNotFoundError)"
- "False cuando se excede el timeout (TimeoutExpired)"
- "Integration: comprueba contenedor real si docker disponible"
test_file_path: "python/functions/infra/tests/test_docker_container_running.py"
file_path: "python/functions/infra/docker_container_running.py"
---
## Ejemplo
```python
from python.functions.infra.docker_container_running import docker_container_running
if docker_container_running("better_maps_valhalla"):
print("Valhalla up")
else:
print("Valhalla down")
```
## Notas
Wrapper minimalista sobre `docker inspect -f '{{.State.Running}}'`. Pensado como
primitiva componible para verificadores de stack y health checks. No lanza
excepciones — un fallo de cualquier tipo se reporta como False, dejando que
el caller decida qué hacer (skip de test, reintentar, levantar el stack, etc.).
@@ -0,0 +1,28 @@
"""Comprueba si un contenedor Docker está corriendo por nombre."""
from __future__ import annotations
import subprocess
def docker_container_running(name: str, timeout: float = 5.0) -> bool:
"""Devuelve True si el contenedor `name` existe y está corriendo.
Usa `docker inspect -f '{{.State.Running}}' <name>`. Cualquier fallo
(docker no instalado, contenedor inexistente, daemon caído, timeout)
se interpreta como False.
"""
try:
proc = subprocess.run(
["docker", "inspect", "-f", "{{.State.Running}}", name],
capture_output=True,
text=True,
timeout=timeout,
)
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
return False
if proc.returncode != 0:
return False
return proc.stdout.strip().lower() == "true"
@@ -0,0 +1,59 @@
"""Tests para docker_container_running."""
from __future__ import annotations
import os
import shutil
import subprocess
import sys
from unittest.mock import patch
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", ".."))
from python.functions.infra.docker_container_running import docker_container_running
def _make_completed(stdout: str, returncode: int = 0) -> subprocess.CompletedProcess:
return subprocess.CompletedProcess(args=[], returncode=returncode, stdout=stdout, stderr="")
def test_running_true():
with patch("subprocess.run", return_value=_make_completed("true\n", 0)):
assert docker_container_running("anything") is True
def test_running_false_when_state_false():
with patch("subprocess.run", return_value=_make_completed("false\n", 0)):
assert docker_container_running("stopped_container") is False
def test_running_false_when_inspect_fails():
# docker inspect retorna != 0 cuando el contenedor no existe
with patch("subprocess.run", return_value=_make_completed("", 1)):
assert docker_container_running("nope") is False
def test_running_false_when_docker_missing():
with patch("subprocess.run", side_effect=FileNotFoundError):
assert docker_container_running("any") is False
def test_running_false_on_timeout():
with patch("subprocess.run", side_effect=subprocess.TimeoutExpired(cmd="docker", timeout=5.0)):
assert docker_container_running("any") is False
def test_running_strips_and_lowercases():
# docker a veces emite con trailing whitespace; aceptamos "True\n" y "TRUE"
for stdout in ("True\n", "TRUE", " true "):
with patch("subprocess.run", return_value=_make_completed(stdout, 0)):
assert docker_container_running("c") is True
def test_running_integration_real_docker():
"""Si docker está disponible, comprueba con un contenedor inexistente y devuelve False."""
if not shutil.which("docker"):
pytest.skip("docker no disponible en el PATH")
assert docker_container_running("__definitely_does_not_exist_xyz__") is False