Review apps en Heroku: Scripts personalizados – parte 3

Como alimentar la base de datos o crear dominios personalizados automáticamente cuando se crea la aplicación.

Jorge Rodriguez
Get on Board Dev
3 min readMar 12, 2020

--

Unsplash

En la segunda parte de esta serie mostramos cómo configuramos una review app en Get on Board.

# Ejecuta tareas tipo rake al crear y destruir la review app
“scripts”: {
“postdeploy”: “rails getonbrd:heroku:review_app_setup getonbrd:db:create_sample_data”,
“pr-predestroy”: “rails getonbrd:heroku:review_app_predestroy”
}

Lo que queda es revisar en detalle cómo es posible personalizar una aplicación con código que se ejecuta una vez que esta es creada — postdeploy — o después de ser destruida — pr-predestroy.

Data seeds

Es conveniente alimentar una review app con un set mínimo de datos de pruebas. Rails provee db:seeds, sin embargo, cuando tienes mucha data organizada en múltiples modelos dependientes entre sí, se hace incómodo tenerlo todo en un mismo archivo.

# companies.yml
DEFAULTS: &DEFAULTS
name: $LABEL
email: dev+company_$LABEL@getonbrd.com
web: https://www.$LABEL.com
getonbrd:
<<: *DEFAULTS
name: Get on Board
description: Awesome jobs for awesome people.
country: CL
...

A nosotros se nos ocurrió hacer uso de otro mecanismo del framework, que usualmente se usa para alimentar el ambiente de testing con datos de muestra. Entre las ventajas de usar fixtures está la de declarar la data en archivos YAML ☝️, formato amigable que resulta conveniente cuando — como es nuestro caso — colaboradores que no necesariamente entienden código pueden agregar nuevos registros.

Para cargar la muestra en la review app creamos una tarea que importa los fixtures y ejecuta un par de métodos de clean-up y sanity check de la data:

# rake file
task create_sample_data: :environment do
CreateSampleData.run(false)
end
# CreateSampleData.rb
class CreateSampleData
def self.run(perform_reset_db =false)
new.run(perform_reset_db)
end
def run(perform_reset_db = false)
reset_db if perform_reset_db
without_contraints { load_fixtures }
fill_long_texts
update_tenant_hostnames
...
sanity_check
end

...

def without_contraints
drop_constraints
yield
ensure
create_constraints
end
def load_fixtures
perform 'Loading fixtures' do
Rake::Task['db:fixtures:load'].invoke
end
end

...
end

Protip: No es posible cargar fixtures en una base de datos diferente a testing porque se violan constraints tales como primaries o secondaries keys. Por eso ejecutamos la carga dentro de un bloque que, usando funcionalidades nativas de PostgreSQL, eliminan los constraints — drop_constraints — por un rato, recreándolos — create_constraints— una vez que la data es cargada.

Dominios personalizados

Heroku provee un dominio en la forma [nombre-de-la-app].herokuapp.com cuando una aplicación es creada y no todas necesitan cambiarlo para funcionar. Get on Board, por su naturaleza multi-tenant necesita configurar dominios personalizados para los países donde está presente. La forma de hacer esto es crear registros CNAME en un DNS externo que apunten a la review app. Heroku documenta bien el proceso manual.

DNSimple es un proveedor de dominios que provee una API — y una gema ruby — con la que puedes interactuar para modificar las tablas DNS de tus dominios por cinco dólares al mes.

Platform API — y su gema en ruby — es la forma de interactuar con Heroku desde el código de tu aplicación.

Uniendo a ambos es posible crear dominios personalizados una vez que la aplicación ya existe:

Script para crear custom domains cuando se crea la review app.

Finalmente, una vez que se aprueban los cambios introducidos en la review app y el nuevo code se va a staging o producción, la app es destruida. En este punto podemos ejecutar un script para que haga housekeeping, como borrar las entradas CNAME creadas anteriormente en DNSimple:

task :review_app_predestroy do
require “dnsimple”
heroku_app_name = ENV[“HEROKU_APP_NAME”]

# Wipe the DNS records
dnsimple_account_id = ENV[“DNSIMPLE_DEV_ACCOUNT_ID”]
dnsimple_client = Dnsimple::Client.new(
access_token: ENV[‘DNSIMPLE_ACCESS_TOKEN’]
)
resp = dnsimple_client.zones.zone_records(
dnsimple_account_id,
GOB_DEV_DOMAIN,
{ filter: { name_like: “#{heroku_app_name}” } }
)
resp.data.each do |zone|
dnsimple_client.zones.delete_zone_record(
dnsimple_account_id,
GOB_DEV_DOMAIN,
zone.id
)
end
end

Como siempre, si te surgieron dudas o quieres compartir tips relacionados con este post puedes dejarnos un comentario o escribirnos directamente a team at getonbrd.com 🤗

--

--

Jorge Rodriguez
Get on Board Dev

1. Software Engineer @fleetio.com 2. Co-Founder @getonbrd (500 SF)