Despliegue automático desde GitHub a un servidor a través de SFTP (GitHub Actions)

Publicado el 28 de marzo de 2022 por Flikimax
Despliegue automático desde GitHub a un servidor a través de SFTP (GitHub Actions)

Al subir a producción mi primera web con React, note que había que hacer este proceso repetitivo de construir la app (build), hacer su respetivo zip y desplegarla manualmente en el servidor, esto no resulta ser un inconveniente cuando la web no se está actualizando constantemente, pero en mi caso y como esta página web será utilizada para documentar un software, requiero actualizarla seguido.

¿Qué es el Despliegue automático?

El Despliegue automático o implementación continua es una estrategia en el desarrollo de software donde los cambios de código en una aplicación se liberan automáticamente en el entorno de producción. Esta automatización está impulsada por una serie de pruebas predefinidas. Una vez que las nuevas actualizaciones pasan esas pruebas, el sistema envía las actualizaciones directamente a los usuarios del software.

Requisitos

  1. Acceso al servidor.
  2. Conocimientos en servidores y consola.
  3. Se asume que el dominio a utilizar está apuntando al servidor que se va a usar.

Tutorial

1. Crear la llave SSH

Para hacer esta tarea usamos un comando de sistema: ssh-keygen -t rsa. Este comando se encarga del proceso de creación de los archivos de la llave.

ssh-keygen -t rsa
Presionar enter hasta finalizar el comando.

Una vez iniciado este comando nos va a solicitar dos cosas:

  1. La localización donde almacenar la clave y el nombre del archivo.
  2. La clave para poder usar esta llave ssh.

Recomiendo usar la ubicación por defecto que por lo general en ubuntu suele ser /home/ubuntu/.ssh.

Para nuestro ejemplo no agregaremos una clave a nuestro par de llaves ssh.

Nos dirigimos a la ruta /home/ubuntu/.ssh o la ruta en la cual esta nuestra carpeta .ssh, si no encontramos un archivo llamado authorized_keys, lo creamos, lo abrimos y pegamos el contenido de nuestra llave pública en dicho archivo.

La llave pública (id_rsa.pub si se dejó el nombre por defecto) contiene algo como:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQQABAAABBAQC3I543JuHbJLlRScV0K/tLRca8qwSplPt9JrUICb8NeOa4o0zknY13KtrHeGWptvBTQHPPoKz3o7sW+8Ar9YHWhPTh8GYmfqXGs/+HGXKplJtqq8NWErA+BiROpod2ZjtWZu/ZkRXZp8QomgkjVsc/NcmoHu4OM9pbkLydvKSvyqzq2WRR+G8S8LgkwXsPk4fdEEv3NXsZcDF7e0J5VHPyJCB1KV5XY5Uhbaa96MBgrQqsY3qBG5oraGxIkTBUaibmkKdfu0Yc44Xxo/pTrelJKWWIIxk4k/8DsurNoxja8i/3YU7mbqmfGeF3/AO9LreJao/yPacHk/QeeC2jl0VUuO/2x periodicov2

Si el archivo authorized_keys ya existe y contiene una llave pública, basta con agregar una nueva línea y pegar el contenido de nuestra llave pública previamente creada.

2. Crear repositorio

Te dejo la documentación oficial de Github aquí en caso de que necesites una guía.

El repositorio puede ser tanto público como privado.

3. Configurar Actions secrets

Con nuestro par de llaves creadas y nuestro repositorio seleccionado, iremos a los Setting al apartado de Secrets -> Actions, estando ahí, agregaremos un nuevo Secret para el repositorio.

Este nuevo secret tendrá por nombre SSH_PRIVATE_KEY y su valor será el contenido de la llave privada previamente creada (id_rsa, el archivo sin la extensión .pub) y su contenido es algo parecido a este:

-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

Agregamos nuestra secret.

4. Configurar Actions

Nos iremos al apartado de Actions en Github.

Luego hacemos clic en set up a workflow yourself.

Esto creará un archivo llamado ./.github/workflows/main.yml en tu repositorio, el nombre de main.yml puedes modificarlo pero tienes que mantener la extensión .yml.

Este archivo tiene una configuración básica de un workflow que en este caso vamos a ignorar y a reemplazar por:

name: CI-SFTP
 
# Controla cuándo se ejecutará la acción.
on:
  # Activa el workflow en eventos push o pull request pero sólo para la rama que elijas.
  push:
    branches: [ main ]
 # pull_request:
   # branches: [ main ]
 
  # workflow_dispatch: Le permite ejecutar este workflow manualmente desde la pestaña Acciones.
  workflow_dispatch:
 
# Un workflow se compone de uno o varios jobs que pueden ejecutarse de forma secuencial o en paralelo.
jobs:
  # Este workflow contiene un solo job llamado "SFTP".
  deploy-via-sftp:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: SFTP Deploy
        uses: wlixcc/[email protected]
        with:
          # username: Nombre de usuario con el cual nos conectaremos al servidor.
          username: ubuntu
          # server: Nombre del servidor o IP.
          server: 0.0.0.0
          port: 22 
          ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}

          # local_path: Ruta dentro del repositorio la cual quieres copiar en el servidor,por defecto es ./*
          local_path: ./*
          
          # remote_path: Ruta remota donde se copiaran los archivos del repositorio.
          remote_path: /var/www/deploy.flikimax.com/htdocs/
          
          # SFTP args
          args: '-o ConnectTimeout=5'

Es necesario que agregues los valores de server remote_path, para que el action sepa a qué servidor dirigirse y en qué lugar hará deploy.

Guardamos dando clic en Start commit.

5. Probar que funcione

Vamos a nuestro repositorio, creamos un nuevo archivo llamado index.html e insertamos algún contenido y lo guardamos.

Ahora nos dirigimos a los actions de nuestro repositorio y podremos ver algo como:

Si hacemos clic en update index.html, podemos ver en detalle lo que está sucediendo y en caso de que algo falle, ver exactamente que causo que fallara.

Podemos ver que el deploy se hizo correctamente y que el servidor recibió correctamente los archivos del repositorio.

6. Notas finales y Workflow para una React App

Hay varias cosas que se tienen que tener en cuenta para que esta guía funcione.

  1. El nombre SSH_PRIVATE_KEY de nuestro secret debe coincidir con el valor del key ssh_private_key del workflow de esta forma ${{ secrets.SSH_PRIVATE_KEY }}, si se modifica el nombre SSH_PRIVATE_KEY por ejemplo a ALOHAMUNDO, el valor de ssh_private_key del workflow deberá ser: ${{ secrets.ALOHAMUNDO}}.
  2. La ruta del servidor donde se subirá el código del repositorio tiene que tener los permisos necesarios para realizar esta acción.

Como plus te comparto el .yml que use para hacer deploy con una React App.

name: CI-SFTP
 
# Controla cuándo se ejecutará la acción.
on:
  # Activa el workflow en eventos push o pull request pero sólo para la rama que elijas.
  push:
    branches: [ main ]
#  pull_request:
#    branches: [ main ]
 
  # workflow_dispatch: Le permite ejecutar este workflow manualmente desde la pestaña Acciones.
  workflow_dispatch:
 
# Un workflow se compone de uno o varios jobs que pueden ejecutarse de forma secuencial o en paralelo.
jobs:
  # Este workflow contiene un solo job llamado "SFTP".
  deploy-via-sftp:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16.x]
    steps:
      - uses: actions/checkout@v2
      
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node-version }}
      - name: Install Packages
        run: npm install
      - name: Build page
        run: npm run build
      
      - name: SFTP Deploy
        uses: wlixcc/[email protected]
        with:
          # username: Nombre de usuario con el cual nos conectaremos al servidor.
          username: ubuntu
          # server: Nombre del servidor o IP.
          server: 0.0.0.0
          port: 22 
          ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}

          # local_path: Ruta dentro del repositorio la cual quieres copiar en el servidor,por defecto es ./*
          local_path: ./build/*
          
          # remote_path: Ruta remota donde se copiaran los archivos del repositorio.
          remote_path: /var/www/deploy.flikimax.com/htdocs/
          
          # SFTP args
          args: '-o ConnectTimeout=5'

Recuerda modificar el server y la remote_path.

Fuentes: