Skip to content

Instantly share code, notes, and snippets.

@ndbroadbent
Created September 28, 2012 22:18
Show Gist options
  • Save ndbroadbent/3802355 to your computer and use it in GitHub Desktop.
Save ndbroadbent/3802355 to your computer and use it in GitHub Desktop.
Rake task for precompiling assets locally before deploying to Heroku
require 'fileutils'
# Warning: The following deploy task will completely overwrite whatever is currently deployed to Heroku.
# The deploy branch is rebased onto master, so the push needs to be forced.
desc "Deploy app to Heroku after precompiling assets"
task :deploy do
deploy_branch = 'heroku'
remote = 'heroku'
deploy_repo_dir = "tmp/heroku_deploy"
begin
# Check branch and working directory. Fail if not on clean master branch.
branch = `branch_name=$(git symbolic-ref HEAD 2>/dev/null); branch_name=${branch_name##refs/heads/}; echo ${branch_name:-HEAD}`.strip
if branch != 'master'
raise "You have checked out the #{branch} branch, please only deploy from the master branch!"
end
if `git status --porcelain`.present?
raise "You have unstaged or uncommitted changes! Please only deploy from a clean working directory!"
end
unless File.exists? deploy_repo_dir
# Copy repo to tmp dir so we can continue working while it deploys
@new_repo = true
puts "Copying repo to #{deploy_repo_dir}..."
# Can't copy into self, so copy to /tmp first
FileUtils.cp_r Rails.root.to_s, "/tmp/heroku_deploy"
FileUtils.mv "/tmp/heroku_deploy", Rails.root.join('tmp')
end
# Change working directory to copied repo
Dir.chdir(deploy_repo_dir)
# Create new deploy branch if starting with a fresh repo
if @new_repo
# Set remote to parent git dir, so we can fetch updates on future deploys
system "git remote set-url origin ../.."
system "git checkout -b #{deploy_branch}"
# Allow git to see public/assets
puts "Removing public/assets from .gitignore..."
system 'sed -i '' "/^[\/?]public\/assets/d" .gitignore'
system 'git add .gitignore; git commit -m "Allow git to commit public/assets"'
else
# Otherwise, we're already on the deploy branch, so fetch any new changes from original repo
system "git fetch origin"
# Rebase onto origin/master.
# This step should never fail, because we are rebasing commits for files
# that should be ignored by the master repo.
unless system "git rebase origin/master"
raise "Rebase Failed! Please delete tmp/heroku_deploy and start again."
end
end
# Precompile assets
Rake::Task['assets:precompile'].invoke
# Add any extra tasks you want to run here, such as compiling static pages, etc.
# Add all changes and new files
system 'git add -A'
commit_message = "Added precompiled assets"
if @new_repo
# Create new commit for new repo
system "git commit -m '#{commit_message}'", :out => "/dev/null"
else
# If existing repo, amend previous assets commit
system "git commit --amend -m '#{commit_message}'", :out => "/dev/null"
end
puts "Commit: #{commit_message}"
# Force push deploy branch to Heroku
puts "Pushing #{deploy_branch} branch to master on #{remote}..."
IO.popen("git push #{remote} #{deploy_branch}:master -f") do |io|
while (line = io.gets) do
puts line
end
end
end
end
@amalagaura
Copy link

Fantastic work on this and the gem! I would like to share issues I had on Mac OSX 10.6.8 with regular bash shell. Maybe i have some customizations but I got '-n' as the contents of branch when the rake task runs, but it works ok from the shell. So I removed -n from the echo line and added .chomp to the end.

Secondly the sed command didn't work for 2 reasons, the -i needs an non-optional extension and in my gitignore it is public/assets
sed -i '' "/^[/?]public/assets/d" .gitignore

I can make a fork, but maybe this is easier?

@ndbroadbent
Copy link
Author

Hi, thanks very much for the feedback and suggestions! I've changed echo -n ...' to echo ...'.strip, and I've replaced the sed command with your suggestion. Please let me know if you run into any more problems.

Cheers!

@volkanunsal
Copy link

I used it with a small change. Rather than origin, I used #{remote} in shell commands because I don't use a separate origin repo, just the Heroku repo.

@jjb
Copy link

jjb commented Oct 20, 2012

bash and sh will both use their built-in echo. bash's echo supports the -n flag, but sh does not

 ➔ sh
sh-3.2$ type cho
sh: type: cho: not found
sh-3.2$ type echo
echo is a shell builtin
sh-3.2$ /bin/echo foo
foo
sh-3.2$ echo foo
foo
sh-3.2$ /bin/echo -n foo
foosh-3.echo -n foo
-n foo

@jjb
Copy link

jjb commented Oct 20, 2012

(ignore type cho)

@jjb
Copy link

jjb commented Oct 20, 2012

@ndbroadbent why not make this rake task a part of the turbo-sprockets-rails3 gem?

@seocahill
Copy link

great work man! I've a bad case of premature heroku deploying without the assets:precompile - this works great. Love the gem also, well done.

@turadg
Copy link

turadg commented Jan 22, 2013

Thanks for this! One suggestion, change the FileUtils.cp_r on line 27 to using the system command:

system "cp -r #{Rails.root} /tmp/heroku_deploy"

I had a socket file in my tmp/ and FileUtils.cp_r() failed with the following error. Only cp -r told me which file.

$ rake deploy
Copying repo to tmp/heroku_deploy...
rake aborted!
cannot handle socket
/Users/turadg/.rbenv/versions/1.9.3-p327-perf/lib/ruby/1.9.1/fileutils.rb:1357:in `copy'
/Users/turadg/.rbenv/versions/1.9.3-p327-perf/lib/ruby/1.9.1/fileutils.rb:465:in `block in copy_entry'
/Users/turadg/.rbenv/versions/1.9.3-p327-perf/lib/ruby/1.9.1/fileutils.rb:1433:in `preorder_traverse'
/Users/turadg/.rbenv/versions/1.9.3-p327-perf/lib/ruby/1.9.1/fileutils.rb:462:in `copy_entry'
/Users/turadg/.rbenv/versions/1.9.3-p327-perf/lib/ruby/1.9.1/fileutils.rb:437:in `block in cp_r'
/Users/turadg/.rbenv/versions/1.9.3-p327-perf/lib/ruby/1.9.1/fileutils.rb:1515:in `block in fu_each_src_dest'
/Users/turadg/.rbenv/versions/1.9.3-p327-perf/lib/ruby/1.9.1/fileutils.rb:1531:in `fu_each_src_dest0'
/Users/turadg/.rbenv/versions/1.9.3-p327-perf/lib/ruby/1.9.1/fileutils.rb:1513:in `fu_each_src_dest'
/Users/turadg/.rbenv/versions/1.9.3-p327-perf/lib/ruby/1.9.1/fileutils.rb:436:in `cp_r'

@ericboehs
Copy link

👍 @turadg I also had a socket this barfed on.

@rap1ds
Copy link

rap1ds commented Apr 4, 2014

The sed command didn't work for me, probably because I'm on zsh. Removing the brackets helped: sed -i "" "/^public\/assets/d" .gitignore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment