297 lines
11 KiB
Bash
Executable File
297 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
|
||
# Git pre-commit hook para validación del ADN del proyecto srv-ns8
|
||
# Valida que bitácoras, nodos, proyectos y hebras ADN cumplan con las reglas
|
||
# definidas en las hebras del ADN (adn/).
|
||
|
||
# ─── Configuración ───────────────────────────────────────────────────────
|
||
PROYECTO_ROOT="$(git rev-parse --show-toplevel)"
|
||
ADN_TOOLS_DIR="${PROYECTO_ROOT}/adn/tools"
|
||
ADN_CLI="${ADN_TOOLS_DIR}/run"
|
||
CONFIG_FILE="${ADN_TOOLS_DIR}/config/config.yml"
|
||
HOOK_NAME="pre-commit ADN"
|
||
|
||
# ─── Colores para terminal ───────────────────────────────────────────────
|
||
COLOR_RESET="\033[0m"
|
||
COLOR_ROJO="\033[31m"
|
||
COLOR_VERDE="\033[32m"
|
||
COLOR_AMARILLO="\033[33m"
|
||
COLOR_CYAN="\033[36m"
|
||
COLOR_DIM="\033[2m"
|
||
|
||
# ─── Funciones de utilidad ───────────────────────────────────────────────
|
||
log_info() {
|
||
echo -e "${COLOR_CYAN}ℹ $1${COLOR_RESET}"
|
||
}
|
||
|
||
log_exito() {
|
||
echo -e "${COLOR_VERDE}✓ $1${COLOR_RESET}"
|
||
}
|
||
|
||
log_advertencia() {
|
||
echo -e "${COLOR_AMARILLO}⚠ $1${COLOR_RESET}"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${COLOR_ROJO}✗ $1${COLOR_RESET}"
|
||
}
|
||
|
||
# Verificar si un comando existe
|
||
comando_existe() {
|
||
command -v "$1" >/dev/null 2>&1
|
||
}
|
||
|
||
# ─── Validaciones previas ────────────────────────────────────────────────
|
||
verificar_prerequisitos() {
|
||
# Verificar Ruby
|
||
if ! comando_existe "ruby"; then
|
||
log_error "Ruby no encontrado. Es requerido para la validación del ADN."
|
||
log_info "Instala Ruby: sudo apt install ruby (Debian/Ubuntu)"
|
||
return 1
|
||
fi
|
||
|
||
# Verificar CLI ADN (con múltiples rutas posibles)
|
||
local cli_encontrada=0
|
||
local posibles_rutas=(
|
||
"${ADN_CLI}"
|
||
"${PROYECTO_ROOT}/adn/tools/run"
|
||
"./adn/tools/run"
|
||
"adn/tools/run"
|
||
)
|
||
|
||
for ruta in "${posibles_rutas[@]}"; do
|
||
if [[ -f "$ruta" ]]; then
|
||
ADN_CLI="$ruta"
|
||
cli_encontrada=1
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [[ $cli_encontrada -eq 0 ]]; then
|
||
log_error "Herramienta ADN no encontrada en ninguna ruta esperada."
|
||
log_info "Rutas probadas:"
|
||
for ruta in "${posibles_rutas[@]}"; do
|
||
log_info " - $ruta"
|
||
done
|
||
log_info "Ejecuta desde la raíz del proyecto o instala las herramientas ADN."
|
||
return 1
|
||
fi
|
||
|
||
# Verificar que la CLI sea ejecutable
|
||
if [[ ! -x "${ADN_CLI}" ]]; then
|
||
log_error "Herramienta ADN no es ejecutable: ${ADN_CLI}"
|
||
log_info "Ejecuta: chmod +x ${ADN_CLI}"
|
||
return 1
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# ─── Obtener archivos modificados relevantes ─────────────────────────────
|
||
obtener_archivos_relevantes() {
|
||
# Obtener archivos staged con cambios (Added, Copied, Modified)
|
||
git diff --cached --name-only --diff-filter=ACM | while read -r archivo; do
|
||
# Filtrar solo archivos .md en directorios del ADN
|
||
if [[ "$archivo" =~ \.md$ ]]; then
|
||
case "$archivo" in
|
||
bitacoras/*|nodos/*|proyectos/*|adn/*)
|
||
echo "$archivo"
|
||
;;
|
||
esac
|
||
fi
|
||
done
|
||
}
|
||
|
||
# ─── Validación de archivos individuales ─────────────────────────────────
|
||
validar_archivo() {
|
||
local archivo="$1"
|
||
local nombre_archivo=$(basename "$archivo")
|
||
|
||
# Determinar tipo de validación basado en el directorio
|
||
local tipo=""
|
||
case "$archivo" in
|
||
bitacoras/*) tipo="bitacora" ;;
|
||
nodos/*) tipo="nodo" ;;
|
||
proyectos/*) tipo="proyecto" ;;
|
||
adn/*) tipo="hebra" ;;
|
||
*) return 0 ;; # No debería pasar
|
||
esac
|
||
|
||
log_info "Validando $tipo: $nombre_archivo"
|
||
|
||
# Según el principio "Menos es Más", usamos el validador general unificado
|
||
# El validador ya filtra por tipo de archivo internamente
|
||
return 0
|
||
}
|
||
|
||
# ─── Validación completa ─────────────────────────────────────────────────
|
||
ejecutar_validacion() {
|
||
local archivos_relevantes=$(obtener_archivos_relevantes)
|
||
local cantidad_archivos=$(echo "$archivos_relevantes" | wc -l | tr -d ' ')
|
||
|
||
if [[ "$cantidad_archivos" -eq 0 ]]; then
|
||
log_info "No hay archivos del ADN para validar."
|
||
return 0
|
||
fi
|
||
|
||
log_info "Validando $cantidad_archivos archivo(s) del ADN..."
|
||
log_info "Principio aplicado: Armonía Integral - validando coherencia entre componentes"
|
||
|
||
# Ejecutar validador general
|
||
cd "${PROYECTO_ROOT}" || exit 1
|
||
|
||
# Ejecutar validador con opción --fix automática para correcciones menores
|
||
# Según "Mejora Continua", el validador ayuda a evolucionar el ADN
|
||
local output
|
||
local exit_code=0
|
||
|
||
log_info "Ejecutando: ruby ${ADN_CLI} validador --fix"
|
||
|
||
# Cambiar al directorio del proyecto para rutas relativas correctas
|
||
cd "${PROYECTO_ROOT}" || {
|
||
log_error "No se pudo cambiar al directorio del proyecto: ${PROYECTO_ROOT}"
|
||
return 1
|
||
}
|
||
|
||
output=$(ruby "${ADN_CLI}" validador --fix 2>&1)
|
||
exit_code=$?
|
||
|
||
if [[ $exit_code -eq 0 ]]; then
|
||
# El validador exitó con éxito
|
||
# Remover códigos de color ANSI para búsqueda más robusta
|
||
local output_sin_color=$(echo "$output" | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g")
|
||
|
||
if echo "$output_sin_color" | grep -q "Con errores: 0"; then
|
||
if echo "$output_sin_color" | grep -q "Con advertencias: 0"; then
|
||
log_exito "✅ Validación del ADN completada sin errores ni advertencias."
|
||
log_exito "Principio aplicado: Armonía Integral - todos los componentes son coherentes"
|
||
else
|
||
log_advertencia "⚠ Validación del ADN completada con advertencias (revisa arriba)."
|
||
log_info "Principio aplicado: Mejora Continua - oportunidades de mejora identificadas"
|
||
# Permitir commit a pesar de advertencias (son oportunidades de mejora)
|
||
fi
|
||
return 0
|
||
else
|
||
log_error "✗ Validación del ADN encontró errores. Corrígelos antes de commit."
|
||
log_info "Principio aplicado: Armonía Integral - se requiere coherencia antes de avanzar"
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo "$output" | tail -30 # Mostrar más contexto del output
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
return 1
|
||
fi
|
||
else
|
||
# Error en la ejecución del validador
|
||
log_error "✗ Error ejecutando el validador del ADN (código: $exit_code):"
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo "$output"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
log_info "Sugerencia: Ejecuta manualmente: ./adn/tools/run validador --fix"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# ─── Instalación automática (si se ejecuta con --install) ────────────────
|
||
instalar_hook() {
|
||
local hook_dest="${PROYECTO_ROOT}/.git/hooks/pre-commit"
|
||
local hook_source="${ADN_TOOLS_DIR}/hooks/pre-commit"
|
||
|
||
if [[ ! -d "${PROYECTO_ROOT}/.git" ]]; then
|
||
log_error "No se encuentra directorio .git. Ejecuta desde un repositorio Git."
|
||
return 1
|
||
fi
|
||
|
||
log_info "Instalando hook pre-commit para validación ADN..."
|
||
|
||
# Copiar hook (seguir principio "Menos es Más" - reutilizar componente existente)
|
||
cp "${hook_source}" "${hook_dest}"
|
||
chmod +x "${hook_dest}"
|
||
|
||
log_info "Hook instalado como componente reutilizable en: ${hook_dest}"
|
||
|
||
# Verificar si ya existe un hook pre-commit previo
|
||
if [[ -f "${PROYECTO_ROOT}/.git/hooks/pre-commit" && ! -L "${PROYECTO_ROOT}/.git/hooks/pre-commit" ]] && \
|
||
! cmp -s "${hook_source}" "${hook_dest}"; then
|
||
log_advertencia "Ya existe un hook pre-commit. Se ha sobrescrito."
|
||
log_info "Si necesitas múltiples hooks, considera usar un gestor de hooks."
|
||
fi
|
||
|
||
log_exito "✅ Hook pre-commit instalado en ${hook_dest}"
|
||
log_info "Se ejecutará automáticamente en cada 'git commit'."
|
||
log_info "Principios aplicados:"
|
||
log_info " • Menos es Más - componente atómico y reutilizable"
|
||
log_info " • Armonía Integral - valida coherencia antes de cada commit"
|
||
log_info " • Mejora Continua - identifica oportunidades de mejora"
|
||
}
|
||
|
||
# ─── Ayuda ───────────────────────────────────────────────────────────────
|
||
mostrar_ayuda() {
|
||
echo -e "${COLOR_CYAN}${HOOK_NAME} - Hook Git pre-commit${COLOR_RESET}"
|
||
echo ""
|
||
echo "Uso:"
|
||
echo " Ejecución automática: Se ejecuta en cada 'git commit'"
|
||
echo " Instalación: $0 --install"
|
||
echo " Validación manual: $0 --validate"
|
||
echo " Ayuda: $0 --help"
|
||
echo ""
|
||
echo "Descripción:"
|
||
echo " Valida que los archivos del ADN (bitácoras, nodos, proyectos, hebras)"
|
||
echo " cumplan con las reglas definidas en las hebras del ADN (adn/)."
|
||
echo ""
|
||
echo "Comportamiento:"
|
||
echo " - Errores: Bloquea el commit hasta que se corrijan"
|
||
echo " - Advertencias: Muestra alerta pero permite el commit"
|
||
echo " - Correcciones automáticas: Intenta corregir errores simples (--fix)"
|
||
echo ""
|
||
echo "Principios ADN aplicados:"
|
||
echo " • Menos es Más: Validador unificado, sin duplicación"
|
||
echo " • Armonía Integral: Valida coherencia entre componentes"
|
||
echo " • Mejora Continua: Identifica oportunidades de mejora"
|
||
echo ""
|
||
echo "Configuración: ${CONFIG_FILE}"
|
||
}
|
||
|
||
# ─── Main ────────────────────────────────────────────────────────────────
|
||
main() {
|
||
# Parsear argumentos
|
||
case "${1:-}" in
|
||
--install|-i)
|
||
instalar_hook
|
||
exit $?
|
||
;;
|
||
--validate|-v)
|
||
if ! verificar_prerequisitos; then
|
||
exit 1
|
||
fi
|
||
ejecutar_validacion
|
||
exit $?
|
||
;;
|
||
--help|-h)
|
||
mostrar_ayuda
|
||
exit 0
|
||
;;
|
||
"")
|
||
# Modo hook normal (sin argumentos)
|
||
if ! verificar_prerequisitos; then
|
||
# Según "Mejora Continua", no bloquear pero advertir para permitir evolución
|
||
log_advertencia "Prerequisitos incompletos, pero permitiendo commit para no bloquear trabajo."
|
||
log_info "Sugerencia: Ejecuta './adn/tools/hooks/pre-commit --install' para configurar correctamente."
|
||
exit 0
|
||
fi
|
||
ejecutar_validacion
|
||
exit $?
|
||
;;
|
||
*)
|
||
log_error "Argumento desconocido: $1"
|
||
mostrar_ayuda
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# Solo ejecutar main si el script es ejecutado directamente
|
||
if [[ "${BASH_SOURCE[0]}" = "$0" ]]; then
|
||
main "$@"
|
||
fi
|