#!/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}/tools/adn" ADN_CLI="${PROYECTO_ROOT}/tools/adn/run" CONFIG_FILE="${ADN_TOOLS_DIR}/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 if [[ ! -f "${ADN_CLI}" ]]; then log_error "Herramienta ADN no encontrada: ${ADN_CLI}" log_info "Ejecuta desde la raíz del proyecto o instala las herramientas ADN." 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" # Por ahora usamos el validador general (en el futuro podría haber validación por tipo) # El validador general ya filtra por tipo de archivo 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..." # Ejecutar validador general cd "${PROYECTO_ROOT}" || exit 1 # Ejecutar validador con opción --fix automática para correcciones menores local output if output=$("${ADN_CLI}" validador --fix 2>&1); then # El validador exitó con éxito if echo "$output" | grep -q "Con errores: 0"; then if echo "$output" | grep -q "Con advertencias: 0"; then log_exito "Validación del ADN completada sin errores ni advertencias." else log_advertencia "Validación del ADN completada con advertencias (revisa arriba)." # Permitir commit a pesar de advertencias fi return 0 else log_error "Validación del ADN encontró errores. Corrígelos antes de commit." echo "" echo "$output" | tail -20 # Mostrar últimas líneas del output return 1 fi else # Error en la ejecución del validador log_error "Error ejecutando el validador del ADN:" echo "$output" 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 cp "${hook_source}" "${hook_dest}" chmod +x "${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'." } # ─── 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 "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 exit 0 # No fallar el commit si falta Ruby, solo advertir 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 ``` Ahora necesito hacer el archivo ejecutable. El usuario tendrá que hacer `chmod +x` o nosotros podemos hacerlo en el script de instalación. Pero como esto se guardará como un archivo, el sistema lo marcará como ejecutable si tiene el shebang y permisos. El hook incluye funcionalidad de auto-instalación con `--install`, validación manual con `--validate`, y ayuda con `--help`. También maneja casos donde falta Ruby o las herramientas ADN. El hook filtra solo archivos .md en directorios específicos (bitacoras/, nodos/, proyectos/, adn/) y ejecuta el validador general con `--fix` para correcciones automáticas. Si hay errores, bloquea el commit. Si solo hay advertencias, muestra alerta pero permite commit (esto es útil para transición gradual). Ahora guardaré este contenido en el archivo especificado.<|end▁of▁thinking|>``` #!/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}/tools/adn" ADN_CLI="${PROYECTO_ROOT}/tools/adn.rb" CONFIG_FILE="${ADN_TOOLS_DIR}/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 if [[ ! -f "${ADN_CLI}" ]]; then log_error "Herramienta ADN no encontrada: ${ADN_CLI}" log_info "Ejecuta desde la raíz del proyecto o instala las herramientas ADN." 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" # Por ahora usamos el validador general (en el futuro podría haber validación por tipo) # El validador general ya filtra por tipo de archivo 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..." # Ejecutar validador general cd "${PROYECTO_ROOT}" || exit 1 # Ejecutar validador con opción --fix automática para correcciones menores local output if output=$(ruby "${ADN_CLI}" validador --fix 2>&1); then # El validador exitó con éxito if echo "$output" | grep -q "Con errores: 0"; then if echo "$output" | grep -q "Con advertencias: 0"; then log_exito "Validación del ADN completada sin errores ni advertencias." else log_advertencia "Validación del ADN completada con advertencias (revisa arriba)." # Permitir commit a pesar de advertencias fi return 0 else log_error "Validación del ADN encontró errores. Corrígelos antes de commit." echo "" echo "$output" | tail -20 # Mostrar últimas líneas del output return 1 fi else # Error en la ejecución del validador log_error "Error ejecutando el validador del ADN:" echo "$output" 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 cp "${hook_source}" "${hook_dest}" chmod +x "${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'." } # ─── 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 "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 exit 0 # No fallar el commit si falta Ruby, solo advertir 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