So, as most people do, I wrote my own. Hopefully this short rake task can help you as well. This assumes that your application server has your app checked out as a clone of some git repo you push changes to and that you are running under passenger. When I want to deploy, I log in to my production server,
cd
to my app repo, and then run:rake myapp:deploy
For just strictly view updates, it completes in 3 seconds or less. There are several things it does, in addition to checking for errors:
- Checks to make sure the app's git checkout isn't dirty from any local edits.
- Fetches the remote branch and checks if there are any new commits, exits if not.
- Tags the current production code base before pulling the changes.
- Does a git pull with fast-forward only (to avoid unexpected merging).
- Checks if there are any new gems to install via bundle (checks for changes in Gemfile and Gemfile.lock).
- Checks if there are any database migrations that need to be done (checks for changes to db/schema.db db/migrations/*).
- Checks for possible changes to assets and precompiles if needed (checks Gemfile.lock and app/assets/*).
- Restarts passenger to pick up the changes.
- Does a HEAD request on / to make sure it gets an expected 200 showing the server is running without errors.
The script can also take a few arguments:
:branch
Git branch, defaults to master:remote
Git remote, defaults to origin:server_url
URL for HEAD request to check server after completion
Note, if the task encounters an error, you have to manually complete the deploy. You should not rerun the task.
Any finally, here is the task itself. You can save this to
lib/tasks/myapp.rb
# We can't use Rake::Task because it can fail when things are mid
# upgrade
require "net/http"
def do_at_exit(start_time)
puts "Time: #{(Time.now - start_time).round(3)} secs"
end
def start_timer
start_time = Time.now
at_exit { do_at_exit(start_time) }
end
namespace :myapp do
desc 'Deployment automation'
task :deploy, [:branch, :remote, :server_url] do |t, args|
start_timer
# Arg supercedes env, which supercedes default
branch = args[:branch] || ENV['DEPLOY_BRANCH'] || 'master'
remote = args[:remote] || ENV['DEPLOY_REMOTE'] || 'origin'
server_url = args[:server_url] || ENV['DEPLOY_SERVER_URL'] || 'http://localhost/'
puts "II: Starting deployment..."
# Check for dirty repo
unless system("git diff --quiet")
puts "WW: Refusing to deploy on a dirty repo, exiting."
exit 1
end
# Update from remote so we can check for what to do
system("git fetch -n #{remote}")
# See if there's anything new at all
if system("git diff --quiet HEAD..#{remote}/#{branch} --")
puts "II: Nothing new, exiting"
exit
end
# Tag this revision...
tag = "prev-#{DateTime.now.strftime("%Y%m%dT%H%M%S")}"
system("git tag -f #{tag}")
# Pull in the changes
if ! system("git pull --ff-only #{remote} #{branch}")
puts "EE: Failed to fast-forward to #{branch}"
exit 1
end
# Base command to check for differences
cmd = "git diff --quiet #{tag}..HEAD"
if system("#{cmd} Gemfile Gemfile.lock")
puts "II: No updates to bundled gems"
else
puts "II: Running bundler..."
Bundler.with_clean_env do
if ! system("bundle install")
puts "EE: Error running bundle install"
exit 1
end
end
end
if system("#{cmd} db/schema.rb db/migrate/")
puts "II: No db changes"
else
puts "II: Running db migrations..."
# We run this as a sub process to avoid errors
if ! system("rake db:migrate")
puts "EE: Error running db migrations"
exit 1
end
end
if system("#{cmd} Gemfile.lock app/assets/")
puts "II: No changes to assets"
else
puts "II: Running asset updates..."
if ! system("rake assets:precompile")
puts "EE: Error precompiling assets"
exit 1
end
system("rake assets:clean")
end
puts "II: Restarting Passenger..."
FileUtils.touch("tmp/restart.txt")
puts "II: Checking HTTP response code..."
uri = URI.parse(server_url)
res = nil
Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
req = Net::HTTP::Head.new(uri, {'User-Agent' => 'deploy/net-check'})
res = http.request req
end
if res.code != "200"
puts "EE: Server returned #{res.code}!!!"
exit 1
else
puts "II: Everything appears to be ok"
end
end
end
Here's an example of the command output:
$ rake myapp:deploy
II: Starting deployment...
remote: Counting objects: 15, done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 8 (delta 6), reused 0 (delta 0)
Unpacking objects: 100% (8/8), done.
From /home/user/myapp
efee45c..e5468c1 master -> origin/master
From /home/user/myapp
* branch master -> FETCH_HEAD
Updating efee45c..e5468c1
Fast-forward
app/views/users/_display.html.erb | 7 +++++--
public/svg/badges/caretakers-club.svg | 1 -
2 files changed, 5 insertions(+), 3 deletions(-)
delete mode 100644 public/svg/badges/caretakers-club.svg
II: No updates to bundled gems
II: No db changes
II: No changes to assets
II: Restarting Passenger...
II: Checking HTTP response code...
II: Everything appears to be ok
Time: 3.031 secs