Github Actions + Laravel

Hace un tiempo les conté por acá cómo configurar Travis para poder crear un ambiente de integración continua en un proyecto de Laravel.

Y como saben también, hace un tiempo estuve jugando con Github Actions para correr los tests de un proyecto de Rails.

En este post les voy a contar como hacer para configurar Github Actions para ejecutar los tests de Laravel.

El Proyecto

El año pasado en Programación Web II – TUPAR, la materia en la que soy docente, hicimos un proyecto de ejemplo en Laravel, al cual le configuramos Github Actions, para generar un ambiente de Continuous Integration (CI).

El proyecto es muy sencillo, una To Do List, que tiene usuarios comunes y usuarios que son manager. Está todo en este repo de Github.

En cuanto a los tests, tiene bastantes Unit tests y Feature Tests. Tambien agregamos un test de Dusk (browser test) muy sencillo, solo entra y mira el titulo de la página. Este último es solo a efectos de poder ejecutar este tipo de test “visuales” en un ambiente de CI.

¿Qué vamos a configurar?

En principio queremos que corran los unit y feature tests. Luego vamos a crear otro workflow para que corran los tests de Dusk y por último crear uno que nos haga un análisis estático de código.

A su vez, vamos a configurar Codecov, para poder ver visualmente el coverage que tenemos.

Unit Tests y Feature Tests

Si miramos un poco el proyecto, vamos a ver que estamos usando docker-compose para tener varios containers:

  • App: Es donde tenemos PHP
  • Web: El servidor web Nginx
  • Database: La base de datos Postgres
  • Adminer: App para poder acceder a la base.
  • Selenium: Donde vamos a tener Chrome para ejecutar los Dusk Tests

Mirando esto lo primero que vamos a necesitar para correr Unit y Feature tests es una base de datos. Por lo tanto en nuestra acción vamos a necesitar un servicio de Postgres corriendo.

Configurar Postgres

Cuando agregamos el servicio de Postgres, tenemos que configurar el usuario, la password y el nombre de la base de datos. También mapeamos el puerto de Postgres al mismo puerto de la máquina host. De esta forma, donde en el container que genera Github, vamos a poder usar la base de datos.

Un dato importante acá es la variable de entorno DB_HOST. Usen 127.0.0.1, en lugar de localhost. (Alguna vez esto me trajo problemas)

Con respecto al servicio de Postgres se agrega una línea más, que sirve para saber si el servicio está listo para atender requests. No es completamente necesario, pero puede arreglarte algún que otro lío.

Steps

Luego, lo que tenemos que agregar son los steps que vamos a necesitar.

  1. Hacer checkout del codigo, este paso nos va a traer el código al container donde se van a ejecutar los tests.
  2. Crear el .env de Laravel para que la solución funcione. Aca es importante que tengamos un .env.example en nuestro proyecto con todo pre-configurado. Esto nos sirve tanto para usarlo en Github Actions o también para cualquier miembro de nuestro equipo pueda crearse un .env muy rapido y facil. Hay que recordar siempre de hacer los ajustes en ese archivo además de en nuestro .env, ya que este último archivo no se commitea en el repo.
  3. Instalar las dependencias usando composer
  4. Generar una key para que nuestra app funcione. Esta key es la que se usa para todo lo que tenga que ver con encripción en nuestra app.
  5. Actualizamos permisos de carpetas donde van a crearse algunos archivos, para que cualquier usuario pueda escribir.
  6. Ejecutamos los tests
  7. Subimos los resultados a Codecov.

name: Laravel
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
laravel-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:9.6
env:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: thisisasecretpassword
POSTGRES_DB: tasks_test
ports:
5432:5432
options: –health-cmd pg_isready –health-interval 10s –health-timeout 5s –health-retries 5
env:
DB_USERNAME: myuser
DB_PASSWORD: thisisasecretpassword
DB_HOST: 127.0.0.1
steps:
uses: actions/checkout@v2
name: Copy .env
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
name: Install Dependencies
run: composer install -q –no-ansi –no-interaction –no-scripts –no-progress –prefer-dist
name: Generate key
run: php artisan key:generate
name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
name: Execute tests (Unit and Feature tests) via PHPUnit
run: vendor/bin/phpunit –coverage-clover=coverage.xml
uses: codecov/codecov-action@v1
view raw laravel.yml hosted with ❤ by GitHub

Dusk Tests

GitHub - JoseVte/laravel-dusk-5.1

Para los Dusk Tests, qué son los que corren en el browser, vamos a tener que agregar algunas partes más.

En primer lugar, tenemos que agregar la variable de entorno APP_URL, para que cuando ejecutemos los tests, Dusk sepa dónde tiene que ir a abrir la app en el browser.

Steps

La primer diferencia con el workflow de la sección anterior es que como tenemos una configuración para ejecutar Dusk localmente, lo que tenemos que hacer es borrarla para que no interfiera con la configuración en Github Actions.

Luego los steps son prácticamente los mismos, se suman estos:

  • Ejecutar las migraciones, esto nos va a permitir que nuestra app funcione correctamente.
  • Actualizar el Plugin de Chrome, con este paso actualizamos el binario que vamos a usar de Chrome.
  • Le damos más permisos a la carpeta de binarios de Dusk para que se puedan ejecutar.
  • Levantamos el server de artisan para poder acceder a nuestra app. Redirigimos toda la salida a /dev/null para que no interfiera con el output de nuestros tests. Es muy importante el & del final, para poder ejecutar en paralelo los siguientes steps.
  • Ejecutamos Chrome, también agregamos el & al final.
  • Ejecutamos los tests de Dusk
name: Dusk Tests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
dusk-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:9.6
env:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: thisisasecretpassword
POSTGRES_DB: laravel
ports:
5432:5432
options: –health-cmd pg_isready –health-interval 10s –health-timeout 5s –health-retries 5
env:
DB_USERNAME: myuser
DB_PASSWORD: thisisasecretpassword
APP_URL: http://127.0.0.1:8000
steps:
uses: actions/checkout@v2
name: Copy .env
run: php -r "copy('.env.example', '.env');"
name: Remove .env.dusk.local
run: rm .env.dusk.local
name: Install Dependencies
run: composer install -q –no-ansi –no-interaction –no-scripts –no-progress –prefer-dist
name: Generate key
run: php artisan key:generate
name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
name: Run Migrations
run: php artisan migrate
name: Upgrade Chrome Driver
run: php artisan dusk:chrome-driver `/opt/google/chrome/chrome –version | cut -d " " -f3 | cut -d "." -f1`
name: Change permissions to dusk
run: chmod -R 0755 vendor/laravel/dusk/bin/
name: Run Laravel Server
run: php artisan serve > /dev/null 2>&1 &
name: Start Chrome Driver
run: ./vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &
name: Execute dusk tests
run: php artisan dusk
view raw dusk.yml hosted with ❤ by GitHub

Análisis estático de código (Phan)

Por último, podemos crear otro workflow para chequear el estilo de código que tenemos.

Este es mucho más sencillo, porque al ser un análisis estático de código, no necesitamos tener la app corriendo. Solo necesitamos ejecutar Phan

name: Static Code Analysis
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
code-analysis:
runs-on: ubuntu-latest
steps:
uses: actions/checkout@v2
name: Install Dependencies
run: composer install -q –no-ansi –no-interaction –no-scripts –no-progress –prefer-dist
name: Run Phan
run: vendor/bin/phan –no-progress-bar –allow-polyfill-parser –output-mode checkstyle | vendor/bin/cs2pr –graceful-warnings –colorize | true
view raw static.yml hosted with ❤ by GitHub

Espero que les sirva y puedan usarlo en sus proyectos Laravel.

Github Actions + Rails

Integración y Distribución Continua (CI/CD) con GitHub Actions - Thinking  In Swift

En este post quería mostrarles un poco de la potencia y lo fácil que es usar Github Actions, pero primero…

¿Qué es Github Actions?

Si leemos lo que dice la web nos encontramos con esto:

Automatiza, personaliza y ejecuta tus flujos de trabajo de desarrollo de software directamente en tu repositorio con GitHub Actions. Puedes descubrir, crear y compartir acciones para realizar cualquier trabajo que quieras, incluido CI/CD, y combinar acciones en un flujo de trabajo completamente personalizado.

Pagina de Github Actions

En definitiva nos permite automatizar cosas en nuestro Repo. No solo continuous integration, sino manejo de issues en el repo, darle la bienvenida a los nuevos usuarios, y muchísimas cosas más.

Hubo un hackaton creado en Dev.to para que los devs puedan crear acciones en diferentes categorías. Algunas de las cosas que surgieron fueron muy locas.

Se pueden hacer mil cosas, desde jugar a un juego a mandar un mensaje por telegram. Nosotros lo vamos a usar para tener un ambiente de CI en nuestra aplicación Rails.

¿Qué vamos a hacer?

Vamos a partir de un proyecto de Rails que hice hace algún tiempo que se llama Beer API.

Esta aplicación es una Api muy sencilla que hice para hacer algunas pruebas de diferentes frontends, con diferentes tecnologías, usando la misma API.

Este proyecto tiene corriendo algunos tests de Rspec.

Workflow en Github Actions

Lo primero que tenemos que hacer en nuestro proyecto de Github es hacer click en Actions.

Ahora buscamos en la sección Continuous integration workflows la acción que más nos convenga. En este caso Ruby y hacemos click en Set up this workflow.

Esto nos abre un editor con un archivo pre-creado.

Modifiquemos el workflow

name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
rspec:
name: Rspec
runs-on: ubuntu-latest
services:
postgres:
image: postgres:11
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
5432:5432
options: –health-cmd pg_isready –health-interval 10s –health-timeout 5s –health-retries 5
env:
POSTGRES_HOST: 127.0.0.1
steps:
uses: actions/checkout@v2
uses: actions/setup-ruby@v1
with:
ruby-version: 2.7.2
name: Install Dependencies
run: |
gem install bundler
bundle install
name: Run Migrations
run: bundle exec rails db:create db:migrate RAILS_ENV=test
name: Run Rspec
run: bundle exec rspec
view raw main.yml hosted with ❤ by GitHub

Como yo no necesitaba la estrategia de Matrix, para probar esta api en múltiples versiones de Ruby, borre todo lo relacionado a eso y solo deje una Ruby 2.7.

Lo primero que hice fue cambiar el name por CI (acronimo para Continuous Integration).

Luego en la sección de jobs cree uno que se llama rspec, que tiene el servicio de postgres. Esto lo que va a hacer es levantar un container con Postgres, para que desde el container donde se ejecutan los tests pueda usarlo y ejecutarlos usando una base de datos.

Agregué una sección de options, esto sirve para que los tests ejecuten recién cuando el servicio de la base de datos esté funcionando y no nos fallen los tests porque no se puede conectar. Esto es una configuración que es necesaria para evitar fallas del CI aleatorias.

Luego en la sección env, configuré el host donde va a correr postgres, en este caso 127.0.0.1. En esto tuve algún problema, si no lo ponia o si ponia localhost, por eso les recomiendo agregarlo.

Por último la sección de steps, los primeros son los que vienen por default (el de setup de ruby es un poco distinto porque no uso la matrix) y después agregue:

  • Install Dependencies: Ejecuto bundle install para traer todas las gemas que necesito.
  • Run Migrations: Creo la base de datos y ejecuto las migraciones
  • Run rspec: Ejecuto los tests.

Más Jobs

En este mismo workflow yo podría poner múltiples jobs, en el caso de Beer API, agregué un job que corre Rubocop, para el estilo del código.

Para eso en la sección de jobs agregue uno nuevo que se llama rubocop con la siguiente configuración.

jobs:
rubocop:
name: Rubocop
runs-on: ubuntu-18.04
steps:
uses: actions/checkout@v1
uses: actions/setup-ruby@v1
with:
ruby-version: 2.7.2
name: Install Rubocop
run: |
gem install rubocop
gem install rubocop-rails
gem install rubocop-rspec
name: Run Rubocop
run: rubocop
view raw main.yml hosted with ❤ by GitHub

Para verlo funcionando solo nos queda hacer commit de nuestro archivo de configuración y listo.

Ahora cada vez que se abra un Pull Request, o haga un Merge a main, se van a ejecutar los tests y además el chequeo estático de la sintaxis.

Espero que les sirva para tener mejores prácticas en sus proyectos.

Nos leemos.