feat: add bash shell utility functions
12 funciones Bash del dominio shell: utilidades de scripting (bash_log, bash_colors, bash_check_deps, bash_confirm, bash_handle_error, bash_safe_run), manipulacion de texto (convert_text_case), estructura de proyectos (create_project_structure), y operaciones git (git_clean_branches, git_log_visual, git_push_all_remotes, git_repo_status). Cada una con su .sh y .md de frontmatter.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
---
|
||||
name: bash_check_deps
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "check_command(cmd: string, [error_code: string], [description: string]) -> void; check_commands(cmd...: string) -> void; check_directory(dir: string, [msg: string]) -> void; check_file(file: string, [msg: string]) -> void"
|
||||
description: "Verifica existencia de comandos, directorios y archivos con output formateado. Complementa assert_command_exists con mensajes de error detallados y logging."
|
||||
tags: [bash, check, dependency, command, exists, validation]
|
||||
uses_functions: [bash_log_bash_shell]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: cmd
|
||||
desc: "nombre del comando a verificar en PATH"
|
||||
- name: error_code
|
||||
desc: "codigo identificador del error; default COMMAND_NOT_FOUND"
|
||||
- name: description
|
||||
desc: "mensaje de error personalizado; default 'El comando CMD no esta disponible'"
|
||||
- name: dir
|
||||
desc: "ruta del directorio a verificar"
|
||||
- name: file
|
||||
desc: "ruta del archivo a verificar"
|
||||
output: "exit code 0 si todas las verificaciones pasan; exit code 1 en caso de fallo con mensaje de error formateado"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/bash_check_deps.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/lib/common.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/bash_check_deps.sh
|
||||
|
||||
check_command "docker" "DOCKER_NOT_FOUND" "Docker no esta instalado"
|
||||
check_commands "git" "curl" "jq"
|
||||
check_directory "/var/data" "El directorio de datos no existe"
|
||||
check_file "/etc/config.yaml" "Falta el archivo de configuracion"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
`check_command` acepta un error_code y descripcion opcionales para mensajes mas descriptivos que `assert_command_exists`. Usa `debug` internamente para loggear cada verificacion.
|
||||
|
||||
`check_commands` verifica multiples comandos en una sola llamada y reporta todos los faltantes antes de retornar 1.
|
||||
|
||||
Sourcea `bash_log.sh` automaticamente, que a su vez sourcea `bash_colors.sh`. No es necesario sourcea dependencias por separado.
|
||||
@@ -0,0 +1,75 @@
|
||||
# bash_check_deps
|
||||
# ---------------
|
||||
# Verifica existencia de comandos, directorios y archivos.
|
||||
# Output formateado con colores via bash_log.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source bash_check_deps.sh
|
||||
# check_command "docker" "DOCKER_NOT_FOUND" "Docker no esta instalado"
|
||||
# check_commands "git" "curl" "jq"
|
||||
# check_directory "/path/to/dir"
|
||||
# check_file "/path/to/file"
|
||||
|
||||
SCRIPT_DIR_BASH_CHECK="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR_BASH_CHECK/bash_log.sh"
|
||||
bash_log_init
|
||||
|
||||
check_command() {
|
||||
local cmd="$1"
|
||||
local error_code="${2:-COMMAND_NOT_FOUND}"
|
||||
local description="${3:-El comando '$cmd' no esta disponible}"
|
||||
|
||||
debug "Verificando comando: $cmd"
|
||||
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
error "$description ($error_code)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
check_commands() {
|
||||
local failed=0
|
||||
for cmd in "$@"; do
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
error "Comando no encontrado: $cmd"
|
||||
failed=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $failed -eq 1 ]; then
|
||||
error "Faltan multiples dependencias requeridas"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
check_directory() {
|
||||
local dir="$1"
|
||||
local error_msg="${2:-El directorio '$dir' no existe}"
|
||||
|
||||
debug "Verificando directorio: $dir"
|
||||
|
||||
if [ ! -d "$dir" ]; then
|
||||
error "$error_msg"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
check_file() {
|
||||
local file="$1"
|
||||
local error_msg="${2:-El archivo '$file' no existe}"
|
||||
|
||||
debug "Verificando archivo: $file"
|
||||
|
||||
if [ ! -f "$file" ]; then
|
||||
error "$error_msg"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: bash_colors
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "bash_colors() -> void"
|
||||
description: "Exporta variables ANSI de colores, caracteres box drawing y simbolos unicode para uso en scripts de terminal."
|
||||
tags: [bash, colors, ansi, terminal, symbols, box]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
params: []
|
||||
output: "exporta variables de entorno con codigos ANSI: colores (RED, GREEN, BLUE, etc.), box drawing (BOX_TL, BOX_H, etc.) y simbolos (CHECKMARK, CROSS, ARROW, etc.)"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/bash_colors.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/lib/common.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/bash_colors.sh
|
||||
bash_colors
|
||||
|
||||
echo -e "${GREEN}Todo bien${NC}"
|
||||
echo -e "${RED}${CROSS} Error detectado${NC}"
|
||||
echo -e "${BOX_TL}${BOX_H}${BOX_TR}"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion pura: solo define y exporta variables de entorno, sin I/O ni efectos secundarios. Debe llamarse una vez antes de usar cualquier variable de color o simbolo.
|
||||
|
||||
Variables de color disponibles: PURPLE, MAGENTA, GREEN, BLUE, YELLOW, RED, CYAN, ORANGE, GRAY, DIM_GRAY, BOLD, DIM, NC.
|
||||
|
||||
Variables box drawing: BOX_TL, BOX_TR, BOX_BL, BOX_BR, BOX_H, BOX_V, BOX_ML, BOX_MR, BOX_SEP.
|
||||
|
||||
Simbolos unicode: CHECKMARK, CROSS, ARROW, BULLET, WARNING, INFO.
|
||||
@@ -0,0 +1,41 @@
|
||||
# bash_colors
|
||||
# -----------
|
||||
# Variables ANSI de color, caracteres box drawing y simbolos para scripts.
|
||||
# Diseñado para ser sourced por otros scripts.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source bash_colors.sh
|
||||
# bash_colors
|
||||
|
||||
bash_colors() {
|
||||
export PURPLE='\033[35m'
|
||||
export MAGENTA='\033[35m'
|
||||
export GREEN='\033[0;32m'
|
||||
export BLUE='\033[0;34m'
|
||||
export YELLOW='\033[1;33m'
|
||||
export RED='\033[0;31m'
|
||||
export CYAN='\033[0;36m'
|
||||
export ORANGE='\033[0;33m'
|
||||
export GRAY='\033[0;90m'
|
||||
export DIM_GRAY='\033[2;37m'
|
||||
export BOLD='\033[1m'
|
||||
export DIM='\033[2m'
|
||||
export NC='\033[0m'
|
||||
|
||||
export BOX_TL="╔"
|
||||
export BOX_TR="╗"
|
||||
export BOX_BL="╚"
|
||||
export BOX_BR="╝"
|
||||
export BOX_H="═"
|
||||
export BOX_V="║"
|
||||
export BOX_ML="╠"
|
||||
export BOX_MR="╣"
|
||||
export BOX_SEP="─"
|
||||
|
||||
export CHECKMARK="✓"
|
||||
export CROSS="✗"
|
||||
export ARROW="→"
|
||||
export BULLET="•"
|
||||
export WARNING="⚠"
|
||||
export INFO="ℹ"
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
name: bash_confirm
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "bash_confirm(prompt: string, [default: string]) -> exit_code"
|
||||
description: "Dialogo interactivo de confirmacion y/n con valor por defecto configurable. Soporta respuestas yes/y/si."
|
||||
tags: [bash, confirm, prompt, interactive, dialog]
|
||||
uses_functions: [bash_colors_bash_shell]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: prompt
|
||||
desc: "pregunta a mostrar al usuario; default '¿Continuar?'"
|
||||
- name: default
|
||||
desc: "valor por defecto cuando el usuario presiona Enter sin escribir nada; 'y' o 'n'; default 'n'"
|
||||
output: "exit code 0 si el usuario confirma (y/yes/si), exit code 1 si niega o acepta el default negativo"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/bash_confirm.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/lib/common.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/bash_confirm.sh
|
||||
|
||||
# Con default no (muestra [y/N])
|
||||
if bash_confirm "¿Deseas continuar?"; then
|
||||
echo "Continuando..."
|
||||
fi
|
||||
|
||||
# Con default yes (muestra [Y/n])
|
||||
bash_confirm "¿Eliminar archivos temporales?" "y" && rm -rf /tmp/cache
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
El prompt muestra el default en mayuscula: `[Y/n]` cuando default=y, `[y/N]` cuando default=n. Si el usuario presiona Enter sin escribir, se usa el valor por defecto.
|
||||
|
||||
Acepta variantes: y, Y, yes, YES, si, SI como afirmativo. Cualquier otra respuesta se trata como negativo.
|
||||
|
||||
Usa colores de `bash_colors` para el prompt en amarillo. Requiere terminal interactiva (lee de stdin con `read -r`).
|
||||
@@ -0,0 +1,36 @@
|
||||
# bash_confirm
|
||||
# ------------
|
||||
# Dialogo de confirmacion y/n con valor por defecto.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source bash_confirm.sh
|
||||
# bash_confirm "Continuar?" && echo "Si" || echo "No"
|
||||
# bash_confirm "Eliminar?" "y" # default yes
|
||||
|
||||
SCRIPT_DIR_BASH_CONFIRM="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR_BASH_CONFIRM/bash_colors.sh"
|
||||
bash_colors
|
||||
|
||||
bash_confirm() {
|
||||
local prompt="${1:-¿Continuar?}"
|
||||
local default="${2:-n}"
|
||||
|
||||
if [ "$default" = "y" ]; then
|
||||
prompt="$prompt [Y/n]"
|
||||
else
|
||||
prompt="$prompt [y/N]"
|
||||
fi
|
||||
|
||||
echo -ne "${YELLOW}$prompt ${NC}"
|
||||
read -r response
|
||||
|
||||
response=${response:-$default}
|
||||
case "$response" in
|
||||
[yY][eE][sS]|[yY]|[sS][iI])
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: bash_handle_error
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "handle_error(error_code: string, error_description: string, [solution: string]) -> exit_code"
|
||||
description: "Muestra un box de error formateado con contexto del fallo: script, linea, funcion, directorio y usuario. Registra en log."
|
||||
tags: [bash, error, handler, box, formatted, context]
|
||||
uses_functions: [bash_colors_bash_shell, bash_log_bash_shell]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: error_code
|
||||
desc: "codigo identificador del error, ej: BUILD_FAILED, DB_CONNECTION_ERROR"
|
||||
- name: error_description
|
||||
desc: "descripcion legible del error para mostrar al usuario"
|
||||
- name: solution
|
||||
desc: "solucion sugerida; opcional; si se provee se muestra en seccion separada"
|
||||
output: "box de error formateado en stdout con contexto (script, linea, funcion, directorio, usuario) + registro en log; retorna exit code 1"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/bash_handle_error.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/lib/common.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/bash_handle_error.sh
|
||||
|
||||
build_project() {
|
||||
go build ./... || handle_error "BUILD_FAILED" \
|
||||
"La compilacion del proyecto fallo" \
|
||||
"Ejecuta 'go mod tidy' y verifica que todas las dependencias esten instaladas"
|
||||
}
|
||||
|
||||
connect_db() {
|
||||
psql "$DB_URL" -c '\q' 2>/dev/null || handle_error "DB_CONNECTION_ERROR" \
|
||||
"No se pudo conectar a la base de datos"
|
||||
}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
El box usa caracteres unicode de `bash_colors` (BOX_TL, BOX_H, etc.) para el borde en rojo. La informacion de contexto se extrae de `BASH_SOURCE`, `BASH_LINENO` y `FUNCNAME` con offset 2 para apuntar al caller del caller.
|
||||
|
||||
La funcion siempre retorna 1, permitiendo usarla como `cmd || handle_error ...` en pipelines de error.
|
||||
|
||||
Usa `bash_colors` y `bash_log` como dependencias. Sourcea ambas automaticamente al cargarse.
|
||||
@@ -0,0 +1,47 @@
|
||||
# bash_handle_error
|
||||
# -----------------
|
||||
# Muestra un box de error formateado con contexto del fallo.
|
||||
# Incluye script, linea, funcion y directorio.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source bash_handle_error.sh
|
||||
# handle_error "BUILD_FAILED" "La compilacion fallo" "Verifica las dependencias"
|
||||
|
||||
SCRIPT_DIR_BASH_HANDLE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR_BASH_HANDLE/bash_colors.sh"
|
||||
source "$SCRIPT_DIR_BASH_HANDLE/bash_log.sh"
|
||||
bash_colors
|
||||
bash_log_init
|
||||
|
||||
handle_error() {
|
||||
local error_code="$1"
|
||||
local error_description="$2"
|
||||
local solution="$3"
|
||||
|
||||
echo ""
|
||||
echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${RED}║ ${CROSS} ERROR DETECTADO ║${NC}"
|
||||
echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo -e "${RED}${BOLD}Error:${NC} ${error_description}"
|
||||
echo -e "${GRAY}Codigo: ${error_code}${NC}"
|
||||
echo ""
|
||||
|
||||
if [ -n "$solution" ]; then
|
||||
echo -e "${YELLOW}${INFO} Solucion sugerida:${NC}"
|
||||
echo -e " ${solution}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e "${GRAY}${INFO} Informacion adicional:${NC}"
|
||||
echo -e " ${GRAY}${BULLET} Script: ${BASH_SOURCE[2]:-desconocido}${NC}"
|
||||
echo -e " ${GRAY}${BULLET} Linea: ${BASH_LINENO[1]:-desconocido}${NC}"
|
||||
echo -e " ${GRAY}${BULLET} Funcion: ${FUNCNAME[2]:-main}${NC}"
|
||||
echo -e " ${GRAY}${BULLET} Directorio: $(pwd)${NC}"
|
||||
echo -e " ${GRAY}${BULLET} Usuario: $(whoami)${NC}"
|
||||
echo ""
|
||||
|
||||
log "ERROR" "Code: $error_code | Description: $error_description | Script: ${BASH_SOURCE[2]} | Line: ${BASH_LINENO[1]}"
|
||||
|
||||
return 1
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
name: bash_log
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "bash_log_init() -> void; success(msg: string) -> void; info(msg: string) -> void; warning(msg: string) -> void; error(msg: string) -> void; debug(msg: string) -> void; progress(msg: string) -> void"
|
||||
description: "Funciones de logging con colores para scripts bash. Incluye niveles success/info/warning/error/debug/progress con escritura a archivo de log."
|
||||
tags: [bash, log, logging, colors, terminal]
|
||||
uses_functions: [bash_colors_bash_shell]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: msg
|
||||
desc: "mensaje a mostrar y registrar en el archivo de log"
|
||||
output: "mensaje formateado con colores en stdout/stderr y registro con timestamp en archivo de log (ERROR_LOG_FILE)"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/bash_log.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/lib/common.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/bash_log.sh
|
||||
bash_log_init
|
||||
|
||||
success "Servicio iniciado correctamente"
|
||||
info "Procesando 42 registros..."
|
||||
warning "El puerto 8080 ya esta en uso"
|
||||
error "No se pudo conectar a la base de datos"
|
||||
debug "Valor de variable: $VAR"
|
||||
progress "Descargando imagen Docker..."
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Llama a `bash_log_init` una vez al inicio para configurar ERROR_LOG_FILE y DEBUG_MODE. Por defecto el log va a `/tmp/script-errors-YYYYMMDD.log`.
|
||||
|
||||
La variable de entorno `DEBUG_MODE=1` activa los mensajes de debug a stderr y muestra timestamps en todos los logs.
|
||||
|
||||
`error` escribe a stderr; el resto a stdout. Todos los niveles escriben al archivo de log independientemente de DEBUG_MODE.
|
||||
|
||||
Fuente: sourcea `bash_colors.sh` automaticamente al cargarse.
|
||||
@@ -0,0 +1,64 @@
|
||||
# bash_log
|
||||
# --------
|
||||
# Funciones de logging con colores para scripts.
|
||||
# Incluye: log, success, info, warning, error, debug, progress.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source bash_log.sh
|
||||
# bash_log_init
|
||||
# success "Operacion completada"
|
||||
# info "Procesando..."
|
||||
# error "Algo fallo"
|
||||
|
||||
SCRIPT_DIR_BASH_LOG="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR_BASH_LOG/bash_colors.sh"
|
||||
bash_colors
|
||||
|
||||
bash_log_init() {
|
||||
export ERROR_LOG_FILE="${ERROR_LOG_FILE:-/tmp/script-errors-$(date +%Y%m%d).log}"
|
||||
export DEBUG_MODE="${DEBUG_MODE:-0}"
|
||||
}
|
||||
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
local message="$@"
|
||||
local timestamp
|
||||
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo "[$timestamp] [$level] $message" >> "${ERROR_LOG_FILE:-/tmp/script-errors.log}"
|
||||
|
||||
if [ "${DEBUG_MODE:-0}" = "1" ]; then
|
||||
echo -e "${GRAY}[$timestamp] [$level] $message${NC}" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "${GREEN}${CHECKMARK} $*${NC}"
|
||||
log "SUCCESS" "$*"
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "${BLUE}${INFO} $*${NC}"
|
||||
log "INFO" "$*"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "${YELLOW}${WARNING} $*${NC}"
|
||||
log "WARNING" "$*"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}${CROSS} Error: $*${NC}" >&2
|
||||
log "ERROR" "$*"
|
||||
}
|
||||
|
||||
debug() {
|
||||
if [ "${DEBUG_MODE:-0}" = "1" ]; then
|
||||
echo -e "${GRAY}[DEBUG] $*${NC}" >&2
|
||||
fi
|
||||
log "DEBUG" "$*"
|
||||
}
|
||||
|
||||
progress() {
|
||||
echo -e "${CYAN}${ARROW} $*${NC}"
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
name: bash_safe_run
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "safe_run(cmd: string, [error_code: string], [error_desc: string]) -> void; setup_error_trap() -> void; error_trap_handler(exit_code: int, line_number: int) -> void"
|
||||
description: "Ejecuta comandos con manejo de errores integrado. Incluye trap handler que captura fallos con numero de linea y codigo de salida."
|
||||
tags: [bash, safe, run, error, trap, handler]
|
||||
uses_functions: [bash_log_bash_shell]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: cmd
|
||||
desc: "comando a ejecutar via eval"
|
||||
- name: error_code
|
||||
desc: "codigo identificador del error en caso de fallo; default COMMAND_FAILED"
|
||||
- name: error_desc
|
||||
desc: "descripcion del error a mostrar; default 'El comando fallo: CMD'"
|
||||
output: "exit code 0 si el comando tuvo exito; exit code 1 con mensaje de error formateado si fallo"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/bash_safe_run.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/lib/common.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/bash_safe_run.sh
|
||||
bash_log_init
|
||||
setup_error_trap
|
||||
|
||||
safe_run "go build ./..." "BUILD_FAILED" "La compilacion fallo"
|
||||
safe_run "docker compose up -d" "DOCKER_FAILED" "No se pudo iniciar Docker Compose"
|
||||
safe_run "npm install" "NPM_FAILED"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
`safe_run` usa `eval` internamente para ejecutar el comando, lo que permite pasar comandos con pipes y redirecciones como string. Usar con precaucion en entornos con input no confiable.
|
||||
|
||||
`setup_error_trap` instala un trap `ERR` que llama a `error_trap_handler` automaticamente en cualquier comando fallido del script, mostrando numero de linea y codigo de salida.
|
||||
|
||||
`error_trap_handler` no llama a `exit` — el caller decide si continuar o abortar. Muestra la ruta al log para debugging.
|
||||
@@ -0,0 +1,44 @@
|
||||
# bash_safe_run
|
||||
# -------------
|
||||
# Ejecutar comandos con manejo de errores y trap.
|
||||
# Incluye safe_run, setup_error_trap y error_trap_handler.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source bash_safe_run.sh
|
||||
# setup_error_trap
|
||||
# safe_run "go build ./..." "BUILD_FAILED" "La compilacion fallo"
|
||||
|
||||
SCRIPT_DIR_BASH_SAFE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR_BASH_SAFE/bash_log.sh"
|
||||
bash_log_init
|
||||
|
||||
safe_run() {
|
||||
local cmd="$1"
|
||||
local error_code="${2:-COMMAND_FAILED}"
|
||||
local error_desc="${3:-El comando fallo: $cmd}"
|
||||
|
||||
debug "Ejecutando: $cmd"
|
||||
|
||||
if ! eval "$cmd"; then
|
||||
error "$error_desc ($error_code)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
setup_error_trap() {
|
||||
trap 'error_trap_handler $? $LINENO' ERR
|
||||
}
|
||||
|
||||
error_trap_handler() {
|
||||
local exit_code=$1
|
||||
local line_number=$2
|
||||
|
||||
if [ "$exit_code" -ne 0 ]; then
|
||||
echo ""
|
||||
error "El script fallo en la linea $line_number con codigo de salida $exit_code"
|
||||
echo -e "${GRAY}Consulta el log: ${ERROR_LOG_FILE:-/tmp/script-errors.log}${NC}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
name: convert_text_case
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "convert_text_case(mode: string, file: string, [output_file: string]) -> void"
|
||||
description: "Convierte el contenido de un archivo de texto. Modos: upper (todo mayúsculas), lower (todo minúsculas), lf (normaliza saltos de línea a LF eliminando \\r), crlf (normaliza saltos a CRLF). Sin output_file imprime a stdout."
|
||||
tags: [bash, text, convert, case, encoding]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: mode
|
||||
desc: "transformación a aplicar: upper|lower|lf|crlf (requerido)"
|
||||
- name: file
|
||||
desc: "ruta al archivo de entrada (requerido)"
|
||||
- name: output_file
|
||||
desc: "ruta al archivo de salida (opcional; default: stdout)"
|
||||
output: "texto convertido a stdout o escrito en output_file; exit code 1 si el modo o archivo son inválidos"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/convert_text_case.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/linux/conversores/conversor_texto.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/convert_text_case.sh
|
||||
|
||||
# Convertir a mayúsculas y mostrar en stdout
|
||||
convert_text_case upper mi_archivo.txt
|
||||
|
||||
# Convertir a minúsculas y guardar en archivo
|
||||
convert_text_case lower input.txt output_lower.txt
|
||||
|
||||
# Normalizar saltos de línea CRLF a LF
|
||||
convert_text_case lf archivo_windows.txt archivo_unix.txt
|
||||
|
||||
# Añadir CRLF (para Windows)
|
||||
convert_text_case crlf unix.txt windows.txt
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Usa `awk` para las conversiones de case (portable) y `sed` para los saltos de línea. La función no modifica el archivo original si se provee `output_file`. Sin `output_file` imprime directamente a stdout, útil para pipes.
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
# convert_text_case
|
||||
# -----------------
|
||||
# Convierte el contenido de texto de un archivo aplicando transformaciones:
|
||||
# upper (mayúsculas), lower (minúsculas), lf (normalizar saltos a LF),
|
||||
# crlf (normalizar saltos a CRLF).
|
||||
#
|
||||
# USO:
|
||||
# source convert_text_case.sh
|
||||
# convert_text_case mode file [output_file]
|
||||
#
|
||||
# ARGUMENTOS:
|
||||
# mode Transformación a aplicar: upper|lower|lf|crlf (requerido)
|
||||
# file Archivo de entrada (requerido)
|
||||
# output_file Archivo de salida (opcional; por defecto imprime a stdout)
|
||||
|
||||
convert_text_case() {
|
||||
local mode="${1:-}"
|
||||
local input_file="${2:-}"
|
||||
local output_file="${3:-}"
|
||||
|
||||
# Validar argumentos requeridos
|
||||
if [[ -z "$mode" ]]; then
|
||||
echo "convert_text_case: modo requerido (upper|lower|lf|crlf)" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -z "$input_file" ]]; then
|
||||
echo "convert_text_case: archivo de entrada requerido" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$input_file" ]]; then
|
||||
echo "convert_text_case: archivo no encontrado: ${input_file}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Función auxiliar que aplica la conversión y escribe a stdout o archivo
|
||||
_apply_conversion() {
|
||||
local src="$1"
|
||||
local op="$2"
|
||||
case "$op" in
|
||||
upper)
|
||||
awk '{ print toupper($0) }' "$src"
|
||||
;;
|
||||
lower)
|
||||
awk '{ print tolower($0) }' "$src"
|
||||
;;
|
||||
lf)
|
||||
sed 's/\r$//' "$src"
|
||||
;;
|
||||
crlf)
|
||||
sed 's/\r$//' "$src" | sed 's/$/\r/'
|
||||
;;
|
||||
*)
|
||||
echo "convert_text_case: modo no soportado: ${op}. Usa: upper|lower|lf|crlf" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [[ -n "$output_file" ]]; then
|
||||
_apply_conversion "$input_file" "$mode" > "$output_file"
|
||||
echo "convert_text_case: ${input_file} → ${output_file} (modo: ${mode})"
|
||||
else
|
||||
_apply_conversion "$input_file" "$mode"
|
||||
fi
|
||||
}
|
||||
|
||||
# Ejecutar si se invoca directamente
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
convert_text_case "$@"
|
||||
fi
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
name: create_project_structure
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "create_project_structure(project_name: string) -> void"
|
||||
description: "Crea la estructura de directorios estándar de un proyecto funcional en el directorio indicado: database (attachments, data, models), dist (desktop/mobile/docker), docker, docs, frontend, logs, notebooks, robots, scripts, src (application/core/middleware con tests y tipos)."
|
||||
tags: [bash, project, structure, scaffold, init]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: project_name
|
||||
desc: "nombre del proyecto o ruta destino donde crear la estructura (requerido)"
|
||||
output: "crea los directorios a stdout con conteo final; exit code 1 si no se proporciona nombre"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/create_project_structure.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/linux/inicializar_repos/functional_structure.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/create_project_structure.sh
|
||||
|
||||
# Crear estructura en un directorio nuevo
|
||||
create_project_structure mi-proyecto
|
||||
|
||||
# Usar una ruta
|
||||
create_project_structure /home/user/projects/nuevo-proyecto
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Crea 68 directorios organizados en la estructura funcional de DevLauncher. La estructura separa claramente application (orquestación), core (lógica pura) y middleware (efectos/I/O) bajo `src/`. No crea archivos, solo directorios. Idempotente: si los directorios ya existen, no falla.
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env bash
|
||||
# create_project_structure
|
||||
# ------------------------
|
||||
# Crea la estructura de directorios estándar de un proyecto funcional
|
||||
# (database, dist, docker, docs, frontend, logs, notebooks, robots,
|
||||
# scripts, src con application/core/middleware) en el directorio indicado.
|
||||
#
|
||||
# USO:
|
||||
# source create_project_structure.sh
|
||||
# create_project_structure project_name
|
||||
#
|
||||
# ARGUMENTOS:
|
||||
# project_name Nombre del proyecto / ruta destino (requerido)
|
||||
|
||||
create_project_structure() {
|
||||
local project_name="${1:-}"
|
||||
|
||||
if [[ -z "$project_name" ]]; then
|
||||
echo "create_project_structure: se requiere el nombre del proyecto" >&2
|
||||
echo " Uso: create_project_structure <project_name>" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local destino="$project_name"
|
||||
|
||||
local directories=(
|
||||
"database"
|
||||
"database/attachments"
|
||||
"database/attachments/audio"
|
||||
"database/attachments/docs"
|
||||
"database/attachments/images"
|
||||
"database/attachments/video"
|
||||
"database/data"
|
||||
"database/data/01_raw"
|
||||
"database/data/02_clean"
|
||||
"database/data/03_objects"
|
||||
"database/data/04_answers"
|
||||
"database/models"
|
||||
"dist"
|
||||
"dist/desktop"
|
||||
"dist/desktop/linux"
|
||||
"dist/desktop/mac"
|
||||
"dist/desktop/win"
|
||||
"dist/docker"
|
||||
"dist/mobile"
|
||||
"dist/mobile/android"
|
||||
"dist/mobile/ios"
|
||||
"docker"
|
||||
"docs"
|
||||
"docs/documentation"
|
||||
"docs/pdf"
|
||||
"frontend"
|
||||
"logs"
|
||||
"notebooks"
|
||||
"robots"
|
||||
"scripts"
|
||||
"src"
|
||||
"src/application"
|
||||
"src/application/event"
|
||||
"src/application/jobs"
|
||||
"src/application/pipes"
|
||||
"src/application/sandbox"
|
||||
"src/application/simulation"
|
||||
"src/application/tasks"
|
||||
"src/application/tests"
|
||||
"src/application/tests/benchmark"
|
||||
"src/application/tests/fuzzers"
|
||||
"src/application/tests/mocks"
|
||||
"src/application/tests/mutation"
|
||||
"src/core"
|
||||
"src/core/base"
|
||||
"src/core/functions"
|
||||
"src/core/rules"
|
||||
"src/core/tests"
|
||||
"src/core/tests/benchmark"
|
||||
"src/core/tests/fuzzers"
|
||||
"src/core/tests/mocks"
|
||||
"src/core/tests/mutation"
|
||||
"src/core/types"
|
||||
"src/core/utils"
|
||||
"src/middleware"
|
||||
"src/middleware/api"
|
||||
"src/middleware/backend"
|
||||
"src/middleware/browser"
|
||||
"src/middleware/config"
|
||||
"src/middleware/connection"
|
||||
"src/middleware/controller"
|
||||
"src/middleware/i18n"
|
||||
"src/middleware/interfaces"
|
||||
"src/middleware/queue"
|
||||
"src/middleware/servers"
|
||||
"src/middleware/tests"
|
||||
"src/middleware/tests/benchmark"
|
||||
"src/middleware/tests/fuzzers"
|
||||
"src/middleware/tests/mocks"
|
||||
"src/middleware/tests/mutation"
|
||||
"src/middleware/types"
|
||||
"src/modules"
|
||||
)
|
||||
|
||||
echo "Creando estructura del proyecto: ${destino}"
|
||||
|
||||
for dir in "${directories[@]}"; do
|
||||
mkdir -p "${destino%/}/${dir}"
|
||||
done
|
||||
|
||||
echo "Estructura creada en: ${destino%/}"
|
||||
echo " ${#directories[@]} directorios creados."
|
||||
}
|
||||
|
||||
# Ejecutar si se invoca directamente
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
create_project_structure "$@"
|
||||
fi
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: git_clean_branches
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "git_clean_branches([base_branch: string], [--remote], [--force]) -> void"
|
||||
description: "Elimina ramas locales ya mergeadas en la rama base (autodetecta main/master). Con --remote también borra las ramas en todos los remotes. Con --force omite la confirmación interactiva."
|
||||
tags: [bash, git, branches, clean, merge]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: base_branch
|
||||
desc: "rama base para detectar merges (opcional; autodetecta main o master)"
|
||||
- name: --remote
|
||||
desc: "flag para también eliminar ramas en todos los remotes"
|
||||
- name: --force
|
||||
desc: "flag para omitir confirmación interactiva"
|
||||
output: "lista de ramas eliminadas a stdout; exit code 1 si no es repo Git o no se detecta la rama base"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/git_clean_branches.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/linux/git_utils/limpiar_ramas.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/git_clean_branches.sh
|
||||
|
||||
# Limpieza básica con confirmación
|
||||
git_clean_branches
|
||||
|
||||
# Sin confirmación, sobre rama base "develop"
|
||||
git_clean_branches develop --force
|
||||
|
||||
# Eliminar también en remotes
|
||||
git_clean_branches main --remote --force
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Ramas protegidas que nunca se eliminan: `main`, `master`, `develop`, `dev`, `staging`, `release`. Siempre hace prune de referencias remotas obsoletas antes de buscar candidatos.
|
||||
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
# git_clean_branches
|
||||
# ------------------
|
||||
# Elimina ramas locales ya mergeadas en la rama base (main/master por defecto).
|
||||
# Opcionalmente también elimina las ramas en remotes.
|
||||
#
|
||||
# USO:
|
||||
# source git_clean_branches.sh
|
||||
# git_clean_branches [base_branch] [--remote] [--force]
|
||||
#
|
||||
# ARGUMENTOS:
|
||||
# base_branch Rama base (opcional; autodetecta main/master si se omite)
|
||||
# --remote Elimina también las ramas en todos los remotes
|
||||
# --force Omite confirmación interactiva
|
||||
|
||||
git_clean_branches() {
|
||||
local base_branch=""
|
||||
local delete_remote=false
|
||||
local force=false
|
||||
|
||||
# Parsear argumentos
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--remote) delete_remote=true ;;
|
||||
--force) force=true ;;
|
||||
*) [[ -z "$base_branch" ]] && base_branch="$arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validar repo
|
||||
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
|
||||
echo "git_clean_branches: el directorio actual no es un repositorio Git" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Detectar rama base
|
||||
if [[ -z "$base_branch" ]]; then
|
||||
if git show-ref --verify --quiet refs/heads/main; then
|
||||
base_branch="main"
|
||||
elif git show-ref --verify --quiet refs/heads/master; then
|
||||
base_branch="master"
|
||||
else
|
||||
echo "git_clean_branches: no se detectó main/master; pasa la rama base como argumento" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "git_clean_branches: rama base = ${base_branch}"
|
||||
|
||||
# Hacer prune de referencias remotas obsoletas
|
||||
echo "Haciendo prune de referencias remotas..."
|
||||
git remote | while IFS= read -r remote; do
|
||||
git remote prune "$remote" 2>/dev/null && echo " Pruned: ${remote}"
|
||||
done
|
||||
echo ""
|
||||
|
||||
# Buscar ramas locales mergeadas
|
||||
local candidates
|
||||
candidates="$(git branch --merged "$base_branch" \
|
||||
| grep -v -E "^\*|^\s*(main|master|develop|dev|staging|release)$" \
|
||||
| sed 's/^[[:space:]]*//' \
|
||||
| grep -v "^$" || true)"
|
||||
|
||||
if [[ -z "$candidates" ]]; then
|
||||
echo "No hay ramas locales mergeadas para eliminar."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Ramas candidatas a eliminar:"
|
||||
echo "$candidates" | while IFS= read -r b; do
|
||||
echo " - ${b}"
|
||||
done
|
||||
echo ""
|
||||
|
||||
# Confirmación (omitir si --force)
|
||||
if [[ "$force" != true ]]; then
|
||||
echo -n "¿Eliminar estas ramas locales? [y/N] "
|
||||
read -r answer
|
||||
[[ "$answer" != "y" && "$answer" != "Y" ]] && { echo "Operación cancelada."; return 0; }
|
||||
fi
|
||||
|
||||
# Eliminar ramas locales
|
||||
local deleted=0
|
||||
local failed=0
|
||||
while IFS= read -r branch; do
|
||||
if git branch -d "$branch"; then
|
||||
echo " Eliminada local: ${branch}"
|
||||
deleted=$((deleted + 1))
|
||||
else
|
||||
echo " No se pudo eliminar: ${branch}" >&2
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
done <<< "$candidates"
|
||||
|
||||
echo ""
|
||||
echo "Resumen: ${deleted} eliminada(s), ${failed} fallida(s)"
|
||||
|
||||
# Eliminar en remotes si se solicitó
|
||||
if [[ "$delete_remote" == true ]]; then
|
||||
local remotes
|
||||
remotes="$(git remote 2>/dev/null || true)"
|
||||
if [[ -n "$remotes" ]]; then
|
||||
echo ""
|
||||
echo "Eliminando ramas en remotes..."
|
||||
while IFS= read -r branch; do
|
||||
while IFS= read -r remote; do
|
||||
if git ls-remote --heads "$remote" "$branch" 2>/dev/null | grep -q "$branch"; then
|
||||
if git push "$remote" --delete "$branch"; then
|
||||
echo " Eliminada remota: ${remote}/${branch}"
|
||||
else
|
||||
echo " No se pudo eliminar remota: ${remote}/${branch}" >&2
|
||||
fi
|
||||
fi
|
||||
done <<< "$remotes"
|
||||
done <<< "$candidates"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Limpieza completada."
|
||||
}
|
||||
|
||||
# Ejecutar si se invoca directamente
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
git_clean_branches "$@"
|
||||
fi
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
name: git_log_visual
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "git_log_visual([--count N: int], [--author X: string], [--since D: string], [--all]) -> void"
|
||||
description: "Muestra el historial de commits Git con grafo visual, colores y formato legible (hash, fecha, autor, mensaje, ramas). Soporta filtros por cantidad, autor, fecha y modo todas-las-ramas. Pagina con less."
|
||||
tags: [bash, git, log, history, graph]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: --count
|
||||
desc: "número de commits a mostrar (default: 20)"
|
||||
- name: --author
|
||||
desc: "filtrar por nombre o email del autor (parcial, igual que git --author)"
|
||||
- name: --since
|
||||
desc: "mostrar solo commits desde esta fecha (ej: '1 week ago', '2025-01-01')"
|
||||
- name: --all
|
||||
desc: "incluir commits de todas las ramas, no solo la actual"
|
||||
output: "historial de commits paginado con less -R; exit code 1 si no es un repo Git"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/git_log_visual.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/linux/git_utils/historial_commits.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/git_log_visual.sh
|
||||
|
||||
# Últimos 20 commits (default)
|
||||
git_log_visual
|
||||
|
||||
# Últimos 50 commits de todas las ramas
|
||||
git_log_visual --count 50 --all
|
||||
|
||||
# Commits de un autor esta semana
|
||||
git_log_visual --author "lucas" --since "1 week ago"
|
||||
|
||||
# Commits de hoy en todas las ramas
|
||||
git_log_visual --since "00:00:00" --all --count 100
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Usa `less -R --quit-if-one-screen` para paginar: si el log cabe en pantalla, no abre el paginador. El formato incluye hash corto (amarillo), fecha (cyan), autor (verde), mensaje y refs de ramas (rojo).
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env bash
|
||||
# git_log_visual
|
||||
# --------------
|
||||
# Muestra el historial de commits con grafo visual, colores y formato legible.
|
||||
# Soporta filtros por cantidad, autor, fecha y todas las ramas.
|
||||
#
|
||||
# USO:
|
||||
# source git_log_visual.sh
|
||||
# git_log_visual [--count N] [--author X] [--since D] [--all]
|
||||
#
|
||||
# ARGUMENTOS:
|
||||
# --count N Número de commits a mostrar (default: 20)
|
||||
# --author X Filtrar por nombre o email del autor
|
||||
# --since D Mostrar commits desde fecha (ej: "1 week ago", "2025-01-01")
|
||||
# --all Incluir todas las ramas
|
||||
|
||||
git_log_visual() {
|
||||
local count=20
|
||||
local author=""
|
||||
local since=""
|
||||
local all_branches=false
|
||||
|
||||
# Parsear argumentos
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--count|-n)
|
||||
count="$2"
|
||||
shift 2
|
||||
;;
|
||||
--author|-a)
|
||||
author="$2"
|
||||
shift 2
|
||||
;;
|
||||
--since|-s)
|
||||
since="$2"
|
||||
shift 2
|
||||
;;
|
||||
--all)
|
||||
all_branches=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validar repo
|
||||
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
|
||||
echo "git_log_visual: el directorio actual no es un repositorio Git" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local log_format="%C(yellow)%h%C(reset) %C(cyan)%ad%C(reset) %C(green)%an%C(reset) %s%C(red)%d%C(reset)"
|
||||
|
||||
# Construir argumentos del comando
|
||||
local args=()
|
||||
args+=("--graph")
|
||||
args+=("--color=always")
|
||||
args+=("--date=short")
|
||||
args+=("--format=${log_format}")
|
||||
args+=("-n" "${count}")
|
||||
|
||||
[[ -n "$author" ]] && args+=("--author=${author}")
|
||||
[[ -n "$since" ]] && args+=("--since=${since}")
|
||||
[[ "$all_branches" == true ]] && args+=("--all")
|
||||
|
||||
# Ejecutar
|
||||
git log "${args[@]}" 2>/dev/null | less -R --quit-if-one-screen
|
||||
}
|
||||
|
||||
# Ejecutar si se invoca directamente
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
git_log_visual "$@"
|
||||
fi
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
name: git_push_all_remotes
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "git_push_all_remotes([--message msg: string]) -> void"
|
||||
description: "Hace commit de todos los cambios pendientes (git add -A + git commit) y pushea la rama actual a todos los remotes configurados. Si no hay cambios solo pushea. Requiere --message si hay cambios sin commitear."
|
||||
tags: [bash, git, push, remote, all]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: --message
|
||||
desc: "mensaje de commit a usar si hay cambios pendientes (requerido cuando hay cambios)"
|
||||
output: "progreso del push a stdout por cada remote; exit code 1 si algún push falla"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/git_push_all_remotes.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/linux/git_utils/push_todos_remotes.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/shell/git_push_all_remotes.sh
|
||||
|
||||
# Solo push (sin cambios pendientes)
|
||||
git_push_all_remotes
|
||||
|
||||
# Commit + push a todos los remotes
|
||||
git_push_all_remotes --message "feat: nueva funcionalidad"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Usa `--set-upstream` automáticamente si la rama no existe en el remote. Sale con exit code 1 si hay cambios pero no se pasa `--message`, o si algún push falla.
|
||||
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env bash
|
||||
# git_push_all_remotes
|
||||
# --------------------
|
||||
# Hace commit de todos los cambios pendientes (si los hay) y pushea
|
||||
# a todos los remotes configurados en el repositorio.
|
||||
#
|
||||
# USO:
|
||||
# source git_push_all_remotes.sh
|
||||
# git_push_all_remotes [--message "mensaje"]
|
||||
#
|
||||
# ARGUMENTOS:
|
||||
# --message "msg" Mensaje de commit (si hay cambios). Si se omite y hay
|
||||
# cambios, sale con error.
|
||||
|
||||
git_push_all_remotes() {
|
||||
local commit_msg=""
|
||||
|
||||
# Parsear argumentos
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--message|-m)
|
||||
commit_msg="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validar repo
|
||||
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
|
||||
echo "git_push_all_remotes: el directorio actual no es un repositorio Git" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local branch
|
||||
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
|
||||
|
||||
local remotes
|
||||
remotes="$(git remote 2>/dev/null || true)"
|
||||
|
||||
if [[ -z "$remotes" ]]; then
|
||||
echo "git_push_all_remotes: no hay remotes configurados en este repositorio" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Rama actual: ${branch}"
|
||||
echo "Remotes:"
|
||||
echo "$remotes" | while IFS= read -r r; do
|
||||
local url
|
||||
url="$(git remote get-url "$r" 2>/dev/null || echo "?")"
|
||||
echo " ${r} → ${url}"
|
||||
done
|
||||
echo ""
|
||||
|
||||
# Gestión del commit si hay cambios
|
||||
local has_changes=false
|
||||
if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then
|
||||
has_changes=true
|
||||
fi
|
||||
|
||||
if [[ "$has_changes" == true ]]; then
|
||||
if [[ -z "$commit_msg" ]]; then
|
||||
echo "git_push_all_remotes: hay cambios pero no se proporcionó --message" >&2
|
||||
echo " Usa: git_push_all_remotes --message \"tu mensaje\"" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Cambios detectados:"
|
||||
git status --short | while IFS= read -r line; do
|
||||
echo " ${line}"
|
||||
done
|
||||
echo ""
|
||||
|
||||
echo "Añadiendo todos los cambios..."
|
||||
git add -A
|
||||
|
||||
echo "Haciendo commit: \"${commit_msg}\""
|
||||
git commit -m "$commit_msg"
|
||||
echo ""
|
||||
else
|
||||
echo "Directorio de trabajo limpio. Se empujarán commits existentes."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Push a cada remote
|
||||
local ok_count=0
|
||||
local fail_count=0
|
||||
local failed_remotes=()
|
||||
local remote_count
|
||||
remote_count="$(echo "$remotes" | wc -l | tr -d ' ')"
|
||||
|
||||
while IFS= read -r remote; do
|
||||
# Verificar si la rama existe en el remote
|
||||
local push_flags=""
|
||||
if ! git ls-remote --heads "$remote" "$branch" 2>/dev/null | grep -q "$branch"; then
|
||||
push_flags="--set-upstream"
|
||||
echo "La rama '${branch}' no existe en '${remote}', se creará"
|
||||
fi
|
||||
|
||||
echo "Push → ${remote} (${branch})..."
|
||||
# shellcheck disable=SC2086
|
||||
if git push $push_flags "$remote" "$branch" 2>&1; then
|
||||
echo " Push completado → ${remote}/${branch}"
|
||||
ok_count=$((ok_count + 1))
|
||||
else
|
||||
echo " Falló el push a ${remote}/${branch}" >&2
|
||||
fail_count=$((fail_count + 1))
|
||||
failed_remotes+=("$remote")
|
||||
fi
|
||||
echo ""
|
||||
done <<< "$remotes"
|
||||
|
||||
echo "=== Resumen ==="
|
||||
echo "Push completado en ${ok_count}/${remote_count} remote(s)"
|
||||
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo "Fallaron ${fail_count} remote(s): ${failed_remotes[*]}" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Ejecutar si se invoca directamente
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
git_push_all_remotes "$@"
|
||||
fi
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: git_repo_status
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "git_repo_status() -> void"
|
||||
description: "Muestra el estado completo de un repositorio Git: rama actual, upstream (ahead/behind), cambios pendientes, stash, remotes y últimos 8 commits. Sale con exit code 1 si el directorio actual no es un repo Git."
|
||||
tags: [bash, git, status, repo, branch]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: "(ninguno)"
|
||||
desc: "opera sobre el directorio de trabajo actual (cwd)"
|
||||
output: "imprime el estado completo del repo a stdout; exit code 1 si no es un repo Git"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/shell/git_repo_status.sh"
|
||||
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/egutierrez/DevLauncher.git"
|
||||
source_license: "MIT"
|
||||
source_file: "scripts/linux/git_utils/estado_repo.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
# Desde un directorio con repo git
|
||||
cd /home/user/my-project
|
||||
source bash/functions/shell/git_repo_status.sh
|
||||
git_repo_status
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
No requiere dependencias externas más allá de git. Los colores del log de commits usan `--color=always` de git directamente. No produce output en stdout en caso de error — los mensajes de error van a stderr.
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
# git_repo_status
|
||||
# ---------------
|
||||
# Muestra el estado completo de un repositorio Git: rama actual, upstream,
|
||||
# cambios pendientes, stash, remotes y últimos commits.
|
||||
# Sale con exit code 1 si el directorio actual no es un repositorio Git.
|
||||
#
|
||||
# USO:
|
||||
# source git_repo_status.sh
|
||||
# git_repo_status
|
||||
#
|
||||
# O como script directo:
|
||||
# bash git_repo_status.sh
|
||||
|
||||
git_repo_status() {
|
||||
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
|
||||
echo "git_repo_status: el directorio actual no es un repositorio Git" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local branch
|
||||
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
|
||||
|
||||
# Upstream
|
||||
local upstream
|
||||
upstream="$(git rev-parse --abbrev-ref "${branch}@{upstream}" 2>/dev/null || echo "")"
|
||||
local upstream_info
|
||||
if [[ -z "$upstream" ]]; then
|
||||
upstream_info="sin upstream"
|
||||
else
|
||||
local ahead behind
|
||||
ahead="$(git rev-list "${upstream}..HEAD" --count 2>/dev/null || echo 0)"
|
||||
behind="$(git rev-list "HEAD..${upstream}" --count 2>/dev/null || echo 0)"
|
||||
upstream_info="↑${ahead} ↓${behind} (${upstream})"
|
||||
fi
|
||||
|
||||
echo "=== Rama & Upstream ==="
|
||||
echo " Rama actual: ${branch}"
|
||||
echo " Upstream: ${upstream_info}"
|
||||
echo ""
|
||||
|
||||
# Cambios
|
||||
echo "=== Cambios ==="
|
||||
local changes
|
||||
changes="$(git status --short 2>/dev/null)"
|
||||
if [[ -z "$changes" ]]; then
|
||||
echo " Directorio de trabajo limpio"
|
||||
else
|
||||
echo " Cambios pendientes:"
|
||||
echo "$changes" | while IFS= read -r line; do
|
||||
echo " ${line}"
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Stash
|
||||
echo "=== Stash ==="
|
||||
local stash_count
|
||||
stash_count="$(git stash list 2>/dev/null | wc -l | tr -d ' ')"
|
||||
if [[ "$stash_count" -eq 0 ]]; then
|
||||
echo " Sin entradas en stash"
|
||||
else
|
||||
echo " ${stash_count} entrada(s) en stash:"
|
||||
git stash list | head -5 | while IFS= read -r line; do
|
||||
echo " ${line}"
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Remotes
|
||||
echo "=== Remotes ==="
|
||||
local remotes
|
||||
remotes="$(git remote -v 2>/dev/null | grep '(fetch)' || true)"
|
||||
if [[ -z "$remotes" ]]; then
|
||||
echo " Sin remotes configurados"
|
||||
else
|
||||
echo "$remotes" | while IFS= read -r line; do
|
||||
local name url
|
||||
name="$(echo "$line" | awk '{print $1}')"
|
||||
url="$(echo "$line" | awk '{print $2}')"
|
||||
echo " ${name} → ${url}"
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Últimos commits
|
||||
echo "=== Últimos commits ==="
|
||||
git log --oneline --decorate --color=always -8 2>/dev/null || true
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Ejecutar si se invoca directamente
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
git_repo_status "$@"
|
||||
fi
|
||||
Reference in New Issue
Block a user