Añadiendo aprobaciones manuales en despliegues con Github Actions

Tiempo de lectura: 7 minutos
Imagen ornamental con el logo de Github Actions para la entrada "Añadiendo aprobaciones  manuales en despliegues con Github Actions"

Ha llegado la vuelta de las vacaciones y con ello la vuelta a las buenas costumbres. He de reconocer que después del parón de navidades me ha costado más de lo habitual volver a escribir. Muchos frentes abiertos durante las fiestas y no acababa de encontrar ningún tema del que me apeteciese hablar… Pero eso por fin ha cambiado y he encontrado algo que personalmente me parece realmente interesante, Github ha añadido soporte para entornos y aprobaciones manuales.

He de aclarar aquí, que el soporte a diferentes entornos es anterior a los aprobadores manuales aunque yo los nombre a la vez.

De dónde venimos

A la gente que me conoce no le sorprende el hecho de que siempre este con un ojo puesto en como avanzan los procesos de CI/CD en diferentes proveedores. Soy firme defensor de los procesos automatizados tanto para la integración como para el despliegue. Muestra de ello son las entradas sobre el tema durante estos años.

Es por eso que cuando aparecieron las Github Actions fue una grata sorpresa, Microsoft hacia una fuerte apuesta por llevar sus herramientas a Github. De hecho, hay una entrada específica hablando sobre Github Actions, y tuve la suerte de charlar un rato con Alberto Gimeno ☕️ (@gimenete) en Github Actions: ¿Hasta dónde podemos llegar?.

El titular de ese momento es que Github Actions estaba todavía muy por detrás de otras herramientas como Azure Pipelines, ya que las posibilidades de CI/CD que teníamos a nuestra disposición se limitaban prácticamente a hacer integraciones y algún despliegue sencillo. Esto por suerte a cambiado como vamos a ver.

¿Por qué son tan importantes los aprobadores manuales en Github Actions?

Pues aquí no hay mucho donde rascar, los despliegues a producción tienen que ser controlados, cualquier código no puede llegar a producción. De acuerdo que trabajamos con metodologías agiles y desplegamos a producción en ciclos cortos, pero eso no es carta blanca para que los despliegues a producción sean «la jungla».

Aquí vamos a aclarar que desde luego una aprobación manual no tiene por qué ser ni el requisito elegido, ni el único. Existen otros tipos de aprobaciones que no son manuales, pero esto es el mínimo que nos permite tener despliegues controlados.

¿Y eso de los entornos?

Más allá de opiniones personales (posiblemente con muchos sesgos), vamos a lo que no tiene discusión, que es el proceso para poder tener aprobadores. El concepto de aprobador manual en Github Actions (y otros muchos proveedores) no es otra cosa más que una acción que debe hacer una persona para poder iniciar los pasos contra cierto entorno.

Esto nos lleva a otro punto interesante entre las novedades de Github, los entornos. Es muy habitual que diferentes etapas del desarrollo tengan diferentes requisitos, por poner un ejemplo, si trabajamos con kubernetes en producción, posiblemente tengamos un cluster potente, autoescalable y bien monitorizado y supervisado con alertas. En cambio, un cluster de desarrollo puede ser algo sencillito, con apenas configuraciones de seguridad y sin monitorización y/o alertas.

Esto se traduce en una ‘entidad’ llamada entorno (‘environment’) que contendrá valores específicos para ciertas variables de los despliegues como el tamaño del cluster del ejemplo anterior. Este entorno es además sobre el que añadiremos a los distintos aprobadores que deben dar su autorización para que el proceso pueda seguir en él.

Para poder comprobar que efectivamente tenemos diferentes valores en las mismas variables, vamos a utilizar un programa de consola al que vamos a llamar desde los diferentes entornos:

class Program
{
    static void Main(string[] args)
    {
        var env = Environment.GetEnvironmentVariable("MYVAL");
        int.TryParse(env, out var id);
        var message = id switch
        {
            1 => "Development",
            2 => "Production",
            _ => "MYVAL is not defined"
        };
        Console.WriteLine(message);
    }
}

Con ese programa listo, simplemente vamos a crear los entornos y a registrar un secreto llamado ‘MYVAL’ en ellos, cuyo valor sea 1 y 2 en desarrollo y producción respectivamente.

Para esto, basta con ir a la pestaña de ‘Settings’, dentro de ella al menú ‘Environments’ y después pulsar sobre ‘New environment’:

La imagen señala la pestaña 'Settings', dentro de ella el botón 'Environments' y dentro de este panel el botón 'New environment'

Esto nos permitirá darle un nombre al entorno. Tras esto, pasamos directamente a configurarlo. Es desde esta sección precisamente donde vamos a añadir nuestro secreto de ejemplo ‘MYVAL’ con su valor concreto:

La imagen señala el botón 'Add Secret' dentro del panel de configuración del entorno

Para poder probar que nuestros entornos funcionan correctamente, vamos a crear nuestro workflow para Github Actions donde vamos a compilar nuestro programa de ejemplo y lo vamos a ejecutar tanto en desarrollo como en producción:

name: CI/CD

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

env:
  PACKAGE_PATH: ./package
  PACKAGE_NAME: console_sample
  CONFIGURATION: Release
  DOTNET_CORE_VERSION: 5.0.x
  DOTNET_CLI_TELEMETRY_OPTOUT: 1
  DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/[email protected]
    - name: Setup .NET
      uses: actions/[email protected]
      with:
        dotnet-version: ${{ env.DOTNET_CORE_VERSION}}
    - name: Restore dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --no-restore --configuration ${{ env.CONFIGURATION }}
    - name: Publish
      run: dotnet publish --no-build --configuration ${{ env.CONFIGURATION }} --output "${{ env.PACKAGE_PATH }}"
    - name: Publish Artifacts
      uses: actions/uplo[email protected]
      with:
        name: ${{ env.PACKAGE_NAME }}
        path: ${{ env.PACKAGE_PATH }}

  development:
    # Avoid to continue if the trigger is a PR
    if: github.event_name == 'push'
    needs: build
    name: Deploy Development Environment
    runs-on: ubuntu-latest
    environment:
      name: Development
    env:
      MYVAL: ${{ secrets.MYVAL }}
    steps:
    # Download artifacts
    - name: Download artifacts
      uses: actions/[email protected]
      with:
        name: ${{ env.PACKAGE_NAME }}
    - name: Setup .NET
      uses: actions/[email protected]
      with:
        dotnet-version: ${{ env.DOTNET_CORE_VERSION}}
    - name: Execute 
      run: dotnet ManualApproval.dll    

  # This stage doesn't need to use if because the previous stage applies it
  production:
    needs: development
    name: Deploy Production Environment
    runs-on: ubuntu-latest
    environment:
      name: Production
    env:
      MYVAL: ${{ secrets.MYVAL }}
    steps:
    # Download artifacts
    - name: Download artifacts
      uses: actions/[email protected]
      with:
        name: ${{ env.PACKAGE_NAME }}
    - name: Setup .NET
      uses: actions/[email protected]
      with:
        dotnet-version: ${{ env.DOTNET_CORE_VERSION}}
    - name: Execute 
      run: dotnet ManualApproval.dll    

Nada raro en el fichero, simplemente es un workflow con 3 jobs, uno donde vamos a generar el artefacto, y 2 para nuestra ejecución (uno por entorno). La novedad aquí es que dentro del job, vamos a indicar el entorno al que pertenece utilizando:

environment:
  name: Nombre_Entorno

Además, para probar que cambia en función del valor que hemos asignado al entorno, creamos una variable que utiliza ese secreto:

env:
  MYVAL: ${{ secrets.MYVAL }}

Basta con hacer un push al repositorio para que automáticamente se ejecute, y si vamos a la sección de acciones, vamos a poder encontrarnos con que el paso ‘Execute’ de cada uno de los jobs, muestra el valor correcto:

La imagen muestra la sección de Actions y el resultado de la ejecución del paso 'Execute' del job Development donde se lee 'Development' en la entrada "Añadiendo aprobaciones  manuales en despliegues con Github Actions"

Añadiendo aprobadores manuales a nuestra Github Action

Con esto que hemos hecho hasta ahora, ya tenemos 2 entornos diferenciados. Con esto hecho, ya nos queda lo más fácil, añadir aprobaciones manuales a nuestras Github Actions.

Para esto, es suficiente con que vayamos al entorno en concreto que queremos que tenga una aprobación manual, y marcar el check ‘Required reviewers’. Esto nos mostrará un campo de entrada donde vamos a poder buscar a integrantes del repositorio a los que añadir. Aquí hay que tener en cuenta que ahora mismo el número máximo de aprobadores es de 6. Una vez añadidos, simplemente pulsamos el botón ‘Save protection rules’.

La imagen señala como añadir aprobación manual a Github Actions. Señala el check 'Required reviewers', el campo de entrada y el botón 'Save protection rules'

Una vez que hemos guardado los cambios, las nuevas ejecuciones de nuestra acción se pararán cuando lleguen al entorno de producción:

La imagen muestra una etiqueta amarilla donde dice: 'JorTurFer requested your review to deploy to Production'

En este punto, se habrá enviado un email a las personas con capacidad de aprobar el proceso, y una vez se tenga la aprobación, el proceso continuará con normalidad.

La imagen muestra los botónes de aprobación y de rechazo de la acción

Conclusión

Aunque Github Actions sigue estando un paso por detrás de su hermano mayor Azure Pipelines, está dando unos saltos agigantados para alcanzarlo y seguramente superarlo. Es todo un placer ver como esta herramienta evoluciona y va añadiendo nuevas características.

No olvidemos que es una herramienta gratuita en una gran cantidad de casos, pero que aporta mucho valor en nuestros proyectos. Quizás todavía no esté preparada para grandes proyectos con despliegues extremadamente complejos, pero sí que tiene una madurez suficiente para proyectos pequeños y medianos. En este ejemplo nos hemos limitado a ejecutar una aplicación de consola por sencillez, pero lo habitual sería hacer despliegues directamente.

Por último, aunque la aprobación manual es insuficiente para automatizar totalmente los procesos, abre la puerta a otro tipo de aprobaciones más sofisticadas en el futuro. Y eso sin hablar de que acciones manuales es más que suficiente para una cantidad de casos enorme.

Si quieres, puedes echarle un ojo en detalle al repositorio en Github que es como mejor se aprende 🙂

Deja un comentario