Rails 3 and Heroku: automatically "rake db:migrate" on push?

Ruby on-RailsGitHeroku

Ruby on-Rails Problem Overview


I have a slight annoyance with my heroku push/deploy process, which otherwise has been a joy to discover and use.

If i add a new migration to my app, the only way i can get it up onto the heroku server is to do a push to the heroku remote. This uploads it and restarts the app. But it doesn't run the migration, so i have to do heroku rake db:migrate --app myapp, then heroku restart --app myapp. In the meantime, the app is broken because it hasn't run the migrations and the code is referring to fields/tables etc in the migration.

There must be a way to change the deployment process to run the rake db:migrate automatically as part of the deploy process but i can't work it out.

Is it something i set in a heroku cpanel? Is it an option i pass to heroku from the command line? Is it a git hook? Can anyone set me straight? thanks, max

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

Heroku now has the ability to handle this as part of their "release phase" feature.

You can add a process called release to your Procfile and that will be run during each and every deploy.

Rails >= 5 Example

release: bundle exec rails db:migrate

Rails < 5 example

release: bundle exec rake db:migrate

Solution 2 - Ruby on-Rails

What about this simple command chaining solution:

git push heroku master && heroku run rake db:migrate

It will automatically run the migrate as soon as the first one finishes successfully. It's tipically 1-2 seconds delay or less.

Solution 3 - Ruby on-Rails

Here is a rake task that wraps up everything into a one-liner (and also supports rollback):

https://gist.github.com/362873

You still might wind up deploying on top of your boss's demo, but at least you don't waste time typing between the git push and the rake db:migrate.

Solution 4 - Ruby on-Rails

I created a custom buildpack that gets Heroku to run rake db:migrate for you automatically on deployment. It's just a fork of Heroku's default Ruby buildpack, but with the rake db:migrate task added.

To use it with your app you'd do this:

heroku config:set BUILDPACK_URL=https://github.com/dtao/rake-db-migrate-buildpack

Also note that in order for it to work, you need to enable the user-env-compile Heroku Labs feature. Here's how you do that:

heroku labs:enable user-env-compile

And here's my evidence that this works:

rake db:migrate on Heroku deployment

Solution 5 - Ruby on-Rails

Perhaps you could try separating your schema commits (migrations, etc.) commits from code commits (models, validations, etc.).

(Note the following assumes your migration changes are NOT destructive, as you've indicate covers most of your use cases.)

Your deploy process could then be:

  1. Push schema changes to Heroku
  2. migrate
  3. Push application code to Heroku

This is of course far form optimal, but is an effective way to avoid downtime in the situation you've described: by the time the app receive the code for the dynamic fields, the DB will already have migrated.

(Of course, the simplest solution would be to simply push and migrate while your boss is out to lunch ;-D)

Otherwise, even if schema modifications were carried out automatically you'd still run the risk of a request passing through right before the migrations have been run.

Solution 6 - Ruby on-Rails

Just for those googling folks like me I want to give a plain solution here.

I am using Rails 4 and needed to add a simple Rake task to the deployment to heroku. As I am using the 'deploy to heroku' button in github there is no chance to run "heroku run ..." immediately after deployment.

What I did: I extended the standard Rake Task 'assets:clean' that is automatically run during a deployment to heroku. The task still runs normally but I have attached my own stuff to it's end. This is done with the 'enhance' method. In the example below I add a db:migrate because this is probably what most people want:

# in lib/tasks/assets_clean_enhance.rake
Rake::Task['assets:clean'].enhance do
  Rake::Task['db:migrate'].invoke
end

I admit that this is no perfect solution. But the heroku Ruby Buildpack still does not support any other way. And writing my own buildback seemed a bit of an overkill for so simple a thing.

Solution 7 - Ruby on-Rails

I use a rake task to put the app in maintenance mode, push, migrate and move it off maintenance mode.

Solution 8 - Ruby on-Rails

I wrote SmartMigrate buildpack which is a simple Heroku buildpack to warn of pending migrations after a ruby build whenever new migrations detected. This buildpack is intended to be part of a Multipack that has a preceding Ruby buildpack.

With due respect to other solutions here, this buildpack has 3 advantages over those:

  1. No need for maintenance mode
  2. No need for out-dated ruby buildpack forks that just insert the migration at the end
  3. No need to run migrations ALL THE TIME, a warning is only displayed if new migrations are detected since the last deployment

Solution 9 - Ruby on-Rails

I think David Sulc's approach is the only one which ensures that you avoid requests getting through while the app is in a broken state.

It is a bit of a pain, but may be necessary in some circumstances.

As he stated, it does require that the db migrations are non-destructive.

However, it can be difficult to push your migrations and schema changes prior to the rest of the code, as the obvious approach ('git push heroku {revnum}') relies on you having checked the migrations in before the rest of the code.

If you haven't done that, it's still possible to do this using a temporary branch:

  • Create a branch, based at the git revision that you most recently pushed to heroku:

     git branch <branchname> <revnum-or-tag>
    
  • Check out that branch:

     git checkout <branchname>
    
  • If your db migration commits only contained migrations, and no code changes, cherry-pick the commits that contain the database changes:

     git cherry-pick <revnum1> <revnum2>...
    
  • If you commited your db changes in revisions which also contained code changes, you can use 'git cherry-pick -n' which won't automatically commit; use 'git reset HEAD ' to remove the files that aren't db changes from the set of things that are going to be commited. Once you've got just the db changes, commit them in your temporary branch.

     git cherry-pick -n <revnum1> <revnum2>...
     git reset HEAD <everything that's modified except db/>
     git status
     ... check that everything looks ok ...
     git commit
    
  • Push this temporary branch to heroku (ideally to a staging app to check that you've got it right, since avoiding downtime is the whole point of jumping through these hoops)

     git push heroku <branchname>:master
    
  • Run the migrations

     heroku run rake db:migrate
    
  • At this point, you might think that you could just push 'master' to heroku to get the code changes across. However, you can't, as it isn't a fast-forward merge. The way to proceed is to merge the remainder of 'master' into your temporary branch, then merge it back to master, which recombines the commit histories of the two branches:

     git checkout <branchname>
     git merge master
     git diff <branchname> master
     ... shouldn't show any differences, but just check to be careful ...
     git checkout master
     git merge <branchname>
    
  • Now you can push master to heroku as normal, which will get the rest of your code changes across.

In the second-to-last step, I'm not 100% sure whether merging master to {branchname} is necessary. Doing it that way should ensure that a 'fast-forward' merge is done, which keeps git happy when you push to heroku, but it might be possible to get the same result by just merging {branchname} to master without that step.

Of course, if you aren't using 'master', substitute the appropriate branch name in the relevant places above.

Solution 10 - Ruby on-Rails

I've been using the heroku_san gem as my deployment tool for a while. It is a nice small, focused tool for the push + migration. It adds some other rake commands that make accessing other functions (like console) easy. Beyond not having to remember database migrations, my favorite feature is its Heroku configuration file – so I can name all my servers (production, staging, playground4, shirley) whatever I want – and keep them straight in my head.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMax WilliamsView Question on Stackoverflow
Solution 1 - Ruby on-RailsMax WoolfView Answer on Stackoverflow
Solution 2 - Ruby on-RailsCristianView Answer on Stackoverflow
Solution 3 - Ruby on-RailsPaul A JungwirthView Answer on Stackoverflow
Solution 4 - Ruby on-RailsDan TaoView Answer on Stackoverflow
Solution 5 - Ruby on-RailsDavid SulcView Answer on Stackoverflow
Solution 6 - Ruby on-RailsKariView Answer on Stackoverflow
Solution 7 - Ruby on-RailsAditya SanghiView Answer on Stackoverflow
Solution 8 - Ruby on-RailshammadyView Answer on Stackoverflow
Solution 9 - Ruby on-RailssheltondView Answer on Stackoverflow
Solution 10 - Ruby on-RailsslothbearView Answer on Stackoverflow