Je travaille sur une application Rails avec pas mal de branches git et beaucoup d'entre elles incluent des migrations db. Nous essayons d'être prudents mais parfois un morceau de code dans master demande une colonne qui a été supprimée/renommé dans une autre branche.
Quelle serait une bonne solution pour "coupler" des branches git avec des états DB?
Que seraient réellement ces "états"?
Nous ne pouvons pas simplement dupliquer une base de données si sa taille est de quelques Go.
Et que devrait-il se passer avec les fusions?
La solution se traduirait-elle également dans les bases de données noSQL?
Nous utilisons actuellement MySQL, mongodb et redis
EDIT: On dirait que j'ai oublié de mentionner un point très important, je ne suis intéressé que par environnement de développement mais avec de grandes bases de données (quelques Go en taille).
Lorsque vous ajoutez une nouvelle migration dans une branche, exécutez rake db:migrate
et valider la migration etdb/schema.rb
Si vous faites cela, en cours de développement, vous pourrez passer à une autre branche qui a un ensemble différent de migrations et simplement exécuter rake db:schema:load
.
Notez que cela recréera la base de données entière et les données existantes seront perdues.
Vous voudrez probablement exécuter la production à partir d'une seule branche avec laquelle vous faites très attention, donc ces étapes ne s'appliquent pas là (exécutez simplement rake db:migrate
comme d'habitude là-bas). Mais en développement, il ne devrait pas être très important de recréer la base de données à partir du schéma, ce qui est rake db:schema:load
ça ira.
Si vous avez une grande base de données que vous ne pouvez pas reproduire facilement, je vous recommande d'utiliser les outils de migration normaux. Si vous voulez un processus simple, voici ce que je recommanderais:
rake db:rollback
) à l'état avant le point de branchement. Ensuite, après avoir changé de branche, exécutez db:migrate
. C'est mathématiquement correct, et tant que vous écrivez des scripts down
, cela fonctionnera.Voici un script que j'ai écrit pour basculer entre les branches qui contiennent différentes migrations:
https://Gist.github.com/4076864
Cela ne résoudra pas tous les problèmes que vous avez mentionnés, mais étant donné un nom de branche, il:
Je me retrouve à le faire manuellement tout le temps sur notre projet, alors j'ai pensé que ce serait bien d'automatiser le processus.
C'est le seul moyen de voler.
J'y suis revenu après un certain temps et j'ai apporté quelques améliorations:
bundle exec rake git:branch
.db:clone_from_branch
la tâche prend un SOURCE_BRANCH
et un TARGET_BRANCH
variable d'environnement. Lors de l'utilisation de git:branch
il utilisera automatiquement la branche courante comme SOURCE_BRANCH
.config/database.yml
Et pour vous faciliter la tâche, voici comment mettre à jour votre database.yml
fichier pour déterminer dynamiquement le nom de la base de données en fonction de la branche actuelle.
<%
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
Voici une tâche Rake pour cloner facilement votre base de données d'une branche à une autre. Cela prend un SOURCE_BRANCH
et un TARGET_BRANCH
Variables d'environnement. Basé sur la tâche 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
Cette tâche créera une branche git hors de la branche actuelle (principale ou autre), la vérifiera et clonera la base de données de la branche actuelle dans la nouvelle base de données de la nouvelle branche. C'est AF lisse.
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
Maintenant, tout ce que vous devez faire est d'exécuter bundle exec git:branch
, entrez le nouveau nom de la branche et commencez à tuer les zombies.
Peut-être devriez-vous prendre cela comme un indice que votre base de données de développement est trop grande? Si vous pouvez utiliser db/seeds.rb et un ensemble de données plus petit pour le développement, votre problème peut être facilement résolu en utilisant schema.rb et seeds.rb de la branche actuelle.
Cela suppose que votre question concerne le développement; Je ne peux pas imaginer pourquoi vous auriez besoin de changer régulièrement de branche en production.
Je me débattais avec le même problème. Voici ma solution:
Assurez-vous que schema.rb et toutes les migrations sont archivés par tous les développeurs.
Il doit y avoir une personne/machine pour les déploiements en production. Appelons cette machine la machine de fusion. Lorsque les modifications sont extraites de la machine de fusion, la fusion automatique pour schema.rb échoue. Pas d'issues. Remplacez simplement le contenu par le contenu précédent de schema.rb (vous pouvez mettre une copie de côté ou l'obtenir auprès de github si vous l'utilisez ...).
Voici l'étape importante. Les migrations de tous les développeurs seront désormais disponibles dans le dossier db/migrate. Allez-y et exécutez le bundle exec rake db: migrate. Il apportera la base de données sur la machine de fusion à égalité avec toutes les modifications. Il régénérera également schema.rb.
Validez et transférez les modifications vers tous les référentiels (télécommandes et individus, qui sont également des télécommandes). Vous devriez avoir terminé!
C'est ce que j'ai fait et je ne suis pas sûr d'avoir couvert toutes les bases:
En développement (en utilisant postgresql):
C'est beaucoup plus rapide que les utilitaires de râteau sur une base de données avec environ 50K enregistrements.
Pour la production, conservez la branche principale en tant que sacro-saint et toutes les migrations sont archivées, shema.rb correctement fusionné. Suivez votre procédure de mise à niveau standard.
J'expérimente totalement le pita que vous avez ici. En y réfléchissant, le vrai problème est que toutes les branches n'ont pas le code pour restaurer certaines branches. Je suis dans le monde Django, donc je ne sais pas si bien ratisser. Je joue avec l'idée que les migrations vivent dans leur propre référentiel qui ne soit pas ramifié (git -submodule, dont j'ai récemment entendu parler.) De cette façon, toutes les branches ont toutes les migrations. La partie collante consiste à s'assurer que chaque branche est limitée aux seules migrations qui leur tiennent à cœur. sujettes à erreur. Mais aucun des outils de migration n'est conçu pour cela. C'est le point où je suis sans voie à suivre.
Vous souhaitez conserver un "environnement db" par branche. Regardez le script smudge/clean pour pointer vers différentes instances. Si vous manquez d'instances db, demandez au script de dériver une instance temporaire, donc lorsque vous passez à une nouvelle branche, elle est déjà là et doit juste être renommée par le script. Les mises à jour de base de données doivent s'exécuter juste avant d'exécuter vos tests.
J'espère que cela t'aides.
Je suggérerais l'une des deux options:
seeds.rb
. Une bonne option consiste à créer vos données de semences via FactoryGirl/Fabrication gem. De cette façon, vous pouvez garantir que les données sont synchronisées avec le code si nous supposons que les usines sont mises à jour avec l'ajout/la suppression de colonnes.rake db:reset
, qui supprime/crée/amorce efficacement la base de données.Gérez manuellement les états de la base de données en exécutant toujours rake db:rollback
/rake db:migrate
avant/après un paiement en succursale. La mise en garde est que toutes vos migrations doivent être réversibles, sinon cela ne fonctionnera pas.
Sur l'environnement de développement:
Vous devriez travailler avec rake db:migrate:redo
pour tester si votre script est réversible, mais gardez à l'esprit que vous devriez toujours avoir un seed.rb
avec la population de données.
Si vous travaillez avec git, vous seed.rb devrait être modifié avec un changement de migration, et l'exécution de db:migrate:redo
pour le début (charger les données pour un nouveau développement sur une autre machine ou une nouvelle base de données)
Hormis ´change´, avec vos méthodes up et down, votre code sera toujours des scénarios de couverture pour le "changement" en ce moment et quand il partira de zéro.