GitHub Actions permite crear flujos de trabajo (workflows) que se pueden utilizar para compilar, probar y desplegar código, dando la posibilidad de crear flujos de integración y despliegue continuo dentro del propio repositorio de git.
Los flujos de trabajo tienen que contener al menos un job. Estos incluyen una serie de pasos que ejecutan tareas individuales que pueden ser acciones o comandos. Un flujo de trabajo puede comenzar por distintos eventos que suceden dentro de GitHub, como un push al repositorio o un pull request.
En este tutorial vamos a crear un workflow para ejecutar las pruebas unitarias de un proyecto Laravel y realizar el despliegue por ssh a un servidor.
Puedes ver el workflow funcionando en este repositorio de demo.
Creando el archivo del workflow
Los archivos de workflow deben ser almacenados en la carpeta .github/workflows
en formato yaml en la raíz del repositorio.
Crea el archivo .github/workflows/ci-cd.yml
con el siguiente contenido:
name: ci/cd workflow on: push: branches: - master - features/* pull_request: jobs: tests: runs-on: ubuntu-latest strategy: matrix: php: [7.3, 7.4] name: Test - PHP ${{ matrix.php }} steps: - name: Checkout code uses: actions/checkout@v2 - name: Cache PHP dependencies uses: actions/cache@v1 with: path: vendor key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, gd coverage: none - name: Copy ENV Laravel Configuration for CI run: php -r "file_exists('.env') || copy('.env.example', '.env');" - name: Install dependencies run: composer install --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist - name: Generate key run: php artisan key:generate - name: Execute tests run: vendor/bin/phpunit --verbose deploy: runs-on: ubuntu-latest needs: tests if: github.ref == 'refs/heads/master' steps: - name: Checkout code uses: actions/checkout@v2 - name: Cache PHP dependencies uses: actions/cache@v1 with: path: vendor key: dependencies-php-composer-${{ hashFiles('**/composer.lock') }} - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: 7.3 extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, gd coverage: none - name: Install dependencies run: composer install --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist - name: Copy dotenv file run: php -r "file_put_contents(__DIR__ . '/.env', '${{ secrets.DOT_ENV }}');" - name: Deploy uses: AEnterprise/[email protected] env: DEPLOY_KEY: ${{ secrets.KEY }} ARGS: '-avzh --exclude=".*"' SERVER_PORT: ${{ secrets.PORT }} FOLDER: "./" SERVER_IP: ${{ secrets.HOST }} USERNAME: ${{ secrets.USERNAME }} SERVER_DESTINATION: "/var/www/html"
Configurando los secretos
Con Github Actions podemos manejar secretos, que son información sensible (como contraseñas, api keys, etc) que no debería estar versionada en nuestro repositorio.
Debemos seguir los siguientes pasos para configurar los secretos que necesitaremos en nuestro workflow:
- Dentro de nuestro repositorio hacer clic en Settings y luego en Secrets.
- Hacer clic en Add a new secret para añadir un nuevo secreto al repositorio.
- En Name escribir
DOT_ENV
y en Value copiar el contenido del archivo.env
que queremos desplegar en nuestro servidor. - Hacer clic en Add secret para guardar el secreto.
- Repetir los pasos 2 a 4 para crear secretos que se llamen
HOST
,PORT
,USERNAME
yKEY
. Deben tener los valores para poder conectarse por ssh al servidor al que queramos desplegar.
Entendiendo el Workflow
Vamos a analizar cada sección del workflow para entender su funcionamiento.
Definir eventos
on: push: branches: - master - features/* pull_request:
En esta sección definimos que el workflow se ejecute cuando se haga un push en la rama master o cualquier rama que empiece con feature; o cuando se realice un pull request.
Definir el Job para las pruebas
jobs: tests: runs-on: ubuntu-latest strategy: matrix: php: [7.3, 7.4] name: Test - PHP ${{ matrix.php }}
Estamos definiendo el job «tests» que tendrá los pasos para ejecutar nuestras pruebas unitarias. Además, le indicamos que se ejecute en una máquina con la última versión de Ubuntu (ésta será provisionada de forma automática por GitHub).
En la sección de strategy estamos utilizando la función de matrix. Esto nos va a permitir ejecutar el job pasando como variable dos versiones de PHP.
El valor de name se utilizará para poder identificarlo dentro de GitHub.
Definir Steps para el Job de pruebas
Los steps son los pasos que va a ejecutar nuestro job.
Descargar el código
- name: Checkout code uses: actions/checkout@v2
Esta acción descarga el código del repositorio dentro de la máquina virtual.
Realizar un caché de dependencias
- name: Cache PHP dependencies uses: actions/cache@v1 with: path: vendor key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }}
Esta acción nos permite hacer un cache de las librerías de Composer que se mantendrá entre cada ejecución del workflow. Esto nos ayuda a acelerar el workflow en caso que no se haya modificado el archivo composer.json.
Instalar PHP
- name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, gd coverage: none
Vamos a utilizar la acción shivammathur/setup-php para instalar php y las dependencias que necesitamos. Esta instalación de PHP incluye Composer.
Estamos utilizando la sintaxis ${{ matrix.php }}
para inyectar el valor del matrix que definimos al inicio del job. Este valor será inyectado automáticamente por GitHub Actions.
Creando el archivo .env para las pruebas
- name: Copy ENV Laravel Configuration for CI run: php -r "file_exists('.env') || copy('.env.example', '.env');"
Estamos ejecutando un comando de php para renombrar el archivo .env.example a .env que es necesario para poder ejecutar las pruebas. También podrías crear un archivo .env.ci con las configuraciones que necesite tu proyecto para realizar el flujo de Integración Continua.
Instalar dependencias de Composer
- name: Install dependencies run: composer install --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist
Realizamos un composer install
para instalar las dependencias de nuestro proyecto. Los parámetros extra nos permiten realizar la instalación sin que dependa de la interacción con una persona.
Generar key
- name: Generate key run: php artisan key:generate
El archivo .env.example no cuenta con un API_KEY
, por lo que tenemos que ejecutar el comando de Artisan correspondiente para generarla.
Ejecutar pruebas
- name: Execute tests run: vendor/bin/phpunit --verbose
Utilizamos el comando de phpunit para ejecutar las pruebas unitarias.
Definir el Job para el despliegue
deploy: runs-on: ubuntu-latest needs: tests if: github.ref == 'refs/heads/master'
En el job de despliegue estamos utilizando dos parámetros nuevos:
- El parámetro
needs
indica que este job tiene que esperar que el job de tests se complete antes de ejecutarse. De esta manera nos aseguramos de no desplegar nuestro código si las pruebas unitarias están fallando. - El parámetro
if
nos permite decir que este job solo debe ser ejecutado si estamos en la rama master.
Definiendo los Steps para el Job de despliegue
Los primeros cuatro pasos para configurar el ambiente son los mismos que en el job de tests.
Crear el archivo .env para producción
- name: Create dotenv file run: php -r "file_put_contents(__DIR__ . '/.env', '${{ secrets.DOT_ENV }}');"
Creamos el archivo .env utilizando el contenido que guardamos en el secreto con el nombre DOT_ENV
.
Desplegar el proyecto
- name: Deploy uses: AEnterprise/[email protected] env: DEPLOY_KEY: ${{ secrets.KEY }} ARGS: '-avzh --exclude=".*"' SERVER_PORT: ${{ secrets.PORT }} FOLDER: "./" SERVER_IP: ${{ secrets.HOST }} USERNAME: ${{ secrets.USERNAME }} SERVER_DESTINATION: "/var/www/html"
Estamos desplegando a una instancia Linux utilizando una acción de Github que utiliza rsync. En los parámetros de ambiente utilizamos la información guardada en los secretos para hacer la conexión por ssh utilizando una llave .pem. Especificamos que queremos copiar todo el contenido utilizando los caracteres ./
en el parámetro FOLDER.
El Workflow en acción
Cuando realizamos un nuevo push a la rama master con el archivo, vamos a poder ver cómo se ejecuta el workflow dentro de la pestaña Actions de nuestro repositorio.
Al hacer clic en cualquier workflow vamos a ver en la barra lateral todos los jobs que se han ejecutado. Al hacer clic en un job en la parte derecha aparecerán todos los pasos con el estado de su ejecución.
Agregando un Badge para visualizar el estado del workflow en el Readme.md
Una funcionalidad común es mostrar el resultado del flujo de CI/CD mediante badges dentro del Readme.md del proyecto:
Para obtener la imagen con el estado del workflow, se puede utilizar una url con la siguiente estructura:
https://github.com/<OWNER>/<REPOSITORY>/workflows/<WORKFLOW_NAME>/badge.svg
Para agregarla dentro del README.md tenemos que utilizar la sintaxis de markdown para agregar la imagen:
![](https://github.com/actions/hello-world/workflows/Greet%20Everyone/badge.svg)
Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.