paint-brush
Configuración de .gitlab-ci.ymlby@josejaviasilis
83,151
83,151

Configuración de .gitlab-ci.yml

tldt arrow
ES

Originalmente, esta era una publicación completa, pero se hizo tan grande que tuve que dividirla en 2. Esto continúa desde la publicación n.º 2, <a href="https://medium.com/@josjaviasilis/configuring-gitlab-ci-on-aws-ec2-using-docker-7c359d513a46" target="_blank">Configure GitLab CI en AWS EC2 usando Docker.</a>

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Configuración de .gitlab-ci.yml
Jose Javi Asilis HackerNoon profile picture

Originalmente, esta era una publicación completa, pero se hizo tan grande que tuve que dividirla en 2. Esto continúa desde la publicación n.º 2, Configure GitLab CI en AWS EC2 usando Docker.

Publicaciones:

  1. [Tutorial — Guía] Instalación de GitLab, GitLab CI en AWS EC2 desde cero.
  2. Configurar GitLab CI en AWS EC2 mediante Docker
  3. Configuración de .gitlab-ci.yml (Esta publicación)
  4. Solución de problemas de GitLab y GitLab CI

#1- Entendiendo el archivo .gitlab-ci.yml

El archivo .gitlab-ci.yml es un archivo YAML que crea en la raíz de su proyecto. Este archivo se ejecuta automáticamente cada vez que envía una confirmación al servidor. Esto desencadena una notificación al corredor que especificó en el n. ° 3 y luego procesa la serie de tareas que especificó. Entonces, si lo presiona 3 veces, ¡lo ejecutará 3 veces! Es por eso que si está impulsando múltiples, desea un corredor más rápido o un corredor separado por máquina.

Tenga en cuenta que, dado que usamos Docker , las tareas siempre comienzan en un estado limpio de la imagen. Esto significa que todos los archivos y modificaciones que coloque o haga dentro de .gitlab-ci.yml se revertirán cada vez que envíe una confirmación al servidor. Puede evitar esto especificando cachés.

El contenido de los archivos está compuesto por las claves que podrá encontrar en esta página. No hay ningún orden que deba seguir, pero tenga mucho cuidado con las sangrías . Esto puede compensar o deshacer su proyecto. Puede verificar con un filtro YAML en línea para ver si funciona antes de presionar.

Estaré trabajando en una aplicación NodeJS de ejemplo. Con Karma como corredor de pruebas. He publicado un .gitlab-ci.yml que usé en uno de mis proyectos anteriores en este GitHub Gist . (¡Por favor, míralo!)

Esta página puede darle otra perspectiva.

Desglosándolo:

imagen: nodo: 9.4.0

La clave de imagen toma una imagen de Docker Hub y la usa como imagen base. GitLab basará todas las pruebas en esta imagen. Si está realizando un proyecto en Ruby, Java, Go, PHP, etc., especifique la imagen correcta de Docker Hub.




cache:paths:- node_modules/- .yarn

Esto crea una carpeta de caché temporal que evita que node_modules y .yarn se vuelvan a crear cada ejecución de CI (cada vez que realiza una confirmación).

antes_script:

  • apt-get update -qq && apt-get install
  • otro comando que se ejecutará después del anterior
  • puedes seguir agregando líneas y líneas.

before_script le dice a GitLab que ejecute lo que haya especificado antes que nada. Puede considerar esto como un guión de preparación.

#1.1 Etapas de comprensión

Las etapas son una serie de pasos por los que pasa su código para llegar a su destino final (Producción). GitLab le permite definir cualquier número de etapas con cualquier nombre. Lo hace especificándolo bajo la clave de etapa, en el orden en que desea que se ejecuten.

Luego, GitLab irá ejecutando cada uno de ellos, paso a paso. Si uno de ellos falla, impide la ejecución de los siguientes.

etapas:

  • construir
  • prueba
  • puesta en escena
  • abiertoSr.
  • producción

En la parte anterior, ejecutará primero la etapa de build , hasta llegar a la production .

#1.2 Definición de las acciones de las etapas en el archivo .gitlab-ci.yml

Usted define qué etapa se ejecutará especificando primero una clave principal de nombre de etapa. Esta clave puede tener el nombre que desee y puede contener espacios .

Por ejemplo:






Build My App:stage: buildtags:#- you_would_put_your_tag_in_here#- nodebefore_script:

  • conjunto de configuración de hilo carpeta de caché .yarn


  • hilo installscript: - compilación de ejecución de npm

El nombre de la etapa es Build My App y especifica una clave llamada stage que hace referencia a la etapa que creó anteriormente, en la lista de etapas.

before_script se ejecuta igual que el que especificamos anteriormente, solo en el contexto de la etapa de build : nada se ejecutará hasta que se ejecuten esos scripts.

En este caso, estamos usando yarn en lugar de npm , y está creando una carpeta de caché que contendrá toda la configuración de yarn, que no se volverá a crear en la ejecución de cada proyecto (cada vez que ingrese al repositorio)

#1.3 Etiquetas

Si siguió el artículo anterior que escribí sobre "etiquetas" ( Punto #3.1, del artículo 2 ), ¡aquí es donde las especificamos! Si las etiquetas coinciden con la que especificamos en el corredor, esto activará el corredor una vez que haya terminado. Usted especifica cada etiqueta en su propia línea. Si se refiere al ejemplo anterior, si elimina el # antes del node , eso significa que esa etapa específica solo funcionará en los corredores con la etiqueta de nodo. Si no especifica ninguna etiqueta (omita la clave de tags ), puede conectarse al corredor (siempre que no esté bloqueado en el proyecto actual).

Nota: 5.4–5.6 Es una descripción general de las partes del archivo. Estoy explicando con más detalle POR QUÉ, cómo y Cuáles son los contenidos en el #7.

#1.4 Entorno de prueba

Una vez más, no importa cómo nombre su escenario. En este caso, simplemente lo llamé "Prueba" para probar.






Test:stage: testbefore_script:- yarn config set cache-folder .yarn- yarn installscript:

Instala cromo

 - wget -q -O — [https://dl-ssl.google.com/linux/linux\_signing\_key.pub](https://dl-ssl.google.com/linux/linux_signing_key.pub) | apt-key add - - echo 'deb \[arch=amd64\] [http://dl.google.com/linux/chrome/deb/](http://dl.google.com/linux/chrome/deb/) stable main' | tee /etc/apt/sources.list.d/google-chrome.list - apt-get update - apt-get install google-chrome-stable -y # Runs the tests. - npm run test:karma-headless

Hay mucho que hacer allí. La metodología de integración continua se basa en las pruebas que ejecuta en su máquina local. Esas pruebas van acompañadas de su ejecución en la máquina real que va a implementar.

Dado que esto es muy específico para Node y JavaScript (de lo que está hecho mi proyecto), necesito preparar el campo para que puedan ejecutarse perfectamente. En este caso, utilizo karma como corredor de pruebas para ejecutar todas mis pruebas locales. Requiere de un navegador web local, en este caso, Google Chrome.

Por lo tanto, necesito emitir un comando de instalación de Google Chrome (recuerde que cada vez que presionamos, todo comienza desde un estado limpio) y ejecutar las pruebas.

Si todas las pruebas tienen éxito, GitLab pasará automáticamente a la siguiente sección.

#1.5 Abrir una solicitud de fusión


# Recuerde tener el PRIVATE_TOKEN generado. Esto solo debe hacerse una vez por proyecto y no por usuario. # Una vez que lo agregue (necesita privilegios de maestro) como variable secreta, debería funcionar.






Solicitud de combinación abierta: # Lo obtuve de aquí: https://gitlab.com/tmaier/gitlab-auto-merge-request/blob/develop/.gitlab-ci.yml imagen: tmaier/gitlab-auto-merge-requeststage: openMrscript:- bash ./gitlab-deploy/auto-merge-request.sh # El nombre del script

Después de que nuestro entorno de prueba tenga éxito, queremos que GitLab abra automáticamente una solicitud de fusión que podamos fusionar con éxito para dominar si pasa.

# 1.6 Entornos de puesta en escena y producción



Implementar en Staging:stage: stagingbefore_script:

Genera para conectarse a la unidad de AWS la clave SSH.

  • mkdir -p ~/.ssh
  • echo -e “$SSH_PRIVATE_KEY” > ~/.ssh/id_rsa

Establece el permiso en 600 para evitar un problema con AWS

que está demasiado desprotegido.

  • chmod 600 ~/.ssh/id_rsa
  • '[[ -f /.dockerenv ]] && echo -e “Host *\n\tStrictHostKeyChecking no\n\n” > ~/.ssh/config'




secuencia de comandos: - bash ./gitlab-deploy/.gitlab-deploy.staging.shenvironment:name: staging

Expone un botón que, cuando se hace clic, lo lleva a la URL definida:

 url: [http://ec2-11-44-514-91.us-east-2.compute.amazonaws.com:3001](http://ec2-13-59-173-91.us-east-2.compute.amazonaws.com:3001)



Implementar en Production:stage: productionbefore_script:

Genera para conectarse a la unidad de AWS la clave SSH.

 - mkdir -p ~/.ssh - echo -e "$SSH\_PRIVATE\_KEY" > ~/.ssh/id\_rsa

Establece el permiso en 600 para evitar un problema con AWS

que está demasiado desprotegido

 - chmod 600 ~/.ssh/id\_rsa - '\[\[ -f /.dockerenv \]\] && echo -e "Host \*\\n\\tStrictHostKeyChecking no\\n\\n" > ~/.ssh/config'






script:- bash ./gitlab-deploy/.gitlab-deploy.prod.shenvironment:name: production# Expone un botón que, cuando se hace clic, lo lleva a la URL definida: url: http://ec2-13-59-173- 91.us-east-2.compute.amazonaws.com:81

cuando: manuales

Hablaré sobre el contenido de esto en la parte inferior.

#2 Tuberías

Cuando envías el repositorio de git a GitLab con el archivo .gitlab-ci.yml , activará automáticamente las canalizaciones. Las canalizaciones son las etapas que definiste en tu .gitlab-ci.yml. En nuestro caso, tenemos build, test, staging, openMr y production. Cada una de esas marcas que ves en la captura de pantalla anterior representa cada una de las etapas. Una cruz roja representará una etapa fallida. Una marca de verificación verde indicará que la prueba pasó con éxito. La barra diagonal identificará que la prueba fue cancelada.

Puede ver una interfaz de línea de comandos que le muestra el desarrollo de cada una de las etapas haciendo clic en el icono y luego haciendo clic en la ventana emergente:

Esta es la pantalla que le muestra después de que la etapa de construcción se haya completado con éxito.

#3- Integración a AWS. Cómo conectar la instancia de GitLab a la instancia de EC2 con su proyecto

Uno de los mayores desafíos es integrar la canalización de CI con su proyecto. Hasta donde yo , GitLab no ofrece una forma nativa de hacer esto. Puede enviar su código a AWS Code Deploy y luego realizar la migración desde allí.

Hay una fantástica guía paso a paso que lo guiará a través de ese proceso por autronix de stackoverflow:


Cómo implementar con Gitlab-Ci en EC2 mediante AWS CodeDeploy/CodePipeline/S3 _He creado un conjunto de archivos de muestra para acompañar la guía proporcionada a continuación._stackoverflow.com

Recomiendo el enfoque anterior sobre el que les voy a mostrar . Haga lo siguiente si por casualidad el enfoque de autronix no funciona.

Esta integración transmite el uso de git (extraemos el repositorio fusionado de GitLab) y lo actualizamos en nuestra instancia EC2, ejecutamos el script de recarga desde npm (suponemos que estamos usando Node en este proyecto) y publicamos los cambios. Puede ver, a estas alturas, que esto parece más un truco que una solución real. Es posible que esto no funcione en entornos muy distribuidos en los que necesite replicar el código base en varias instancias de EC2. Pero, de nuevo, se trata de tener opciones, ¿verdad?

#4: prepare la máquina EC2 que aloja el código implementado.

Este enfoque está inspirado en este post .

Tratamos la máquina EC2, la que aloja el código en producción, como un cliente de GitLab. Creamos una clave SSH que se conecta a GitLab y extraemos el código de allí.

Si recuerdas, del primer tutorial:


ssh-keygen -t rsa -C “tu_nombre@tu_correo.com” ssh-add ~/.ssh/id_rsa

Si tuvo problemas para agregar la clave, intente ejecutar esto primero ( Fuente ):


eval `ssh-agent -s`ssh-add ~/.ssh/id_rsa

Una cosa acerca de este enfoque es que no he encontrado una manera de hacer que funcione con una frase de contraseña, así que cuando te pregunte al respecto, ¡ déjalo en blanco !

Cuando crea la clave, se encuentra en:

~/.ssh/id_rsa.pub

Tenga en cuenta que esta vez no podrá copiar su contenido en el portapapeles a menos que instale "clip"

Nota: Esto consumirá más de 300 MB de espacio en disco. No lo haga a menos que no esté limitado por el espacio en disco.

sudo apt-obtener clip de instalación

gato ~/.ssh/id_rsa.pub | acortar

Otra opción es simplemente ejecutar cat y copiar el resultado del comando.

gato ~/.ssh/id_rsa.pub

Para controlar los posibles riesgos de seguridad, le recomiendo que cree un usuario independiente en GitLab que maneje solo una extracción del repositorio y nada más. Adjuntas la clave pública a esa cuenta.

Creé un usuario fantasma en GitLab que maneja la extracción de GitLab.

Vaya a /admin en su dirección de GitLab. (También puede hacer clic en el icono de la herramienta en la barra de navegación)

Crear un nuevo usuario:

Desmarque "puede crear un grupo". Nivel de acceso “Regular”, Externo “marcado”.

Vaya al proyecto con el que tiene su repositorio:

Busque el nuevo miembro que creó y configúrelo como Reportero.

Vaya a la pestaña Usuarios en el área de administración y haga clic en el nombre del usuario creado recientemente:

Haga clic en "Suplantar" y vaya a la página de claves ssh.

#5- El entorno de Staging, configuración:

Aquí es donde empiezo a explicar qué diablos fue lo que puse arriba.










Deploy to Staging:stage: stagingbefore_script:# Genera para conectarse a la unidad de AWS la clave SSH.- mkdir -p ~/.ssh- echo -e “$SSH_PRIVATE_KEY” > ~/.ssh/id_rsa# Establece el permiso en 600 para evitar un problema con AWS# que está demasiado desprotegido.- chmod 600 ~/.ssh/id_rsa- '[[ -f /.dockerenv ]] && echo -e “Host *\n\tStrictHostKeyChecking no\n\n” > ~ /.ssh/config'


secuencia de comandos: - bash ./gitlab-deploy/.gitlab-deploy.staging.sh




environment:name: staging# Expone un botón que, cuando se hace clic, lo lleva a la URL definida:url: http://ec2-13-14-444-91.us-east-2.compute.amazonaws.com:3001

#5.1- Comunicación con la instancia EC2.

Necesitamos una forma de comunicarnos con la unidad de AWS. La forma en que hacemos esto es tomando la clave privada (¡Cuidado! Información confidencial) del id_rsa generado (El que generamos dentro de nuestra instancia EC2) y enviándolo con nuestro script de shell predefinido (De lo cual hablaré más en un momento ).

El código en before_script lo que hace es que genera un archivo en blanco llamado id_rsa (que coincide con la convención para la clave privada). Lo completamos con una variable personalizada (más sobre eso ahora) cada vez que se ejecuta el proyecto.

GitLab CI le permite almacenar variables en la configuración del proyecto:

Vaya a Su proyecto -> Configuración -> CI/CD -> Variables secretas

Lo que vamos a hacer es tomar el contenido de la id_rsa (clave privada, la que no tiene .pub) y vamos a copiar y pegar su contenido.

Hacemos el mismo procedimiento que hicimos con el archivo público (Tenga en cuenta que este es el id_rsa sin extensión):

gato ~/.ssh/id_rsa | acortar

O, si no instaló el clip, cópielo y péguelo desde la consola:

gato ~/.ssh/id_rsa

Vamos a copiar y pegar ese valor en el formulario de "Variables secretas" y darle una "SSH_PRIVATE_KEY" (Esto coincide con el de .gitlab-ci.yml y puedes verlo en la imagen de arriba)

Una vez que lo tengas, haz clic en “Guardar variables”.

#5.2- Crear un Shell Script para el entorno de Staging

Todavía necesitamos indicar a GitLab que ejecute la solicitud de extracción en nuestro entorno EC2.

Para la separación de preocupaciones y la capacidad de mantenimiento, podemos especificar un archivo de shell externo que ejecutará la extracción desde la rama principal. Llamamos a este archivo .gitlab-deploy.staging.sh Puede llamar a este archivo como desee. Solo recuerda especificarlo en el archivo .gitlab-ci.yml .

Así tengo estructurado mi proyecto.

.gitlab-ci.yml está en la raíz. Mientras que los archivos de shell están en una carpeta llamada gitlab-deploy . Por lo tanto, hacemos referencia a ellos como ./gitlab-deploy/.gitlab-deploy.staging.sh .

El contenido del archivo es el siguiente:

#!/bin/bash


# Obtener lista de servidores: establecer — f









# Variables del servidor GitLab:# Nota: ¡¡No pueden tener espacios!!string=$DEPLOY_SERVERarray=(${string//,/ })# Iterar servidores para implementar y extraer la última confirmación# Cuidado con ; https://stackoverflow.com/a/20666248/1057052 para i en “${!array[@]}”; doecho “Implementar proyecto en el servidor ${array[i]}”ssh ubuntu@${array[i]} “cd ./Staging/vr && git stash && git checkout $CI_BUILD_REF_NAME && git stash && git pull && sudo yarn install && sudo npm ejecutar puesta en escena”

hecho

Como puede ver, lo que estamos haciendo aquí es que estamos ejecutando un git pull y una instalación de los paquetes en el servidor de ensayo.

Era barato y estaba ejecutando ambos: producción y pruebas en el mismo servidor (expuse diferentes puertos). Te recomiendo que tengas diferentes máquinas para eso.

La variable $DEPLOY_SERVER es otra variable personalizada que creamos en la página de variables secretas con la dirección IPv4 de nuestra instancia EC2:

Vaya a Su proyecto -> Configuración -> CI/CD -> Variables secretas

La clave de entorno que especifica el nombre y la URL es "solo para mostrar". Esto le mostrará un botón en la etapa de la consola que le indicará la URL que especifique allí. Esto es opcional y se puede omitir.

#6 Abrir solicitudes de combinación automáticamente

GitLab no abrirá automáticamente las solicitudes de combinación. Es por eso que tenemos que hacer un poco de trabajo nosotros mismos para que funcione.

Esto se hace a través de una imagen Docker de tmaier de GitHub


# Recuerde tener el PRIVATE_TOKEN generado. Esto solo debe hacerse una vez por proyecto y no por usuario. # Una vez que lo agregue (necesita privilegios de maestro) como variable secreta, debería funcionar.






Solicitud de combinación abierta: # Lo obtuve de aquí: https://gitlab.com/tmaier/gitlab-auto-merge-request/blob/develop/.gitlab-ci.yml imagen: tmaier/gitlab-auto-merge-requeststage: openMrscript:- bash ./gitlab-deploy/auto-merge-request.sh # El nombre del script

Este es el archivo auto-merge-request.sh


#!/usr/bin/env bashset -e




# Obtenido de: # https://about.gitlab.com/2017/09/05/how-to-automatically-create-a-new-mr-on-gitlab-with-gitlab-ci/ # Esto creará automáticamente una solicitud de fusión justo después de que se haya enviado la compilación. # Se agregaron algunos toques de: https://gitlab.com/tmaier/gitlab-auto-merge-request/blob/develop/merge-request.sh





si [-z “$PRIVATE_TOKEN”]; thenecho “PRIVATE_TOKEN no establecido”echo “Configure el token privado de GitLab como PRIVATE_TOKEN”exit 1fi


# Extraiga el host donde se ejecuta el servidor y agregue la URL a las API [[ $CI_PROJECT_URL =~ ^https?://[^/]+ ]] && HOST=”${BASH_REMATCH[0]}/api/ v4/proyectos/”


# Mira cuál es la rama predeterminada TARGET_BRANCH=`curl — silencioso “${HOST}${CI_PROJECT_ID}” — encabezado “PRIVATE-TOKEN:${PRIVATE_TOKEN}” | jq — salida sin procesar '.default_branch'`;










# La descripción de nuestro nuevo MR, queremos eliminar la rama después de que el MR se haya # cerradoBODY=”{\”id\”: ${CI_PROJECT_ID},\”source_branch\”: \”${CI_COMMIT_REF_NAME}\”, \”target_branch\”: \”${TARGET_BRANCH}\”,\”remove_source_branch\”: true,\”title\”: \”WIP: ${CI_COMMIT_REF_NAME}\”,\”assignee_id\”:\”${ ID_USUARIO_GITLAB}\”}”;




# Requerir una lista de todas las solicitudes de combinación y mirar si ya hay # una con la misma fuente branchLISTMR=`curl — silencioso “${HOST}${CI_PROJECT_ID}/merge_requests?state=opened” — encabezado “PRIVATE- TOKEN:${PRIVATE_TOKEN}”`;COUNTBRANCHES=`echo ${LISTMR} | grep -o “\”source_branch\”:\”${CI_COMMIT_REF_NAME}\”” | wc -l`;






# No se encontró MR, vamos a crear uno nuevoif [ ${COUNTBRANCHES} -eq “0” ]; thencurl -X POST “${HOST}${CI_PROJECT_ID}/merge_requests” \— encabezado “PRIVATE-TOKEN:${PRIVATE_TOKEN}” \— encabezado “Content-Type: application/json” \— datos “${BODY}” ;



echo “Abrió una nueva solicitud de combinación: WIP: ${CI_COMMIT_REF_NAME} y se le asignó”;salir;fi

echo “No se abrió ninguna nueva solicitud de fusión”;

Para que esto funcione, necesitamos generar un PRIVATE_TOKEN que es solo un token aleatorio que podemos generar. Para tener un token fuerte y seguro, podemos usar un generador de contraseñas o cualquier otra cosa (¡usted elige!).

Coloque los contenidos dentro de las "Variables secretas" como PRIVATE_TOKEN

Vaya a Su proyecto -> Configuración -> CI/CD -> Variables secretas

#7 Implementar en Producción

Es muy similar al proceso de puesta en escena, pero aquí radica una diferencia. La principal diferencia entre un enfoque de CI (Integración continua) y de Implementación continua (CD) es que este último, cualquier cambio que realice en el código, se envía automáticamente a producción.

En GitLab podemos especificar si lo desplegamos manualmente en producción o no especificando una clave de "cuándo".



Implementar en Production:stage: productionbefore_script:

Genera para conectarse a la unidad de AWS la clave SSH.

 - mkdir -p ~/.ssh - echo -e "$SSH\_PRIVATE\_KEY" > ~/.ssh/id\_rsa

Establece el permiso en 600 para evitar un problema con AWS

que está demasiado desprotegido

 - chmod 600 ~/.ssh/id\_rsa - '\[\[ -f /.dockerenv \]\] && echo -e "Host \*\\n\\tStrictHostKeyChecking no\\n\\n" > ~/.ssh/config'






script:- bash ./gitlab-deploy/.gitlab-deploy.prod.shenvironment:name: production# Expone un botón que, cuando se hace clic, lo lleva a la URL definida: url: http://ec2-13-59-173- 91.us-east-2.compute.amazonaws.com:81

cuando: manuales

Como puede ver al especificar la clave when:manual , le decimos a GitLab que no envíe el código automáticamente a producción y que espere nuestros comandos.

En la página de canalizaciones, hace clic en un botón Reproducir para "Implementar en producción", que es el nombre que especificó en .gitlab-ci.yml

Por último, pero no menos importante, verifique .gitlab-deploy.prod.sh

#!/bin/bash


# Obtener lista de servidores: establecer — f




# Variables del servidor GitLab:# Nota: ¡¡No pueden tener espacios!!string=$DEPLOY_SERVERarray=(${string//,/ })





# Iterar servidores para implementar y extraer la última confirmación # Cuidado con ; https://stackoverflow.com/a/20666248/1057052 para i en “${!array[@]}”; doecho “Implementar proyecto en el servidor ${array[i]}”ssh ubuntu@${array[i]} “cd ./Pardo/vr && git stash && git checkout $CI_BUILD_REF_NAME && git stash && git pull origin master && sudo yarn instalar && sudo npm ejecutar producción”

hecho

Si te fijas, verás que es similar (por no decir idéntico) al de la puesta en escena. Con la excepción de que lo apunto a una ubicación diferente dentro de mi instancia EC2, donde se encuentra el código de producción. Usted es libre de modificar este archivo también.

#8 ¡La canalización de CI/CD ha sido configurada! ¡Hora de empujar!

¡¡Sí!! ¡Por fin es ese momento!

¡Confirme su archivo y envíelo a su instancia de GitLab!

¡Vea cómo sus cambios comienzan a suceder!

¡Eso es todo!

¡GUAU! Gracias por el aventón.