Fastest way to deploy rails apps with Passenger - ruby-on-rails

I am working on a Dreamhost server with Rails 2.3.5.
Every time I make changes to a site, I have to ssh into the site, remove all the files, upload a zip file containing all the new files for the site, unzip that file, migrate the database, and go.
Something tells me there's a faster way to deploy rails apps. I am using mac Time Machine to keep track of different versions of my applications. I know git tracks files, but I don't really know how to work with it to deploy my applications, since passenger takes care of all the magic for me.
What would be a faster way to deploy my applications (and avoid the downtime associated with my method when I delete all files on the server)?
Thanks!

Take a look at Capistrano.

And go get yourself Capistrano helpers: http://github.com/westarete/capistrano-helpers
Here's a simplified, annotated Deploy file for my own site.
require 'capistrano-helpers/branch' # prompts for branch
require 'capistrano-helpers/passenger' # Support for Apache passenger
require 'capistrano-helpers/version' # Record the version number after deploying
require 'capistrano-helpers/privates' # handle sensitive files
require 'capistrano-helpers/shared'
set :application, "site"
set :repository, "file://."
set :scm, "git"
set :branch, "rails3"
set :deploy_via, :copy
set :copy_exclude, [ ".git", "assets", "test", "Capfile", "vishvish.tmproj", "bin" ]
set :deploy_to, "/blah/blah.com/#{application}"
set :user, "blah"
set :domain, "blah.com"
#Things you want symlinked afterwards, not in version control.
set :privates, %w{
config/database.yml
}
#Things you want shared across deployments, not uploaded every time.
set :shared, %w{
uploads
}
role :app, domain
role :web, domain
role :db, domain, :primary => true
namespace :deploy do
desc "Restarting mod_rails with restart.txt"
task :restart, :roles => :app, :except => { :no_release => true } do
run "touch #{current_path}/tmp/restart.txt"
end
[:start, :stop].each do |t|
desc "#{t} task is a no-op with mod_rails"
task t, :roles => :app do ; end
end
end

You definitely need git and capistrano.
My deployment process:
git commit
git push
cap deploy
Git is pretty straight forward. There are a ton of resources and how-tos on github.com.
If you aren't using source control you absolutely MUST fix that ...

Related

How to deploy licensed fonts that can't go into version-control with Capistrano?

I am using Capistrano to deploy my Rails application to a VPS (as shown in episode 335 on railscasts.com). I have some custom fonts I'm using for the application and don't want to check them into source-control for licensing reasons (See clarification at bottom).
What's the best way to go about this? Should I edit the deploy.rb file to sftp the assets/webfonts directory from my local computer? Maybe I just need to copy them manually to the my_app/shared/assets directory on the VPS, but I'd rather it be automated as part of the deploy task.
Here's my current deploy.rb file:
require "rvm/capistrano"
require "bundler/capistrano"
server "ip_address", :web, :app, :db, primary: true
set :application, "armory"
set :user, "username"
set :deploy_to, "/home/#{user}/apps/#{application}"
set :deploy_via, :remote_cache
set :use_sudo, false
set :scm, "git"
set :github_user, "gorrillamcd"
set :repository, "git#github.com:#{github_user}/Armory.git"
set :branch, "master"
default_run_options[:pty] = true
ssh_options[:forward_agent] = true
after "deploy", "deploy:cleanup" # keep only the last 5 releases
namespace :deploy do
%w[start stop restart].each do |command|
desc "#{command} unicorn server"
task command, roles: :app, except: {no_release: true} do
run "/etc/init.d/unicorn_#{application} #{command}"
end
end
task :setup_config, roles: :app do
sudo "ln -nfs #{current_path}/config/nginx.conf /etc/nginx/sites-enabled/#{application}"
sudo "ln -nfs #{current_path}/config/unicorn_init.sh /etc/init.d/unicorn_#{application}"
run "mkdir -p #{shared_path}/config"
put File.read("config/database.base.yml"), "#{shared_path}/config/database.yml"
puts "Now edit the config files in #{shared_path}."
end
after "deploy:setup", "deploy:setup_config"
task :symlink_config, roles: :app do
run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
run "ln -nfs #{shared_path}/config/initializers/stripe.rb #{release_path}/config/initializers/stripe.rb"
end
after "deploy:finalize_update", "deploy:symlink_config"
desc "Make sure local git is in sync with remote."
task :check_revision, roles: :web do
unless `git rev-parse HEAD` == `git rev-parse origin/master`
puts "WARNING: HEAD is not the same as origin/master"
puts "Run `git push` to sync changes."
exit
end
end
before "deploy", "deploy:check_revision"
end
Edit: Sorry for being so vague beforehand. I'm using Museo Slab from myfonts.com and a public repo on github (check my user profile if you want to see it). I read through the license and found this:
3. The Licensed Webfont(s) may be used on any Website owned or controlled by the Licensee
4. Agencies responsible for multiple clients’ Websites, for example web design agencies or hosting providers, may not share a single Webfont license across multiple clients’ Websites.
Those two lines of the license make me believe that checking the font into a public repository would be against the license, since it could be used on sites that are not mine without obtaining a new license for the font (even though they were free to begin with). I imagine that other people have had this problem before. So my question is, What's the normal/best way to handle deploy, with Capistrano, fonts (or any file for that matter) that can't be checked into source control?
I would pack the web fonts in an archive and put that archive somewhere separate from the repository. (Amazon S3 is a prime candidate.) Then, I'd have the release process retrieve and unpack this archive at deploy time.
This lets you put your entire app in your public repository -- the Capistrano scripts, the Rake tasks, etc. -- but leaves the actual font binaries out, with their location specified as a matter of configuration. This is consistent with 12-factor app principles, treating these webfonts as an external resource needed by but not contained within your application.
You could, for example, specify a WEBFONTS_URL environment variable, and add a fetch_webfonts Rake task. This task would automatically pull these resources into your application, and would do nothing in the case that WEBFONTS_URL is unspecified -- e.g. if someone else did a checkout of your repo without having these fonts. (You could even make the task do appropriate things with stylesheets, ensuring that the fresh-checkout users get a working app without 404ing on font files.) Rake allows you to add dependencies to existing tasks, so you can ensure this happens automatically as part of assets:precompile, which Capistrano very likely already invokes.
Additionally, this approach works equally well on development and production: you can take a fresh checkout of your own repo, specify the appropriate WEBFONTS_URL, and end up with a working set of fonts on your local machine. (The dotenv gem makes it easy to get development configuration into environment variables, keeping all your environments consistent.) Just be sure that you .gitignore the results so you don't accidentally make them public after all.
You could upload/synchronize the fonts directory using rsync. For example:
Add your IP/domain to a separate variable:
set :host, "ip_address"
server host, :web, :app, :db, primary: true
And add a task to run
`rsync -vz app/assets/webfonts #{user}##{host}:#{shared_path}/assets/webfonts`
Then symlink it into the current version of your application like is done already in the deploy script with database.yml and stripe.rb

Deploy Ruby on Rails project on EC2

I have setup everything and i am able to connect to the server via ssh on a free tier
now my project is how can i upload the server files and database?
I see that there are some gems in rails such as rubber that deploy the files to a server but how can i also integrate git so i can commit changes as well?
Code
For uploading the files, check out capistrano. Another popular tool is Vlad the deployer.
Here's a simple deploy.rb for a rails3 app (assuming passenger) that only needs one server right now (replace all the with actual values):
require 'bundler/capistrano'
require 'capistrano_colors'
set :application, "<APP_NAME>"
set :repository, "git#github.com:<USER>/<PROJECT>.git"
set :branch, "master"
set :deploy_to, "/home/<DEPLOY_USER>/<APP_NAME>"
set :keep_releases, 3
set :scm, :git
set :user, "<DEPLOY_USER>"
set :use_sudo, false
set :deploy_via, :remote_cache
default_run_options[:pty] = true
default_run_options[:shell] = '/bin/bash -l'
ssh_options[:forward_agent] = true
role :web, "<IP_OR_PUBLIC_DNS>"
role :app, "<IP_OR_PUBLIC_DNS>"
namespace :deploy do
task :start do ; end
task :stop do ; end
task :restart, :roles => :app, :except => { :no_release => true } do
run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
end
end
after "deploy", "deploy:cleanup"
Note: you should ssh-add the private keys for the deploy user as well as the key you use for github before running cap deploy. (e.g. ssh-add ~/.ssh/id_dsa)
Database
In terms of the database, you can run migrations as part of a deploy via cap deploy:migrations, or independently via cap deploy:migrate. Just be sure to list a server with role :db
role :db, "<IP_OR_PUBLIC_DNS>", :primary => true
(Note: this is the server that will be used to run the migrations, not the actual db. The db should be specified in your database.yml).
Setting up the database itself is beyond the scope of this answer, but you can either run a db directly on the instance, or take a look at Amazon's RDS, which is really simple to set up and more stable than a db running on a single instance.

Rails, Slicehost, Capistrano - Deployment port issues

I'm getting a port error when I try to deploy my app via:
cap deploy:cold
The error:
ssh: connect to host domain.com port 22: Connection refused
My deploy.rb (replaced sensitive info where appropriate):
set :user, 'user'
set :domain, 'domain.com'
set :application, "App Name"
# file paths
set :repository, "user#domain.com:git/appname.git"
set :port, 9728
set :deploy_to, "/home/ruby/public_html/appname"
# distribute your applications across servers (the instructions below put them)
# all on the same server, definied above as 'domain', adjust as necessary
role :web, domain
role :app, domain
role :db, domain, :primary => true
# you might need to set this if you aren't seeing password prompts
# default_run_options[:pty] = true
# As Capistrano executes in a non-interactive mode and therefore doesn't cause
# any of your shell profile scripts to be run, the following might be needed
# if (for example) you have locally installed gems or applications. Note:
# this needs to contain the full values for the variables set, not simply
# the deltas.
# miscellaneous options
set :deploy_via, :remote_cache
set :scm, :git
set :branch, 'master'
set :scm_verbose, true
set :use_sudo, false
# task which causes Passenger to initiate a restart
namespace :deploy do
task :restart do
run "touch #{current_path}/tmp/restart.txt"
end
end
# optional task to reconfigure databases
after "deploy:update_code", :configure_database
desc "copy database.yml into the current release path"
task :configure_database, :roles => :app do
do_config = "#{deploy_to}/config/database.yml"
run "cp #{db_config} #{release_path}/config/database.yml"
end
I understand the error, but can't see where to make the change. I figured :set port, would handle it.
Have you tried putting the port in your repository path ? Something like ssh://user#domain.com:9728/git/appname.git
I think Capistrano only uses the port you specify in set :port to log in your remote servers.
Here's what works: you must have
set :port, port#
set :repository, "ssh://#{user}#IPAddress:#{port}/~/path/to/git
Or also try:
Here's a possible fix. add ssh_options[:port] = port# May work
Sorry to state the obvious, but:
Is the port actually open on the server?
Have you changed it to something else?
Is it accessible from the outside?
Is your firewall blocking it?
please try it
server "domain:9728", :web, :app, :db

Capistrano and Git, Ruining my life. "Unable to resolve revision for [HEAD] on repository ..."

I searched all of the relevant Capistrano issues, but couldn't find something that even elucidated anything here for me.
git version 1.6.4.2
Capistrano v2.5.10
Basically, when I run my cap deploy.rb script, it connects to the server, starts executing the deploy:update task, then in the deploy:update_code task:
*** [deploy:update_code] rolling back
* executing "rm -rf /home/user_name/public_html/project_name/releases/20091223094358; true"
servers: ["project_name.com"]
It fails with the following error:
/Library/Ruby/Gems/1.8/gems/capistrano-2.5.10/lib/capistrano/recipes/deploy/scm/git.rb:231:in `query_revision': Unable to resolve revision for 'master' on repository 'ssh://git#slice_ip:path_to_git_repository'. (RuntimeError)
Here's my deploy script, I've tried including and omitting:
set :branch 'master'
I also just thought my path to the repository was off, but i've tried just about every permutation (absolute, not absolute, .git suffix, no suffix). There's definitely a bare git repository at the path i'm pointing to.
**I do have multiple projects being hosted on one slice. The other projects is also a rails project, but is running SVN. Capistrano deployments work fine.
Any pointers in the right direction or any ideas would help reduce the amount of drinking I am planning on doing if I can't figure this out. (Paths / IPs obfuscated, dont hack me bro!)
set :application, "project1"
set :user, "username"
set :repository, "ssh://git#67.24.9.133/home/git/project1.git"
set :branch, "master"
set :port, 696969
set :deploy_to, "/home/username/public_html/#{application}"
set :scm, :git
role :app, application
role :web, application
role :db, application, :primary => true
# deployment via remote client (workstation)
set :deploy_via, :copy
set :runner, user
# mod_rails
namespace :deploy do
desc "Restarting mod_rails with restart.txt"
task :restart, :roles => :app, :except => { :no_release => true } do
run "touch #{current_path}/tmp/restart.txt"
end
[:start, :stop].each do |t|
desc "#{t} task is a no-op with mod_rails"
task t, :roles => :app do ; end
end
end
This was the most relevant post (extremely relevant even), but I couldn't really figure out what they were saying the fix is. I'm pretty new with git / capistrano configs.
https://capistrano.lighthouseapp.com/projects/8716/tickets/56-query_revision-unable-to-resolve-revision-for-head-on-repository
Ok I seemed to have fixed it.
Basically, since I have 2 separate repositories on the remote server, I think the "git" user was failing because I hadn't registered an ssh keypair for the git user. That explains why one of my deploy.rb scripts was working properly, while this one wasn't.
In the link I posted in the question, one of the commenters pointed out the issue:
https://capistrano.lighthouseapp.com/projects/8716/tickets/56-query%5Frevision-unable-to-resolve-revision-for-head-on-repository
Note this error is also displayed if
you are using multiple github keys per
http://capistrano.lighthouseapp....
and you do not have these keys and a
corresponding entry in your
.ssh/config on the workstation you're
running the deploy from. so the
ls-remote is run locally. is there a
way to reference the repository at
github.com for this request while the
remote deploy uses
git#github-project1:user/project1.git
Also, see the following link for more details, since the whole ssh issue would apply even if you're not using github.
http://github.com/guides/multiple-github-accounts
Both your workstation and your server must be able to reach the repository at the address specified, if not then you may have to set :local_repository to how you access it from your workstaion, and :repository to be how your servers should access it.
For me Capistrano deployments with Git only seem to work when setting set :copy_cache, true
I've only used capistrano with git once, but never used or seen the use of ssh:// in the repository definition.
Try using set :repository, "git#67.24.9.133/home/git/project1.git" instead
Make sure the branch you are deploying from exists.
set :branch, "upgrade-to-2013.4.3"
is not equal to
set :branch, "upgrade-to-2013.3.4"

Why is Capistrano not checking out the latest version of my code from SVN?

I'm using Capistrano and Rails 2.3.4. I've already done a deploy:cold to the remote server. Now on my local box I changed a layout file and committed it to the repository (I am using Netbeans 6 as my IDE). I type cap deploy and Capistrano runs through it's commands and tells me that it's checked out and deployed the most recent version of my code. On the server, however, the changes aren't there and when I looked at the layout file, it was using the old version not the one I just committed and supposedly deployed.
Anyone experience this?
EDIT: The weird thing is that I changed some image files and those were updated on the server, but the HTML layout I modified was not. Could it just be a cookies issue?
EDIT2: I checked the repository itself (I am using ProjectLocker) and sure enough the code is in there, modified. The issue is only that Capistrano is NOT checking it out even though it says that it is, and it's not reporting any errors.
Here is my deploy.rb file (scrubbed, of course):
# Application
set :application, "myapp"
set :deploy_to, "/var/www/html/#{application}"
# Settings
default_run_options[:pty] = true
set :use_sudo, true
# Servers
set :user, "deploy"
set :domain, "111.111.111.111"
set :runner, "deploy"
server domain, :app, :web
role :db, domain, :primary => true
# SVN
set :repository, "http://myhosting.com/svn/myapp/trunk"
set :scm_username, "wayne#mysite.com"
set :scm_password, "secret"
set :checkout, "export"
# Passenger
namespace :passenger do
desc "Restart Application"
task :restart do
run "touch #{current_path}/tmp/restart.txt"
end
end
after :deploy, "passenger:restart"
It works sometimes, it seems. For instance I made some changes to code earlier and it checked it out fine. I had an issue with it not checking out my database.yml file either; I was forced to edit it on the server.
Just a shot in the dark here, but did you actually look at the layout file or did you hit the website and then look at the webpage source?
If you are in production the layout will be cached (config.action_controller.perform_caching = true), you need to reboot the server. This does not occur in development mode by default, since the above setting is set to false.
See this for more info.

Resources