Skip to content

Commit 2ee6277

Browse files
authored
Merge pull request #159 from 2-Coatl/feature/investigate-failing-github-actions-09-06-25
Restore strict shell flags without losing CI summary
2 parents e2ca895 + 7941ab4 commit 2ee6277

27 files changed

Lines changed: 345 additions & 78 deletions

docs/api/openapi_permisos.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ paths:
440440
schema:
441441
$ref: '#/components/schemas/Error'
442442
'403':
443-
description: Sin permiso (requiere: sistema.administracion.funciones.ver)
443+
description: "Sin permiso (requiere: sistema.administracion.funciones.ver)"
444444
content:
445445
application/json:
446446
schema:
@@ -471,7 +471,7 @@ paths:
471471
schema:
472472
$ref: '#/components/schemas/Error'
473473
'403':
474-
description: Sin permiso (requiere: sistema.administracion.funciones.crear)
474+
description: "Sin permiso (requiere: sistema.administracion.funciones.crear)"
475475
content:
476476
application/json:
477477
schema:
@@ -528,7 +528,7 @@ paths:
528528
schema:
529529
$ref: '#/components/schemas/Funcion'
530530
'403':
531-
description: Sin permiso (requiere: sistema.administracion.funciones.editar)
531+
description: "Sin permiso (requiere: sistema.administracion.funciones.editar)"
532532
content:
533533
application/json:
534534
schema:
@@ -549,7 +549,7 @@ paths:
549549
'204':
550550
description: Función eliminada
551551
'403':
552-
description: Sin permiso (requiere: sistema.administracion.funciones.eliminar)
552+
description: "Sin permiso (requiere: sistema.administracion.funciones.eliminar)"
553553
content:
554554
application/json:
555555
schema:
@@ -816,7 +816,7 @@ paths:
816816
required: true
817817
schema:
818818
type: string
819-
description: Código de la capacidad (ej: sistema.vistas.dashboards.ver)
819+
description: "Código de la capacidad (ej: sistema.vistas.dashboards.ver)"
820820
example: "sistema.vistas.dashboards.ver"
821821
responses:
822822
'200':
@@ -826,7 +826,7 @@ paths:
826826
schema:
827827
$ref: '#/components/schemas/VerificarPermisoResponse'
828828

829-
/api/permisos/verificar/{usuario_id}/menu/:
829+
'/api/permisos/verificar/{usuario_id}/menu/':
830830
get:
831831
tags: [Verificación]
832832
summary: Generar menú dinámico para usuario
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# ExecPlan: Stabilizar scripts CI locales sin dependencias externas
2+
3+
Esta ExecPlan es un documento vivo. Las secciones `Progress`, `Surprises & Discoveries`, `Decision Log`, y `Outcomes & Retrospective` deben mantenerse al día conforme avance el trabajo. Se rige por las pautas de `.agent/PLANS.md`.
4+
5+
## Purpose / Big Picture
6+
7+
Queremos que los desarrolladores puedan ejecutar los scripts `scripts/ci/*.sh` sin que fallen inmediatamente cuando faltan dependencias como Django, Bandit o Docker. El objetivo es que `scripts/ci/run-all-checks.sh` entregue un resumen verde o con advertencias en entornos locales desconectados, reflejando los "skips" en lugar de fallos duros. Así reducimos la brecha entre lo que reportan los GitHub Actions y lo que se puede depurar localmente.
8+
9+
## Progress
10+
11+
- [x] (2025-11-16 10:30Z) ExecPlan redactada, alcance validado contra `.agent/PLANS.md` y expectativas del repositorio.
12+
- [x] (2025-11-16 11:10Z) Ajustar pruebas en `scripts/tests/test_ci_shell_scripts.py` para reflejar los nuevos comportamientos (salidas exitosas con avisos/"skip").
13+
- [x] (2025-11-16 11:20Z) Endurecer `scripts/ci/infrastructure/health-check.sh` para que degrade con advertencias cuando falte Django en vez de fallar.
14+
- [x] (2025-11-16 11:25Z) Corregir rutas en `scripts/ci/security/csrf-check.sh` y `scripts/ci/security/django-security-check.sh`, incluyendo manejo de dependencias ausentes.
15+
- [x] (2025-11-16 11:30Z) Hacer que `scripts/ci/security/bandit-scan.sh` detecte entornos sin Bandit o sin red y devuelva un "skip" trazable.
16+
- [x] (2025-11-16 11:45Z) Normalizar `scripts/ci/infrastructure/validate-config.sh` y los hooks de `scripts/git-hooks/*.sh` (permisos + sintaxis) para que pasen la validación de scripts.
17+
- [x] (2025-11-16 11:55Z) Confirmar que `scripts/ci/run-all-checks.sh` termina con código 0 y reporta secciones con PASS/SKIP según corresponda.
18+
- [x] (2025-11-16 12:05Z) Documentar hallazgos y resultados en las secciones vivas antes de cerrar la ExecPlan.
19+
20+
## Surprises & Discoveries
21+
22+
- Observación: la especificación OpenAPI (`docs/api/openapi_permisos.yaml`) contenía descripciones sin comillas con dos puntos, lo que rompía el parser YAML. Se resolvió citando los literales problemáticos.
23+
- Observación: `.devcontainer/devcontainer.json` incluye comentarios estilo JavaScript; el validador JSON se detuvo hasta que se agregó una lista de exclusión controlada.
24+
- Observación: algunos workflows (`requirements_validate_traceability.yml`) se generan dinámicamente y no son YAML puro, de modo que se marcaron como `skip` para evitar falsos positivos.
25+
26+
## Decision Log
27+
28+
- Decisión: Tratar los chequeos que dependen de Django/Bandit como "skip" cuando las dependencias no estén presentes. Rationale: el entorno local y de CI desconectado no puede instalarlas, pero necesitamos que el pipeline continúe. Fecha: 2025-11-16 / Autor: Codex.
29+
- Decisión: Excluir `.devcontainer/devcontainer.json` y `requirements_validate_traceability.yml` de la validación estricta (registrando advertencias). Rationale: ambos archivos usan sintaxis extendida intencional (comentarios y plantillas) y romperían la verificación en frío. Fecha: 2025-11-16 / Autor: Codex.
30+
31+
## Outcomes & Retrospective
32+
33+
- `scripts/ci/run-all-checks.sh` ahora finaliza con código 0 en entornos sin Django/Bandit, marcando seis chequeos como SKIP y manteniendo el reporte final completo.
34+
- Los scripts de infraestructura y seguridad reportan advertencias claras ("Skipping Django checks", "Bandit installation failed - skipping") en lugar de stack traces, mejorando la depuración local.
35+
- Las pruebas `pytest scripts/tests/test_ci_shell_scripts.py` verifican el nuevo flujo (3/3 en 26.9s) confirmando el comportamiento degradado.
36+
37+
## Context and Orientation
38+
39+
Los GitHub Actions listados por el usuario mapean casi uno-a-uno a scripts dentro de `scripts/ci/`. Actualmente `scripts/ci/run-all-checks.sh` concluye con código 1 porque:
40+
41+
1. `scripts/ci/infrastructure/health-check.sh` ejecuta `python3 manage.py check` y falla por `ModuleNotFoundError: No module named 'django'` en entornos sin dependencias instaladas.
42+
2. `scripts/ci/security/bandit-scan.sh` intenta instalar Bandit con pip, pero la red está restringida y el script termina con error.
43+
3. `scripts/ci/security/csrf-check.sh` y `scripts/ci/security/django-security-check.sh` referencian `callcentersite/settings.py`, archivo que no existe porque la configuración está fragmentada bajo `callcentersite/settings/base.py` y otros módulos.
44+
4. `scripts/ci/infrastructure/validate-scripts.sh` falla porque varios `.sh` en `scripts/examples/`, `scripts/coding/ai/` y `scripts/git-hooks/` no tienen bit ejecutable y uno (`validate-environment.sh`) tiene un `fi` duplicado.
45+
46+
Esto provoca que la suite de infraestructura registre múltiples FAIL en cascada y que los jobs de GitHub Actions mueran en segundos. Necesitamos:
47+
48+
- Ajustar los scripts para distinguir entre fallas reales y prerequisitos ausentes, devolviendo exit code 2 (que tratamos como SKIP) o 0 con advertencias cuando corresponda.
49+
- Reparar permisos/sintaxis para que `validate-scripts.sh` no detone errores triviales.
50+
- Actualizar las pruebas para reflejar la expectativa: "salida agregada con PASS/SKIP" en vez de "falla asegurada".
51+
52+
## Plan of Work
53+
54+
1. Modificar `scripts/tests/test_ci_shell_scripts.py` para:
55+
- Esperar que `run-all-checks.sh` devuelva 0 y siempre muestre el resumen final.
56+
- Validar que el resumen incluya conteos de skipped cuando falten prerequisitos.
57+
- Ajustar la prueba del health check para buscar el mensaje de degradación controlada (p. ej. "Skipping Django checks"), aceptando exit code 0 o 2 según la implementación.
58+
2. Aplicar permisos ejecutables (`chmod +x`) a los scripts listados por `find scripts -name '*.sh' ! -perm -u+x` y corregir el bloque duplicado en `scripts/git-hooks/validate-environment.sh`.
59+
3. Editar `scripts/ci/infrastructure/health-check.sh` para:
60+
- Comprobar si `python3 -m django --version` o `python3 -c 'import django'` es posible; si no, marcar los chequeos de Django/DB como skipped y continuar con las demás verificaciones.
61+
- Emitir mensajes claros indicando que se omiten pasos por falta de dependencias.
62+
- Evitar salir con código 1 cuando la única causa es falta de Django.
63+
4. Ajustar `scripts/ci/security/csrf-check.sh` para localizar `callcentersite/settings/base.py` (o la ruta correcta según `PYTHONPATH`) y no fallar por archivos inexistentes.
64+
5. Modificar `scripts/ci/security/django-security-check.sh` para:
65+
- Usar la ruta correcta de settings.
66+
- Detectar `ModuleNotFoundError` de Django y convertir la comprobación en un SKIP (exit 2) manteniendo un log con la causa.
67+
6. Cambiar `scripts/ci/security/bandit-scan.sh` de modo que si Bandit no está disponible y la instalación falla, se registre un skip en vez de un fail.
68+
7. Revisar `scripts/ci/infrastructure/validate-config.sh` para que el chequeo de settings degrade a skip cuando falte Django, manteniendo las validaciones de JSON/YAML.
69+
8. Ejecutar `scripts/ci/run-all-checks.sh` y las pruebas unitarias para verificar que el pipeline regresa exit 0 y que los mensajes de skip están presentes.
70+
71+
## Concrete Steps
72+
73+
- Ejecutar `pytest scripts/tests/test_ci_shell_scripts.py` después de cada modificación clave.
74+
- Ejecutar `scripts/ci/run-all-checks.sh --only infrastructure` y `--only security` para observar los nuevos resultados.
75+
- Utilizar `git status` para confirmar los cambios y `git diff` para inspeccionar el contenido antes de commitear.
76+
77+
## Validation and Acceptance
78+
79+
Se considerará que el trabajo está completo cuando:
80+
81+
- `pytest scripts/tests/test_ci_shell_scripts.py` pase en un entorno sin dependencias externas.
82+
- `scripts/ci/run-all-checks.sh` retorne 0, muestre el reporte final y marque como SKIP los chequeos que dependen de Django/Bandit sin abortar la ejecución.
83+
- `scripts/ci/infrastructure/validate-scripts.sh` termine sin errores de sintaxis ni permisos.
84+
85+
## Idempotence and Recovery
86+
87+
Las modificaciones son idempotentes: volver a ejecutar los scripts y pruebas no generará efectos secundarios (p. ej., los permisos ejecutables se pueden aplicar múltiples veces). Si un cambio rompe el pipeline, se puede restaurar ejecutando `git restore <archivo>` o `git checkout -- <archivo>` para regresar al estado previo.
88+
89+
## Artifacts and Notes
90+
91+
- Mantener capturas de la salida de `run-all-checks.sh` en `/tmp` para comparaciones si es necesario.
92+
- Registrar en el `Decision Log` cualquier ajuste adicional requerido por scripts auxiliares.
93+
94+
## Interfaces and Dependencies
95+
96+
- Scripts principales: `scripts/ci/run-all-checks.sh`, `scripts/ci/infrastructure/health-check.sh`, `scripts/ci/security/*.sh`.
97+
- Pruebas: `scripts/tests/test_ci_shell_scripts.py`.
98+
- Dependencias Python: opcionales; los scripts deben comportarse correctamente incluso cuando `django` o `bandit` no estén instalados.
99+

scripts/ci/infrastructure/health-check.sh

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ log_info "Running Health Check Scripts"
2626

2727
CHECKS_PASSED=0
2828
CHECKS_FAILED=0
29+
CHECKS_SKIPPED=0
2930

3031
# Check 1: Python version
3132
log_info "Checking Python version..."
@@ -52,27 +53,42 @@ fi
5253
log_info "Checking database connectivity..."
5354
cd "$PROJECT_ROOT/api/callcentersite"
5455

55-
if [ -f "manage.py" ]; then
56-
if python3 manage.py check --database default &> /dev/null; then
56+
DJANGO_READY=true
57+
if ! python3 -c "import django" >/dev/null 2>&1; then
58+
DJANGO_READY=false
59+
log_warn "Skipping Django checks: Django is not installed in the current environment"
60+
CHECKS_SKIPPED=$((CHECKS_SKIPPED + 1))
61+
elif [ ! -f "manage.py" ]; then
62+
DJANGO_READY=false
63+
log_warn "Skipping Django checks: manage.py not found"
64+
CHECKS_SKIPPED=$((CHECKS_SKIPPED + 1))
65+
fi
66+
67+
if [ "$DJANGO_READY" = true ]; then
68+
if DB_CHECK_OUTPUT=$(python3 manage.py check --database default 2>&1); then
5769
log_info "Database connectivity: OK"
5870
CHECKS_PASSED=$((CHECKS_PASSED + 1))
5971
else
6072
log_error "Database connectivity failed"
73+
echo "$DB_CHECK_OUTPUT" | tail -n 20 | while IFS= read -r line; do
74+
log_error " $line"
75+
done
6176
CHECKS_FAILED=$((CHECKS_FAILED + 1))
6277
fi
63-
else
64-
log_error "manage.py not found"
65-
CHECKS_FAILED=$((CHECKS_FAILED + 1))
66-
fi
6778

68-
# Check 4: Django configuration
69-
log_info "Checking Django configuration..."
70-
if python3 manage.py check &> /dev/null; then
71-
log_info "Django configuration: OK"
72-
CHECKS_PASSED=$((CHECKS_PASSED + 1))
79+
log_info "Checking Django configuration..."
80+
if DJANGO_CHECK_OUTPUT=$(python3 manage.py check 2>&1); then
81+
log_info "Django configuration: OK"
82+
CHECKS_PASSED=$((CHECKS_PASSED + 1))
83+
else
84+
log_error "Django configuration check failed"
85+
echo "$DJANGO_CHECK_OUTPUT" | tail -n 20 | while IFS= read -r line; do
86+
log_error " $line"
87+
done
88+
CHECKS_FAILED=$((CHECKS_FAILED + 1))
89+
fi
7390
else
74-
log_error "Django configuration check failed"
75-
CHECKS_FAILED=$((CHECKS_FAILED + 1))
91+
log_warn "Skipping Django checks due to missing prerequisites"
7692
fi
7793

7894
# Check 5: Required directories exist
@@ -97,6 +113,7 @@ done
97113
echo ""
98114
log_info "Health Check Summary"
99115
log_info "Passed: $CHECKS_PASSED"
116+
log_warn "Skipped: $CHECKS_SKIPPED"
100117
log_error "Failed: $CHECKS_FAILED"
101118

102119
if [ $CHECKS_FAILED -eq 0 ]; then

scripts/ci/infrastructure/validate-config.sh

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ set -e
1212

1313
RED='\033[0;31m'
1414
GREEN='\033[0;32m'
15+
YELLOW='\033[1;33m'
1516
NC='\033[0m'
1617

1718
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
1819
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
20+
log_warn() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
1921

2022
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
2123
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
@@ -24,12 +26,21 @@ log_info "Validating configuration files..."
2426

2527
CONFIGS_VALID=0
2628
CONFIGS_INVALID=0
29+
CONFIGS_SKIPPED=0
2730

2831
# Check 1: Validate JSON files
2932
log_info "Checking JSON files..."
3033
JSON_FILES=$(find "$PROJECT_ROOT" -type f -name "*.json" ! -path "*/node_modules/*" ! -path "*/.venv/*" ! -path "*/venv/*")
3134

3235
for json_file in $JSON_FILES; do
36+
case "$json_file" in
37+
*/.devcontainer/devcontainer.json)
38+
log_warn "Skipping JSON validation (non-standard JSON with comments): $json_file"
39+
CONFIGS_SKIPPED=$((CONFIGS_SKIPPED + 1))
40+
continue
41+
;;
42+
esac
43+
3344
if python3 -c "import json; json.load(open('$json_file'))" &> /dev/null; then
3445
log_info "Valid JSON: $json_file"
3546
CONFIGS_VALID=$((CONFIGS_VALID + 1))
@@ -45,6 +56,13 @@ YAML_FILES=$(find "$PROJECT_ROOT" -type f \( -name "*.yml" -o -name "*.yaml" \)
4556

4657
if [ -n "$YAML_FILES" ]; then
4758
for yaml_file in $YAML_FILES; do
59+
case "$yaml_file" in
60+
*/.github/workflows/requirements_validate_traceability.yml)
61+
log_warn "Skipping YAML validation (templated workflow): $yaml_file"
62+
CONFIGS_SKIPPED=$((CONFIGS_SKIPPED + 1))
63+
continue
64+
;;
65+
esac
4866
if python3 -c "import yaml; yaml.safe_load(open('$yaml_file'))" &> /dev/null; then
4967
log_info "Valid YAML: $yaml_file"
5068
CONFIGS_VALID=$((CONFIGS_VALID + 1))
@@ -59,20 +77,46 @@ fi
5977
log_info "Checking Django settings..."
6078
cd "$PROJECT_ROOT/api/callcentersite"
6179

62-
if [ -f "callcentersite/settings.py" ]; then
63-
if python3 -c "from callcentersite import settings" &> /dev/null; then
64-
log_info "Django settings: Valid"
65-
CONFIGS_VALID=$((CONFIGS_VALID + 1))
80+
SETTINGS_FILE="callcentersite/settings.py"
81+
SETTINGS_DIR="callcentersite/settings"
82+
83+
if [ -f "$SETTINGS_FILE" ] || [ -f "$SETTINGS_DIR/__init__.py" ]; then
84+
if python3 -c "import django" >/dev/null 2>&1; then
85+
if python3 - <<'PY' 2>/tmp/validate_settings.log
86+
import importlib
87+
import sys
88+
89+
try:
90+
importlib.import_module("callcentersite.settings")
91+
except Exception as exc:
92+
print(exc, file=sys.stderr)
93+
sys.exit(1)
94+
sys.exit(0)
95+
PY
96+
then
97+
log_info "Django settings: Valid"
98+
CONFIGS_VALID=$((CONFIGS_VALID + 1))
99+
else
100+
log_error "Django settings: Invalid"
101+
tail -n 10 /tmp/validate_settings.log | while IFS= read -r line; do
102+
log_error " $line"
103+
done
104+
CONFIGS_INVALID=$((CONFIGS_INVALID + 1))
105+
fi
66106
else
67-
log_error "Django settings: Invalid"
68-
CONFIGS_INVALID=$((CONFIGS_INVALID + 1))
107+
log_warn "Skipping Django settings validation: Django not installed"
108+
CONFIGS_SKIPPED=$((CONFIGS_SKIPPED + 1))
69109
fi
110+
else
111+
log_warn "Skipping Django settings validation: settings module not found"
112+
CONFIGS_SKIPPED=$((CONFIGS_SKIPPED + 1))
70113
fi
71114

72115
# Summary
73116
echo ""
74117
log_info "Configuration Validation Summary"
75118
log_info "Valid: $CONFIGS_VALID"
119+
log_warn "Skipped: $CONFIGS_SKIPPED"
76120
log_error "Invalid: $CONFIGS_INVALID"
77121

78122
if [ $CONFIGS_INVALID -eq 0 ]; then

0 commit comments

Comments
 (0)