Ruby on Rails es un maravilloso framework para el desarrollo de aplicaciones web. Tiene un montón de herramientas para facilitar la vida del desarrollador, además de una serie de valores los cuales dan peso a su existencia.
Cuando empecé a escribir código en Rails era el año 2012 y lo poquito que escribía era en Rails 3. Hice cosas en Rails 4 para saber las diferencias y tuve la oportunidad de seguir escribiendo en la cuarta versión en un software en el que trabajé por casi 2 años. Más adelante, pude empezar un proyecto con Rails 5 y esa fue la constante por un largo rato.
Lo curioso es que siempre que probaba una versión reciente de Rails coincidía con proyectos nuevos, nunca hice una migración. Hasta que el año pasado (2020), tuve que migrar las tres aplicaciones que escribo en Ruby on Rails. Era una gran oportunidad porque nunca antes había hecho una migración y sobretodo porque era a la versión más reciente de Rails que traía un montón de cambios interesantes aunque no los fuera a usar en el mediano plazo.
Resulta que migrar entre versiones de Rails (al menos de la 5 a la 6) fue bien sencillo. Al inicio, pensé que iba a ser algo atormentador y complicado, que muchas librerías se iban a romper pero no fue así. Al contrario, la migración fue descomplicada aunque sí hubo detalles menores que resolví eventualmente.
A continuación, unas notas sobre grandes rasgos de la migración y algunas cosas que me pasaron después y que pude resolver.
Enlaces de Apoyo
Primero que nada, fue posible lograr la migración y sentir confianza debido a estos enlaces:
- How To Upgrade To Rails 6
- Upgrading Rails apps with rake app:update
- Upgrading Ruby on Rails, Rails Guides
Los Pasos que Seguí
- Asegurarse de incrementar el nivel de coverage de la suite de pruebas
- Estar en la versión de Ruby 2.5.3
- Estar en la versión de Rails 5.2.3
- Cambiar la versión de Rails en el Gemfile a la más reciente
- Correr el comando
bundle update
- Correr el comando
rails app:update
- Usar la opción «d» para ver las diferencias en los archivos
- Usar la opción «m» para hacer una mezcla sin borrar lo existente
- Se puede configurar Sublime Text como herramienta de merge para simplificar esta parte.
- Actualiza las gemas faltantes según lo que se indique en RailsDiff
Eso es todo lo que tuve que hacer, en resumen. Me sirvió desde la primera aplicación que migré hasta la tercera.
Lo importante del nivel de cobertura de las pruebas es darnos la mayor certeza posible al volver a correr las pruebas una vez concluida la migración. No tiene que ser un 100% pero sí un número que de confianza o que las partes más importantes estén testeadas.
El comando rails app:update
es crucial y facilita demasiado la migración en conjunto con la herramienta RailsDiff.
Lo de convertir Sublime Text (o tu editor de código) como herramienta para el merge me pareció de mucha utilidad ya que no soy muy bueno usando Nano ni Vim. Poder ver los diffs en mi editor favorito me ayudó mucho.
Los Errores
No todo puede ser de color de rosas cuando se hacen migraciones entre versiones de software y Rails no es la excepción.
Seguro que hay gente que le fue peor que a mí. En todo caso estas son las cosas que encontré y las formas en que las pude solucionar.
DNS Rebinding Protection
El error dice:
To allow requests to api.lvh.me, add the following to your environment configuration:
config.hosts << "api.lvh.me"
El cual resolví agregando la siguiente configuración en el archivo config/environments/development.rb
:
config.hosts << "api.lvh.me"
Información sobre este error y la razón de ser:
Problemas Creando Imágen Docker con Alpine
Al generar imágenes con Alpine, encontré este error en el paso de rails assets:precompile
:
Step 14/16 : RUN rails assets:precompile
---> Running in 1789ea9e4ee7
/bin/sh: rails: not found
The command '/bin/sh -c rails assets:precompile' returned a non-zero code: 127
Entré en modo depurador y pareció ser un detalle con Nokogiri (cuando no) y algunas librerías faltantes:
/usr/src/app # gem install rails -v 6.0.2
Fetching activesupport-6.0.2.gem
Fetching activejob-6.0.2.gem
Fetching actionpack-6.0.2.gem
Fetching actionview-6.0.2.gem
Fetching activemodel-6.0.2.gem
Fetching activerecord-6.0.2.gem
Fetching actionmailer-6.0.2.gem
Fetching actioncable-6.0.2.gem
Fetching rails-6.0.2.gem
Fetching actionmailbox-6.0.2.gem
Fetching actiontext-6.0.2.gem
Fetching railties-6.0.2.gem
Fetching activestorage-6.0.2.gem
Successfully installed activesupport-6.0.2
Building native extensions. This could take a while...
ERROR: Error installing rails:
ERROR: Failed to build gem native extension.
current directory: /usr/local/bundle/gems/nokogiri-1.10.7/ext/nokogiri
/usr/local/bin/ruby -I /usr/local/lib/ruby/site_ruby/2.5.0 -r ./siteconf20191218-31-djw7o.rb extconf.rb
checking if the C compiler accepts ... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers. Check the mkmf.log file for more details. You may
need configuration options.
Provided configuration options:
--with-opt-dir
--without-opt-dir
--with-opt-include
--without-opt-include=${opt-dir}/include
--with-opt-lib
--without-opt-lib=${opt-dir}/lib
--with-make-prog
--without-make-prog
--srcdir=.
--curdir
--ruby=/usr/local/bin/$(RUBY_BASE_NAME)
--help
--clean
/usr/local/lib/ruby/2.5.0/mkmf.rb:456:in `try_do': The compiler failed to generate an executable file. (RuntimeError)
You have to install development tools first.
from /usr/local/lib/ruby/2.5.0/mkmf.rb:574:in `block in try_compile'
from /usr/local/lib/ruby/2.5.0/mkmf.rb:521:in `with_werror'
from /usr/local/lib/ruby/2.5.0/mkmf.rb:574:in `try_compile'
from extconf.rb:138:in `nokogiri_try_compile'
from extconf.rb:162:in `block in add_cflags'
from /usr/local/lib/ruby/2.5.0/mkmf.rb:632:in `with_cflags'
from extconf.rb:161:in `add_cflags'
from extconf.rb:416:in `<main>'
To see why this extension failed to compile, please check the mkmf.log which can be found here:
/usr/local/bundle/extensions/x86_64-linux/2.5.0/nokogiri-1.10.7/mkmf.log
extconf failed, exit code 1
Gem files will remain installed in /usr/local/bundle/gems/nokogiri-1.10.7 for inspection.
Results logged to /usr/local/bundle/extensions/x86_64-linux/2.5.0/nokogiri-1.10.7/gem_make.out
Al ingresar al contenedor e instalar las librerías faltantes libxml2-dev
y libxslt-dev
, este funcionaría normalmente.
Entonces, para resolverlo seguí los pasos mencionados en la documentación de Nokogiri para instalar los paquetes build-base
, libxml2-dev
y libxslt-dev
.
Tales paquetes son eliminados por Alpine para poder hacer que la imagen sea más liviana.
/usr/src/app # apk add --no-cache build-base libxml2-dev libxslt-dev
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz
(1/15) Installing binutils (2.31.1-r2)
(2/15) Installing libmagic (5.36-r1)
(3/15) Installing file (5.36-r1)
(4/15) Installing isl (0.18-r0)
(5/15) Installing libgomp (8.3.0-r0)
(6/15) Installing libatomic (8.3.0-r0)
(7/15) Installing mpfr3 (3.1.5-r1)
(8/15) Installing mpc1 (1.0.3-r1)
(9/15) Installing gcc (8.3.0-r0)
(10/15) Installing musl-dev (1.1.20-r5)
(11/15) Installing libc-dev (0.7.1-r0)
(12/15) Installing g++ (8.3.0-r0)
(13/15) Installing make (4.2.1-r2)
(14/15) Installing fortify-headers (1.0-r0)
(15/15) Installing build-base (0.5-r1)
Executing busybox-1.29.3-r10.trigger
OK: 531 MiB in 176 packages
Y luego instalé Nokogiri
/usr/src/app # gem install nokogiri
Building native extensions. This could take a while...
Successfully installed nokogiri-1.10.7
1 gem installed
Finalmente, bundle install
:
/usr/src/app # bundle install
(...)
Using rails 6.0.2
Using recaptcha 5.2.1
Using redis 4.0.3
Using redis-namespace 1.7.0
Using tilt 2.0.10
Using sinatra 2.0.7
Using vegas 0.1.11
Using resque 1.27.4
Using sassc 2.2.1
Using sassc-rails 2.1.2
Using sass-rails 6.0.0
Using sentry-raven 2.13.0
Using uglifier 4.2.0
Using wicked_pdf 1.4.0
Bundle complete! 42 Gemfile dependencies, 95 gems now installed.
Gems in the groups development and test were not installed.
Bundled gems are installed into `/usr/local/bundle`
Sin embargo, el comando gem list rails
no listaba esta gema y no lo haría hasta que se instalará directamente. Así que para resolverlo, había que instalar todo ello en el proceso de construcción de la imagen Docker:
RUN set -ex \
&& apk add --upgrade --no-cache --virtual .app-builddeps \
$BUILD_PACKAGES \
$DEV_PACKAGES \
&& apk add --no-cache --virtual .app-rundeps $RUNTIME_PACKAGES \
&& update-ca-certificates \
&& gem install nokogiri -- --use-system-libraries \
&& gem install rails -v 6.0.2 \
&& gem install bundler && bundle config disable_multisource \
&& bundle install --jobs 2 --retry 5 --without $BUNDLE_WITHOUT \
&& apk del .app-builddeps
Bloqueo entre dependencias de ActiveModelSerializers
Este error:
Bundler could not find compatible versions for gem "activemodel":
In Gemfile:
active_model_serializers (= 0.10.10) was resolved to 0.10.10, which depends on
activemodel (>= 4.1, < 6.1)
rails (= 6.0.0) was resolved to 6.0.0, which depends on
activemodel (= 6.0.0)
Y este otro:
Bundler could not find compatible versions for gem "activerecord":
In Gemfile:
annotate (~> 3) was resolved to 3.0.3, which depends on
activerecord (>= 3.2, < 7.0)
rails (= 6.0.0) was resolved to 6.0.0, which depends on
activerecord (= 6.0.0)
Si bien traté de solucionarlo haciendo un fork de la gema y luego incrementando la versión de Rails, lo que terminó por servirme fue comentar las gemas problemáticas, correr el comando bundle update rails
, «descomentar» tales gemas y correr el comando bundle install
para volver a instalarlas.
Error de ActiveRecord::NoEnvironmentInSchemaError
Este lo encontré luego de hacer una migracion de Rails 6.0.0.rc2 a Rails 6.0.2 cuando ejecutaba comandos como tareas rake o el comando rspec:
rails aborted!
ActiveRecord::NoEnvironmentInSchemaError:
Environment data not found in the schema. To resolve this issue, run:
rails db:environment:set RAILS_ENV=test
/Users/fquintero/.rvm/gems/ruby-2.5.3/gems/activerecord-6.0.2/lib/active_record/migration.rb:1151:in `last_stored_environment'
/Users/fquintero/.rvm/gems/ruby-2.5.3/gems/activerecord-6.0.2/lib/active_record/tasks/database_tasks.rb:60:in `check_protected_environments!'
/Users/fquintero/.rvm/gems/ruby-2.5.3/gems/activerecord-6.0.2/lib/active_record/railties/databases.rake:15:in `block (2 levels) in <top (required)>'
/Users/fquintero/.rvm/gems/ruby-2.5.3/gems/activerecord-6.0.2/lib/active_record/railties/databases.rake:494:in `block (3 levels) in <top (required)>'
/Users/fquintero/.rvm/gems/ruby-2.5.3/gems/railties-6.0.2/lib/rails/commands/rake/rake_command.rb:23:in `block in perform'
/Users/fquintero/.rvm/gems/ruby-2.5.3/gems/railties-6.0.2/lib/rails/commands/rake/rake_command.rb:20:in `perform'
/Users/fquintero/.rvm/gems/ruby-2.5.3/gems/railties-6.0.2/lib/rails/command.rb:48:in `invoke'
/Users/fquintero/.rvm/gems/ruby-2.5.3/gems/railties-6.0.2/lib/rails/commands.rb:18:in `<top (required)>'
bin/rails:4:in `require'
bin/rails:4:in `<main>'
Tasks: TOP => db:test:load => db:test:purge => db:check_protected_environments
(See full trace by running task with --trace)
Al seguir la sugerencia que aparece en el error se solucionaba de manera temporal. También encontré esta alternativa pero también era de forma temporal.
—
Y así, fue como logré migrar tres aplicaciones Ruby on Rails de la versión 5 a la 6 sin mayores complicaciones y con errores que pude resolver con poco sufrimiento y una que otra búsqueda en DuckDuckGo.