#!/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