No hay ninguna duda que la integración continua tiene enormes ventajas a la hora de simplificar la forma de enviar nuestros proyectos a los entornos en la nube. Por eso, ya antes he configurado este tipo de servidores en Bitbucket, Jenkins e incluso Circle CI.
Ahora, me tocó hacerlo en GitHub Actions y quiero compartir cómo lo logré y algunos aprendizajes.
Voy a partir del resultado final e iré explicando los bloques importantes.
Este es el código para el archivo workflow.yml
ubicado en la carpeta .github/workflows
.
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
RAILS_ENV: test
name: Rails tests
on: [push, pull_request]
jobs:
rspec-test:
name: RSpec
runs-on: ubuntu-18.04
services:
postgres:
image: postgres:latest
ports:
- 5432:5432
options: >-
--mount type=tmpfs,destination=/var/lib/postgresql/data
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: nsac_testing
steps:
- uses: actions/checkout@v1
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7.1
bundler-cache: true
- name: Install postgres client
run: sudo apt-get install libpq-dev
- name: Prepare database
run: bundler exec rails db:prepare RAILS_ENV=test
- name: Run tests
run: bundler exec rake
Los detalles.
Este bloque al inicio existe por dos razones.
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
RAILS_ENV: test
La primera razón es para ayudarnos a reutilizar tales valores en los diferentes bloques subsiguientes de configuración de servicios o pasos a ejecutar.
La segunda razón es que permite que tales variables estén disponibles para la aplicación y pueden ser leídas mediante la instrucción ENV
.
Más adelante cuento porque es importante esta segunda razón.
Claves sobre servidores de Integración Continua

Una forma sencilla de verlos es que ellos ejecutan una serie de pasos predefinidos, si algún paso falla, toda la operación es terminada.
Normalmente, el orden de las instrucciones es algo como:
- Nombra el flujo
- Define las condiciones para ejecutarlo (
on: [push, pull_request]
) - Define las tareas a ejecutar (
jobs
) - Define cada tarea (
rspec-test
) - Describe dependencias de la tarea (
runs-on
,services
) - Define cada cosa que hará la tarea (
steps
) - Define cada cosa de manera individual (
uses
,name, run
)
Con eso en mente, quiero describir brevemente alguna de las partes. Para más info puedes leer la ayuda de GitHub sobre las Actions.
Servicio PostgreSQL
Para este bloque, me permito mencionar algunas cosas:
services:
postgres:
image: postgres:latest
ports:
- 5432:5432
options: >-
--mount type=tmpfs,destination=/var/lib/postgresql/data
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: nsac_testing
Si no se define el bloque postgres
las pruebas nunca se van a ejecutar porque se necesita una base de datos para eso.
Lo que está en options
son configuraciones útiles para verificar que el servicio esté activo y listo para ejecutar.
Y hay que describir un bloque env
para el servicio, a pesar de que en la parte superior hayamos definido otro bloque similar. Esto es porque lo que se describe para este servicio se pasa como argumentos al comando de Docker que crea el contenedor y son valores importantes y requeridos.
NOTA: el valor para POSTGRES_PASSWORD
no puede estar vacío o habrá un error sobre el servicio «unhealthy».
Failed to initialize, postgres service is unhealthy
También es vital definir los valores para usuario y clave de base de datos en el archivo database.yml
del proyecto Rails:
default: &default
host: localhost
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
development:
<<: *default
database: nsac_development
test:
<<: *default
database: nsac_testing
username: <%= ENV.fetch("POSTGRES_USER", "") %>
password: <%= ENV.fetch("POSTGRES_PASSWORD", "") %>
production:
<<: *default
url: <%= ENV['DATABASE_URL'] %>
Sin tales valores configurados, va a fallar todo el proceso.
NOTA: también hay que decir el valor para host
. Sino, vamos a ver el error:
could not connect to server: No such file or directory
Sobre Ruby y Gemas
Algo que me gustó de GitHub Actions es lo simple que esta parte de configurar Ruby.
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7.1
bundler-cache: true
Con la instrucción bundler-cache: true
los pasos de instalación de gemas y de paso cachearlas para agilizar futuras ejecuciones quedan cubiertos.
NOTA: acá usé ruby/setup-ruby
porque es la forma más actualizada.
Segunda Ventaja de definir un env superior
La segunda ventaja está dada en este bloque:
- name: Prepare database
run: bundler exec rails db:prepare RAILS_ENV=test
Si bien estoy repitiendo RAILS_ENV=test
, si no hubiera definido los variables de entorno en la parte superior, el error siguiente aparecería (me pasó como 8 veces mientras configuraba esto xD)
fe_sendauth: no password supplied
Couldn't create 'nsac_testing' database. Please check your configuration.
rails aborted!
PG::ConnectionBad: fe_sendauth: no password supplied
Esto es porque si bien en el archivo database.yml
estoy leyendo las variables de entorno, al crear el contenedor, el flujo no está proveyendo tales variables para ser leídas en el archivo y termina arrojando valores indebidos.
Conclusión
Definitivamente, GitHub Actions es una buena forma de tener un servidor de Integración Continua sin crear cuenta en otro servicio ni tener que aprender una sintaxis YAML externa (aunque sí hay que leer la documentación o buscar un tutorial).
Hay una buena oferta de minutos en la capa gratuita (2000 vs los 50 que da Bitbucket Pipelines).
Al comienzo me ayude de un tutorial y de ahí fui buscando más información a medida que encontraba errores y en el 14avo intento lo conseguí. Decidí compartir acá por si alguien empieza a usar Actions para un proyecto Rails, tal vez encuentre este y tenga un mejor punto de partida que el mío.
Gracias, declarar POSTGRES_USER y POSTGRES_PASSWORD al principio era el detalle que me faltaba.