Entrega continua con Bitbucket pipelines, Digital Ocean y Laravel
Published: 2022-05-03 12:54:58

En esta guía vamos a crear un autodeploy basado en los commits y pull request hace una rama.

> **Esta es una versión básica donde se sincroniza los nuevos archivos y modificados del repositorio bitbucket hacia digitalocean. Sin el uso de docker**

Para este ejemplo vamos a tener 2 Droplets, uno para testing y otro para producción. Tengo bitbucket en lenguaje inglés

Pasos que van a suceder
1. Al actualizar la rama master vamos a copiar los archivos nuevos al droplet
2. vamos a ejecutar un script que instale las dependencias, ejecutar el test, agregar los permisos correctos y otras tareas que necesitemos hacer 
3. Vamos a notificar por un canal de Slack cuando el despliegue se haya realizado

## Determinar ramas y entornos
El primer paso es decidir que ramas se desplegarán en qué entorno. Para este ejemplo usaremos la siguiente configuración

| Branch      | Entorno     | Descripción
| ----------- | ----------- |------------
| develop     | -           | Rama para integrar los features
| master      | Stagging    | Entorno de testing / Pre-produción
| prod-v1     | Production  | Entorno de producción

## Habilitar Pipelines
Ingresar a
`Menu Repositorio -> Pipelines -> Starter Pipeline`
Visualizaremos la pantalla siguiente:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9jm7hjqlhfztk9l7o7ar.png)

- Continuamos con el botón *Commit File*. Esto creará un archivo llamado `bitbucket-pipelines.yml` en la raíz del proyecto para configurar el despligue.

> ES MUY IMPORTANTE NO PONER CUENTAS NI APIKEYS EN EL ARCHIVO bitbucket-pipelines.yml 

Los datos sensibles los manejaremos por variables que explicaremos más adelante.

## Autenticar Bitbucket con DigitalOcean
- Creamos un SSH ingresando a 
`Menu Repositorio -> Repository Settings-> Pipelines -> SSH Keys`
Veremos la siguiente pantalla
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z6m0uj9b7ouhewnumm3r.png)

- Le damos click a **Generate Keys**
- Ingresamos al droplet y abrimos el archivo de llaves autorizadas con el siguiente comando
`nano ~/.ssh/authorized_keys`
- Copiamos la **public key** que generamos en bitbucket y la pegamos en el archivo que abrimos en el droplet
- Pegamos el IP del droplet en el input **Host address** y hacemos click en el botón **Fetch**, esto generará un *fingerprint* 
- Se habilitará el botón **Add Host**, clickeamos y esto agregará el registro a bitbucket.

Con estos pasos tenemos conectado vía SSH bitbucket con nuestro droplet Digital Ocean.

## Configurar entornos
En la siguiente dirección 
`Menu Repositorio -> Repository Settings-> Pipelines -> Deployments`

encontraremos los entornos por default que son: Test, Staging y Production

### variables de entorno
Las variables de entorno son valores que sólo se pueden utilizar en el entorno que determinemos mediante la el atributo `Deployment` del archivo bitbucket-pipelines.yml

> Si necesitamos guardar variables globales para todos los entornos estos deben ir en la siguiente ruta
`Menu Repositorio -> Repository Settings-> Pipelines -> Repository Variables`

> Para aprender más sobre variables puedes entrar [a este enlace](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/)

Para este ejemplo vamos a crear las siguientes variables

| Nombre      | Descripción
| ----------- | ------------
| SSH_USER    | nombre del usuario para la autenticación por SSH
| SERVER_IP   | IP del droplet
| SCRIPT_PATH | Ruta en el droplet donde se encuentra el script que ejecutaremos luego de copiar los archivos
| WEBHOOK_URL | URL del webhook de Slack

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/liu2665l7y22ezn2ommn.png)
 
## Archivo bitbucket-pipelines.yml
En internet y en bitbucket hay muchos ejemplos del uso de este archivo pero para lo que necesitamos explicaremos lo básico para su funcionamiento. La configuración que vamos a usar es la siguiente:
```
pipelines:
  branches:
    master:
      - step:
          name: Deploy to droplet
          deployment: Staging
          script:
            - pipe: atlassian/rsync-deploy:0.7.0
              variables:
                USER: $SSH_USER
                REMOTE_PATH: '/var/www/app'
                LOCAL_PATH: '${BITBUCKET_CLONE_DIR}'
                SERVER: $SERVER_IP
                DEBUG: 'true'
            - pipe: atlassian/ssh-run:0.4.0
              variables:
                SSH_USER: $SSH_USER
                SERVER: $SERVER_IP
                COMMAND: $SCRIPT_PATH
            - pipe: atlassian/slack-notify:2.0.0
              variables:
                WEBHOOK_URL: $WEBHOOK_URL
                MESSAGE: '"Se ha desplegado correctemente la rama master en el entorno de stagging"'
```

> Notarán que no hay image, ya que no uso docker no lo vi necesario ponerlo y evitar demorar por la descarga de la imagen

| Nombre    | Descripción
| ----------| ------------
| branches  | nombre que agrupa las ramas
| step      | Paso que ejecuta una acción
| name      | Título del paso a ejecutar
| deployment| entorno del que se usarán las variables
| script    | Grupo de acciones que se ejecutarán
| pipe      | Integración que facilita el uso de un servicio externo

> Puedes encontrar muchas integraciones pipe en [este enlace] (https://bitbucket.org/product/features/pipelines/integrations)



# Configuración en el servidor (Droplet)
Para este ejemplo crearemos la carpeta `app` en la siguiente dirección `/var/www/app/`. Dentro de esa carpeta crearemos dos archivos.
- `.env` Archivo de variables de entorno que será copiado en cada despliegue
- `deploy.sh` script que se ejecutará después de tener copiados los archivos 

La estructura deberá quedar así:
```
/var/www/
└───app
│   │   .env
│   │   deploy.sh
```

# Rsync
Con las variables puestas correctamente rsync copiará los cambios en el servidor dentro de una carpeta llamada `build` que si no existe la creará.

# Ejecutar bash en droplet
Como segundo paso ejecutaré un comando en el servidor que me dejará la aplicación lista para ser usada.
mi archivo se llamará `deploy.sh` y este será el contenido
```
#!/bin/bash

## Copiar archivo de variables de entorno dentro del proyecto
cp /var/www/app/.env /var/www/app/build/.env

## Ingresar a la carpeta del proyecto para ejecutar los
## siguientes comandos.
cd /var/www/app/build

## instalar composer 
composer install --no-interaction --no-progress --optimize-autoloader --no-dev

## Actualizar archivo de dependencias.
composer dump-autoload -n

php artisan migrate --seed --force

##  Asignar los permisos adecuados para un proyecto laravel
sudo chown -R $USER:www-data .
sudo find . -type f -exec chmod 664 {} \;
sudo find . -type d -exec chmod 775 {} \;
sudo chgrp -R www-data storage bootstrap/cache
sudo chmod -R ug+rwx storage bootstrap/cache
``` 

# notificación a slack
Finalmente con el webhook de un canal de slack se enviará la notificación cuando el proceso haya terminado