# ¡Desplega tu aplicación con estilo usando Bash Scripting!

Hoy te traigo una guía sobre un poderoso **script en Bash** que te permitirá **automatizar la construcción y despliegue** de una aplicación **Node.js**, ya sea ejecutándola localmente o dentro de un contenedor Docker. Además, te enseñaré algunos **tips and tricks** de buenas prácticas para mejorar tu desarrollo en Bash.

Este script forma parte del proyecto alojado en mi [repositorio de GitH](https://github.com/roxsross/roxs-projects-blog/tree/devops-cars-management-system)[ub. ¡Vamos a explorar](https://github.com/roxsross/roxs-projects-blog/tree/devops-cars-management-system)lo y aprender algunos trucos para hacer que tus scripts Bash sean más robustos y eficientes!

### **¿Qué hace este script?**

Este script es un ejemplo perfecto de cómo escribir código en Bash de manera organizada y robusta. Incluye funciones útiles para:

1. **Verificar herramientas instaladas**: [Asegura que todas las](https://github.com/roxsross/roxs-projects-blog.git) dependencias necesarias (como `node`, `npm`, `jq`, `git`, e incluso `docker` en modo contenedor) estén presentes.
    
2. **Obtener información del repositorio Git**: Como el nombre del repositorio, la rama activa, y el nombre de la imagen Docker.
    
3. **Leer la versión de la aplicación desde** `package.json`: Esto ayuda a versionar las imágenes Docker correctamente.
    
4. **Construir y ejecutar un contenedor Docker**: Crea un `Dockerfile` de forma dinámica y luego ejecuta el contenedor de la aplicación.
    
5. **Ejecutar localmente**: Si prefieres probar tu aplicación en tu máquina sin usar Docker, ¡también lo cubre!
    
6. **Detener la aplicación**: Tanto en local como en Docker.
    

### Código:

```bash
#!/bin/bash

# Habilitar modo estricto y debugging
set -euo pipefail
#set -x
IFS=$'\n\t'

TEMP_DIR="tempdir"
PORT=3000
DOCKERFILE_PATH="Dockerfile"
PID_FILE="/tmp/node-server-app.pid"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
NC='\033[0m'

# Banner llamativo
print_banner() {
    echo -e "${BLUE}"
    echo "##############################################"
    echo "      🚀 Bienvenido al RoxsApp Deployer! 🚀   "
    echo "        Automatizando todo con estilo...      "
    echo "              MODO:$EXECUTION_MODE            "
    echo "###############################################"
    echo -e "${NC}"
}

# Función para imprimir mensajes coloreados
print_message() {
    local color=$1
    local message=$2
    echo -e "${color}${message}${NC}"
}

# .......
```

Puedes encontrar el código completo en mi [repositorio en Gi](https://github.com/roxsross/roxs-projects-blog/blob/devops-cars-management-system/automation.sh)[tHub.](https://github.com/roxsross/roxs-projects-blog/blob/devops-cars-management-system/automation.sh)

### ¿Cómo Funciona el Script?

Este script en Bash está diseñado para automatizar el proceso de despliegue de una aplicación Node.js, permitiendo ejecutarla de manera local o dentro de un contenedor Docker. A continuación, te explico cada paso clave del funcionamiento:

1. **Configuración Inicial**:
    
    * El script activa el modo estricto (`set -euo pipefail`) para asegurar una mejor gestión de errores.
        
    * Se definen variables de entorno importantes, como el directorio temporal (`TEMP_DIR`), el puerto de la aplicación (`PORT`), el archivo `Dockerfile` y otros parámetros necesarios para el despliegue.
        
2. **Modos de Ejecución**:
    
    * **LOCAL**: Ejecuta la aplicación Node.js en tu máquina local.
        
    * **DOCKER**: Construye y ejecuta la aplicación dentro de un contenedor Docker.
        
    * **STOP**: Detiene la aplicación, ya sea que esté corriendo localmente o en Docker.
        
3. **Funciones Clave**:
    
    * `check_all_tools`: Verifica que todas las herramientas necesarias (Node.js, npm, jq, git) estén instaladas antes de proceder.
        
    * `get_git_info`: Obtiene información del repositorio y rama actual, generando nombres para la imagen Docker y el contenedor basados en estos datos.
        
    * `read_app_version`: Lee la versión de la aplicación desde el archivo `package.json` para etiquetar la imagen Docker con la versión correcta.
        
    * `create_dockerfile`: Genera dinámicamente un archivo `Dockerfile` que especifica cómo construir la imagen de la aplicación Node.js.
        
    * `docker_build_and_run`: Construye la imagen Docker y la despliega en un contenedor.
        
    * `run_locally`: Ejecuta la aplicación localmente en el puerto especificado y administra los procesos activos.
        
4. **Flujo de Trabajo**:
    
    * Dependiendo del modo seleccionado (LOCAL o DOCKER), el script realiza los siguientes pasos:
        
        * Verifica las herramientas necesarias.
            
        * Obtiene la información del repositorio y la versión de la aplicación.
            
        * En modo **DOCKER**, genera un `Dockerfile`, construye la imagen y despliega un contenedor.
            
        * En modo **LOCAL**, instala las dependencias de npm y ejecuta la aplicación directamente en el puerto especificado.
            
    * Si la aplicación ya está corriendo en el puerto designado, el script puede detener el proceso anterior antes de iniciar uno nuevo.
        
5. **Limpieza y Finalización**:
    
    * Al finalizar, se realiza una limpieza de recursos temporales para garantizar que no queden archivos ni contenedores innecesarios ocupando espacio.
        
    

---

### ¿Cómo Ejecutar el Script?

Sigue estos pasos para ejecutar el script y automatizar el despliegue de tu aplicación Node.js, ya sea localmente o dentro de un contenedor Docker.

#### 1\. **Clona el Repositorio**

Primero, clona el repositorio que contiene el script y la configuración necesaria:

`branch devops-cars-management-system`

```bash
git clone -b devops-cars-management-system https://github.com/roxsross/roxs-projects-blog.git
cd roxs-projects-blog
```

#### 2\. **Haz el Script Ejecutable**

Si aún no lo has hecho, asegúrate de que el archivo del script tenga permisos de ejecución:

```bash
chmod +x deploy.sh
```

#### 3\. **Modos de Ejecución del Script**

El script ofrece tres modos de ejecución: `LOCAL`, `DOCKER`, y `STOP`. A continuación te explico cómo usar cada uno:

* **Ejecución Local**  
    Para ejecutar la aplicación Node.js localmente, ejecuta el siguiente comando:
    
    ```bash
    ./deploy.sh LOCAL
    ```
    
    Esto iniciará la aplicación en tu máquina usando el puerto configurado (por defecto el puerto `3000`). Si deseas cambiar el puerto, puedes hacerlo editando la variable `PORT` en el script.
    
* <div data-node-type="callout">
    <div data-node-type="callout-emoji">💡</div>
    <div data-node-type="callout-text">Version de Node Recomendada superior a v18</div>
    </div>
    
* <div data-node-type="callout">
    <div data-node-type="callout-emoji">ℹ</div>
    <div data-node-type="callout-text">Te recomiendo usar el Gestor NVM para trabajar con multiples versiones de node su instalación es muy fácil <a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/nvm-sh/nvm" style="pointer-events: none">https://github.com/nvm-sh/nvm</a></div>
    </div>
    
* <div data-node-type="callout">
    <div data-node-type="callout-emoji">💡</div>
    <div data-node-type="callout-text">Importante si ejecutas el <strong>script LOCAL</strong> ten en cuenta que si lo corres en Ubuntu debes tener instalado estos paquetes</div>
    </div>
    

* ```bash
    sudo apt update
    sudo apt install build-essential python3 make
    ```
    
* **Ejecución en Docker**  
    Si prefieres desplegar la aplicación dentro de un contenedor Docker, usa el siguiente comando:
    
    ```bash
    ./deploy.sh DOCKER
    ```
    
    El script construirá la imagen Docker utilizando el `Dockerfile` generado automáticamente y ejecutará la aplicación en un contenedor.
    
* **Detener la Aplicación**  
    Para detener la aplicación, ya sea que esté corriendo localmente o en Docker, utiliza:
    
    ```bash
    ./deploy.sh STOP
    ```
    
    Esto se encargará de matar el proceso local o detener el contenedor Docker que esté en ejecución.
    

#### 4\. **Verificar el Despliegue**

Dependiendo del modo de ejecución que elijas, puedes acceder a la aplicación desde el navegador usando:

* **Local**:  
    Si ejecutaste la aplicación localmente, ve a [`http://localhost:3000`](http://localhost:3000) (o el puerto que hayas configurado).
    
* **Docker**:  
    Si utilizaste Docker, abre el navegador y accede a [`http://localhost:<puerto>`](http://localhost:<puerto>) para verificar que el contenedor se está ejecutando correctamente.
    

---

### Script en Acción

%[https://www.youtube.com/watch?v=p8gXmubR5oA] 

---

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Si quieren que este video lo explique paso a paso puedes contactarme en alguna de mis redes sociales roxs.295devops.com</div>
</div>

---

### [**Buenas práctic**](https://github.com/roxsross/roxs-projects-blog.git)**as y tips**

---

#### **1\. Usa** `set -euo pipefail` y `IFS=$'\n\t'` para evitar errores ocultos

* `set -euo pipefail`: Habilitar este modo es clave para evitar que el script continúe si ocurre un error. De esta forma, el script se detiene si un comando falla (`-e`), si intentas usar una variable no declarada (`-u`), o si algún comando dentro de una *pipeline* falla (`pipefail`).
    
* `IFS=$'\n\t'`: Esto ajusta el **Internal Field Separator** para que los espacios en blanco no separen elementos inesperadamente. Es especialmente útil cuando trabajas con archivos o variables que contienen saltos de línea.
    

**Tip**: No olvides incluir este ajuste en tus scripts para prevenir errores difíciles de detectar.

---

#### **2\. Colorea tus salidas para mejorar la legibilidad**

* Este s[cript utiliza variabl](https://github.com/roxsross/roxs-projects-blog.git)es de color como `RED`, `GREEN`, `YELLOW`, `BLUE`, `NC` para imprimir mensajes coloridos. Esto hace que sea más fácil identificar errores, advertencias o mensajes de éxito.
    

```bash
print_message() {
    local color=$1
    local message=$2
    echo -e "${color}${message}${NC}"
}
```

**Tip**: Añadir colores es una excelente manera de hacer que tu script sea más intuitivo. Úsalo especialmente cuando haya errores, advertencias o información clave.

---

#### **3\. Verifica dependencias antes de continuar**

* `check_tool` es una función que comprueba si las herramientas necesarias están instaladas antes de proceder. Si alguna no está, el script muestra un mensaje de error y sale.
    

```bash
check_tool() {
    if ! command -v "$1" &>/dev/null; then 
        print_message "$RED" "Error: '$1' no está instalado."
        exit 1
    fi
}
```

**Tip**: Siempre asegúrate de que todas las dependencias estén disponibles antes de ejecutar el código principal de tu script. Esto evitará problemas inesperados durante la ejecución.

---

#### **4\. Divide tu script en funciones reutilizables**

* Agrupar tareas en **funciones** como `create_dockerfile`, `docker_build_and_run`, `run_locally` y `stop_node_server` ayuda a mantener el código organizado y facilita el mantenimiento.
    

```bash
create_dockerfile() {
    cat <<EOF > "$TEMP_DIR/$DOCKERFILE_PATH"
    FROM node:18-alpine
    LABEL org.opencontainers.image.authors="RoxsRoss"
    RUN apk add --no-cache python3 make g++
    WORKDIR /app
    COPY package*.json ./
    RUN npm install
    COPY . .
    EXPOSE $PORT
    CMD ["npm", "start"]
EOF
}
```

**Tip**: Cada función debe cumplir un propósito claro y específico. Esto no solo mejora la legibilidad del código, sino que también facilita la depuración.

---

#### **5\. Control de errores y salida temprana**

* A lo largo del script, hay comprobaciones continuas de errores con el uso de `if` y el comando `exit 1` si algo falla. Por ejemplo, si el archivo `package.json` no existe o no puede leerse:
    

```bash
if [ ! -f "package.json" ]; then
    print_message "$RED" "Error: 'package.json' no encontrado."
    exit 1
fi
```

**Tip**: Controlar errores y salir temprano es esencial en scripts Bash para evitar que el flujo continúe cuando las condiciones no son las correctas.

---

#### **6\. Automatiza el manejo de procesos**

* El script incluye funciones para **detener procesos** locales o contenedores en ejecución, lo que es clave para limpiar después de cada ejecución:
    

```bash
stop_node_server() {
    PIDS=$(lsof -t -i:"$PORT")
    if [ -n "$PIDS" ]; then
        echo "$PIDS" | xargs kill -9
    fi
}
```

**Tip**: Asegúrate de limpiar los procesos o contenedores para evitar que el puerto quede bloqueado o que queden recursos innecesarios ejecutándose.

---

#### **7\. Usa** `trap` para manejar la limpieza al salir

* Este script maneja la limpieza de recursos (como directorios temporales) usando `trap`. Esto asegura que, incluso si el script es interrumpido, la limpieza ocurra:
    

```bash
trap 'cleanup; show_running_processes; exit 0' EXIT
```

**Tip**: Implementar un *trap* para la limpieza es una buena práctica en Bash scripting, especialmente cuando se trabaja con archivos o procesos temporales.

---

#### **8\. Desactiva debugging para producción, pero habilítalo durante el desarrollo**

* El script incluye `set -x`, que está comentado, pero se puede habilitar para hacer un *debugging* más sencillo:
    

```bash
#set -x
```

**Tip**: Habilita el modo de *debugging* (`set -x`) cuando estés depurando un problema y desactívalo en producción para mantener los logs limpios.

---

### Desafio

Crear un script de automatización para un pipeline de CI/CD que prepare, construya y despliegue una aplicación web simple.

### Requisitos

1. **Script de Automatización**
    
    * Crea un script en Bash llamado `ci_cd_`[`automation.sh`](http://automation.sh).
        
    * El script debe realizar las siguientes tareas:
        
        * a. Obtener la versión actual del proyecto (de package.json, [CHANGELOG.md](http://CHANGELOG.md) o Git tags).
            
        * b. Obtener el nombre del repositorio y la rama actual.
            
        * c. Construir el nombre de la imagen Docker basado en el repositorio y la versión.
            
        * d. Obtener información del último commit (autor y correo electrónico).
            
        * e. Verificar si las herramientas necesarias (git, docker, jq) están instaladas.
            

Revisa el Repositorio [Pasos a Seguir](https://github.com/roxsross/roxs-projects-blog/blob/devops-cars-management-system/DESAFIO2.md)

---

### **Conclusión**

Este script en Bash es un excelente ejemplo de cómo automatizar tareas comunes en el ciclo de vida de desarrollo de una aplicación, desde la verificación de dependencias hasta la ejecución de aplicaciones localmente o en contenedores. Usar funciones modulares, manejo adecuado de errores, y técnicas avanzadas como `trap` y `set -euo pipefail` garantiza que tu script sea seguro, escalable y fácil de mantener.

Implementar estos tips y seguir buenas prácticas de scripting te permitirá mejorar tus habilidades y evitar sorpresas inesperadas en producción. ¡Pruébalo y mejora la automatización de tus despliegues!
