ruby-on-rails - remota - git push example
Cómo trabajar con las ramas de Git y las migraciones de Rails (11)
Base de datos separada para cada rama
Es la única forma de volar.
Actualización 16 de octubre de 2017
Volví a esto después de bastante tiempo e hice algunas mejoras:
- Agregué otra tarea de rastrillo del espacio de nombres para crear una rama y clonar la base de datos de una sola vez, con
bundle exec rake git:branch
. - Ahora me doy cuenta de que la clonación desde el máster no siempre es lo que quieres hacer, así que hice más explícito que la tarea
db:clone_from_branch
toma unaSOURCE_BRANCH
y una variable de entornoTARGET_BRANCH
. Al usargit:branch
, usará automáticamente la rama actual comoSOURCE_BRANCH
. - Refactorización y simplificación.
config/database.yml
Y para que le sea más fácil, así es como actualiza su archivo database.yml
para determinar dinámicamente el nombre de la base de datos en función de la rama actual.
<%
database_prefix = ''your_app_name''
environments = %W( development test )
current_branch = `git status | head -1`.to_s.gsub(''On branch '','''').chomp
%>
defaults: &defaults
pool: 5
adapter: mysql2
encoding: utf8
reconnect: false
username: root
password:
host: localhost
<% environments.each do |environment| %>
<%= environment %>:
<<: *defaults
database: <%= [ database_prefix, current_branch, environment ].join(''_'') %>
<% end %>
lib/tasks/db.rake
Aquí hay una tarea de Rake para clonar fácilmente su base de datos de una rama a otra. Esto requiere un SOURCE_BRANCH
y un TARGET_BRANCH
variables de entorno. Basado en la tarea de @spalladino .
namespace :db do
desc "Clones database from another branch as specified by `SOURCE_BRANCH` and `TARGET_BRANCH` env params."
task :clone_from_branch do
abort "You need to provide a SOURCE_BRANCH to clone from as an environment variable." if ENV[''SOURCE_BRANCH''].blank?
abort "You need to provide a TARGET_BRANCH to clone to as an environment variable." if ENV[''TARGET_BRANCH''].blank?
database_configuration = Rails.configuration.database_configuration[Rails.env]
current_database_name = database_configuration["database"]
source_db = current_database_name.sub(CURRENT_BRANCH, ENV[''SOURCE_BRANCH''])
target_db = current_database_name.sub(CURRENT_BRANCH, ENV[''TARGET_BRANCH''])
mysql_opts = "-u #{database_configuration[''username'']} "
mysql_opts << "--password=/"#{database_configuration[''password'']}/" " if database_configuration[''password''].presence
`mysqlshow #{mysql_opts} | grep "#{source_db}"`
raise "Source database #{source_db} not found" if $?.to_i != 0
`mysqlshow #{mysql_opts} | grep "#{target_db}"`
raise "Target database #{target_db} already exists" if $?.to_i == 0
puts "Creating empty database #{target_db}"
`mysql #{mysql_opts} -e "CREATE DATABASE #{target_db}"`
puts "Copying #{source_db} into #{target_db}"
`mysqldump #{mysql_opts} #{source_db} | mysql #{mysql_opts} #{target_db}`
end
end
lib/tasks/git.rake
Esta tarea creará una rama git fuera de la rama actual (principal, u otra), la verificará y clonará la base de datos de la sucursal actual en la base de datos de la nueva sucursal. Es astuto AF.
namespace :git do
desc "Create a branch off the current branch and clone the current branch''s database."
task :branch do
print ''New Branch Name: ''
new_branch_name = STDIN.gets.strip
CURRENT_BRANCH = `git status | head -1`.to_s.gsub(''On branch '','''').chomp
say "Creating new branch and checking it out..."
sh "git co -b #{new_branch_name}"
say "Cloning database from #{CURRENT_BRANCH}..."
ENV[''SOURCE_BRANCH''] = CURRENT_BRANCH # Set source to be the current branch for clone_from_branch task.
ENV[''TARGET_BRANCH''] = new_branch_name
Rake::Task[''db:clone_from_branch''].invoke
say "All done!"
end
end
Ahora, todo lo que necesitas hacer es ejecutar bundle exec git:branch
, ingresar el nuevo nombre de la rama y comenzar a matar zombies.
Estoy trabajando en una aplicación de rieles con bastantes ramas de git y muchos de ellos incluyen migraciones de db. Tratamos de tener cuidado, pero de vez en cuando algún fragmento de código en el maestro solicita una columna que fue eliminada / renombrada en otra rama.
¿Cuál sería una buena solución para "juntar" ramas git con estados DB?
¿Qué serían realmente estos "estados"?
No podemos simplemente duplicar una base de datos si tiene unos pocos GB de tamaño.
¿Y qué debería pasar con las fusiones?
¿La solución se traduciría a bases de datos no SQL también?
Actualmente usamos MySQL, mongodb y redis
EDITAR: Parece que olvidé mencionar un punto muy importante, solo me interesa el entorno de desarrollo, pero con bases de datos grandes (algunos GB de tamaño).
¿Quizás debería tomar esto como una pista de que su base de datos de desarrollo es demasiado grande? Si puede usar db / seeds.rb y un conjunto de datos más pequeño para el desarrollo, entonces su problema se puede resolver fácilmente utilizando schema.rb y seeds.rb de la rama actual.
Eso supone que su pregunta se relaciona con el desarrollo; No puedo imaginar por qué necesitarías cambiar regularmente las sucursales en producción.
Aquí hay un script que escribí para cambiar entre las ramas que contienen migraciones diferentes:
https://gist.github.com/4076864
No resolverá todos los problemas que mencionaste, pero dado el nombre de una sucursal:
- Revertir cualquier migración en su rama actual que no exista en la rama dada
- Deseche cualquier cambio en el archivo db / schema.rb
- Echa un vistazo a la rama dada
- Ejecuta cualquier nueva migración existente en la rama dada
- Actualiza tu base de datos de prueba
Me encuentro haciendo esto todo el tiempo en nuestro proyecto, así que pensé que sería bueno automatizar el proceso.
Desea conservar un "entorno db" por rama. Mire la secuencia de comandos de borrado / limpieza para señalar instancias diferentes. Si se le agotan las instancias de db, haga que la secuencia de comandos se convierta en una instancia temporal, de modo que cuando cambie a una nueva rama, ya esté allí y el script necesite cambiarle el nombre. Las actualizaciones de DB deben ejecutarse justo antes de ejecutar las pruebas.
Espero que esto ayude.
En el entorno de desarrollo:
Debería trabajar con rake db:migrate:redo
para probar si su script es reversible, pero tenga en cuenta que siempre debe tener un seed.rb
con la población de datos.
Si trabajas con git, debes modificar seed.rb con un cambio de migración, y la ejecución de db:migrate:redo
para el inicio (cargar los datos para un nuevo desarrollo en otra máquina o nueva base de datos)
Aparte del ''cambio'', con los métodos de arriba y abajo, su código siempre cubrirá escenarios para el "cambio" en este momento y cuando comience desde cero.
Estaba luchando con el mismo problema. Aquí está mi solución:
Asegúrese de que tanto schema.rb como todas las migraciones estén marcadas por todos los desarrolladores.
Debe haber una persona / máquina para las implementaciones en la producción. Llamemos a esta máquina como la máquina de fusión. Cuando los cambios se llevan a la máquina de fusión, la combinación automática para schema.rb fallará. Sin problemas. Simplemente reemplace el contenido con el contenido anterior de schema.rb (puede guardar una copia o puede obtenerla de github si la usa ...).
Aquí está el paso importante. Las migraciones de todos los desarrolladores ahora estarán disponibles en la carpeta db / migrate. Adelante, ejecuta bundle exec rake db: migrate. Acercará la base de datos en la máquina de fusión a todos los cambios. También regenerará schema.rb.
Comprométase y envíe los cambios a todos los repositorios (controles remotos e individuos, que también son controles remotos). ¡Deberías haber terminado!
Esto es lo que hice y no estoy seguro de haber cubierto todas las bases:
En desarrollo (usando postgresql):
- sql_dump nombre_bd> tmp / branch1.sql
- git checkout branch2
- dropdb db_name
- createdb db_name
- psql nombre_bd <tmp / branch2.sql # (del interruptor de rama anterior)
Esto es mucho más rápido que las utilidades de rake en una base de datos con aproximadamente 50,000 registros.
Para la producción, mantenga la rama principal como sacrosanct y todas las migraciones estén registradas, shema.rb correctamente fusionada. Siga su procedimiento de actualización estándar.
Experimento totalmente la pita que tienes aquí. Mientras lo pienso, el verdadero problema es que todas las sucursales no tienen el código para deshacer ciertas ramas. Estoy en el mundo django, así que no sé rake tan bien. Estoy jugando con la idea de que las migraciones viven en su propio repositorio que no se ramifica (git-submódulo, del cual aprendí recientemente). De esa forma todas las ramas tienen todas las migraciones. La parte adhesiva es asegurarse de que cada rama esté restringida solo a las migraciones que les interesan. Hacer / mantener un registro de eso manualmente sería una pita y propenso a error. Pero ninguna de las herramientas de migración está diseñada para esto. Ese es el punto en el que no tengo un camino hacia adelante.
Si tiene una gran base de datos que no puede reproducir fácilmente, le recomiendo usar las herramientas de migración normales. Si quiere un proceso simple, esto es lo que recomendaría:
- Antes de cambiar de ramas, revertir (
rake db:rollback
) al estado anterior al punto de ramificación. Luego, después de cambiar de ramas, ejecutedb:migrate
. Esto es matemáticamente correcto, y siempre que escriba scripts, funcionará. - Si olvida hacer esto antes de cambiar de rama, en general puede volver atrás, retroceder y volver a cambiar de manera segura, por lo que creo que es un flujo de trabajo viable.
- Si tiene dependencias entre migraciones en diferentes ramas ... bueno, tendrá que pensar mucho.
Sugeriría una de dos opciones:
Opción 1
- Pon tus datos en
seeds.rb
. Una buena opción es crear sus datos de semilla a través de la gema FactoryGirl / Fabrication. De esta forma puede garantizar que los datos estén sincronizados con el código si suponemos que las fábricas se actualizan junto con la adición / eliminación de columnas. - Después de cambiar de una rama a otra, ejecute
rake db:reset
, que efectivamente elimina / crea / inicia la base de datos.
opcion 2
Mantener manualmente los estados de la base de datos ejecutando siempre rake db:rollback
/ rake db:migrate
antes / después de una extracción de sucursal. La advertencia es que todas sus migraciones deben ser reversibles, de lo contrario, esto no funcionará.
Cuando agrega una nueva migración en cualquier rama, ejecute rake db:migrate
db/schema.rb
tanto la migración como db/schema.rb
Si hace esto, en desarrollo, podrá cambiar a otra rama que tenga un conjunto diferente de migraciones y simplemente ejecutar rake db:schema:load
.
Tenga en cuenta que esto recreará toda la base de datos y se perderán los datos existentes .
Probablemente solo desee ejecutar la producción de una rama con la que tenga mucho cuidado, por lo que estos pasos no se aplican allí (simplemente ejecute rake db:migrate
como siempre). Pero en el desarrollo, no debería ser un gran problema recrear la base de datos desde el esquema, que es lo que rake db:schema:load
hará.