So I just moved my hosting from Dreamhost to Site5. Thus far everything has been stellar. However, when I was looking over the forums to figure out how Capistrano fits at Site5, I noticed people doing things are slightly weird way (weird for the Rails world at least). It is probably due to this tutorial on how to get Rails apps working on Site5.
I can also say that I have the turbo charged plan, which has a different methodology for multi-site hosting and management. From the dates on the forums, the tutorial and most posts I saw could be doing things the “weird” way because they are on the regular hosting plan, etc.
So, on the turbo charged plan, here is a way you can set up your rails app so that Capistrano will work mostly with its defaults (without overriding a lot of variable values).
- Create a subdomain, for example app.jason.com
- In the public_html/app/ folder create a current directory.
- Edit the subdomain’s document root to include the new current directory.
- Now delete the current directory.
- Use Capistrano to deploy your app. It will create a current symlink where there used to be a current directory.
Now everything will just work. That is of course if you configured Capistrano correctly. But you can find plenty of other articles on how to do that.
So I tried Hobo for the first time today. I really like it. It will definitely help when I am creating things that need value added quickly. I look forward to using it more.
The documentation is a work in progress, but is already good enough to learn Hobo. With the release of Hobo 0.7.2 and Rails 2.0.2, some of the documentation is lagging, but if you are already versed in Ruby / Rails, you will figure it out quickly.
I wanted to fill everyone in on an issue I ran into. When I tried to create the Hobo application, it started to fail when creating the user model, which stopped the process, so many other things did not occur. It turns out that the default database on Rails 2.0.2 is SQLLite. I did not have the adapter installed which was the root of the problems. So you could install the adapter and fix the issues.
I opted to modify the hobo command file so it would accept the -d,—database switches that the rails command accepts. This will allow you to set you default database to whatever you like. Thus, the hobo command works again without installing SQLLite adapter.
Simply replace all the code in your hobo file with this code (if you are on Hobo 0.7.2). I found my file at: C:\ruby\lib\ruby\gems\1.8\gems\hobo-0.7.2\bin\hobo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
#!/usr/bin/env ruby
require 'fileutils'
Signal.trap("INT") { puts; exit }
hobo_src = File.join(File.dirname(__FILE__), "../hobo_files/plugin")
USAGE = "USAGE: hobo [ --user-model <model-name-or-false> ] [ --svn ] [--db] [-d or --database <database type>] [--hobo-src <path to hobo src>] <app-path>"
HOBO_REPO = "svn://hobocentral.net/hobo/trunk/hobo"
### Nasty stuff needed for Windows :-( ###
require 'rbconfig'
if Config::CONFIG["arch"] =~ /win32/
require "win32/registry"
def system(command)
win = Win32API.new("crtdll", "system", ['P'], 'L').Call(command)
end
end
### end nasty stuff ###
def command(*s)
ok = system(s.join(' '))
exit(1) unless ok
end
if ARGV.length == 0 || ARGV.include?("--help")
puts USAGE
exit 1
end
app_path = ARGV.pop
user_model = "user"
hobo_svn = create_db = false
until ARGV.empty?
case ARGV.shift
when "--user-model"
arg = ARGV.shift
user_model = arg == "false" ? nil : arg
when "--svn"
hobo_svn = true
when "-d", "--database"
arg = ARGV.shift
change_db = arg == nil ? false : true
default_db = arg unless !change_db
when "--db"
create_db = true
when "--hobo-src"
hobo_src = "../" + ARGV.shift
else
puts USAGE
exit 1
end
end
puts "\nGenerating Rails App...\n"
if change_db == true
puts "\n***Default database type is: #{default_db}...\n"
system("rails -d #{default_db} #{app_path}")
else
system("rails #{app_path}")
end
Dir.chdir(app_path) do
gen = "ruby #{File.join('script', 'generate')}"
plugin = "ruby #{File.join('script', 'plugin')}"
FileUtils.touch("public/stylesheets/application.css")
puts "\nInstalling classic_pagination\n"
command(plugin, "install svn://errtheblog.com/svn/plugins/classic_pagination")
if hobo_svn
puts "\nInstalling Hobo plugin via svn checkout...\n"
command("svn co #{HOBO_REPO} vendor/plugins/hobo")
else
puts "\nInstalling Hobo plugin...\n"
FileUtils.cp_r hobo_src, "vendor/plugins/hobo"
end
puts "\nInitialising Hobo...\n"
command(gen, "hobo --add-routes")
puts "\nInstalling Hobo Rapid and default theme...\n"
command("#{gen} hobo_rapid --import-tags")
if user_model
puts "\nCreating #{user_model} model and controller...\n"
command("#{gen} hobo_user_model #{user_model}")
command("#{gen} hobo_user_controller #{user_model}")
end
puts "\nCreating standard pages...\n"
command("#{gen} hobo_front_controller front --delete-index --add-routes")
if create_db
puts "\nCreating databases"
command("rake db:create:all")
end
end |
If you need to change yours manually, I just added to the usage string, one case to the case statement to handle the new switches and an if statement to testing if a different default database was specified.
Enjoy!
While I have been happy with Dreamhost's services, let's face it, deploying a Rails app to a shared Dreamhost server can be a rather daunting task. Even after I succeeded for the first time, I could not duplicate that success on a dependable basis. Then I found
Capistrano, many thanks to
Jamis Buck.
"Capistrano is a utility that can execute commands in parallel on multiple servers." It's original purpose was deploying web apps, but, it can do much more. However, within the scope of this article, we will explore its uses for deploying web apps to Dreamhost. Many of the techniques used in the article are just my preference for using Capistrano and can be set up differently for your own use.
For this example we will assume an application name of 'blog' and a domain name of 'example.com'. We will set up the app as a sub-domain of example.com.
The first order of business is to set up Capistrano on your machine. Look
here for instructions to do that.
Next we must 'Capistranize' your project on your development (local) machine. This step will add a deploy.rb (the deployment recipe) file to your app's config/ directory. It also adds capistrano.rake to your lib/tasks/ directory. Simply navigate to your project's root at a command line a type the following command. Don't overlook the period in the command, it is referring to the path to your app.
Now we will set up the new domain on Dreamhost. Go to the manage domains section of the Dreamhost panel and make your settings match
this. The key thing that we have done here that we would not do for a non-Capistrano deployment is point the web app to the blog.example.com/current/public/ directory. This current/ directory is actually a symlink that points to a real directory whose name is derived from the time the app was deployed. This real directory will be under the release/ directory, which is at the same level as the current symlink in the directory structure. This will allow us to keep several deployments in the directory and point to the current deployment using the current symlink.
Once you hae saved the new domain at the Dreamhost control panel, you will need to ssh into your Dreamhost account and delete the real current directory that was created for you by the panel. If you do not do this, Capistrano will not be able to create a symlink due to a naming conflict. To delete the real directory, navigate to your apps root directory and issue a recursive remove command, as there is also a public directory underneath the current directory.
Now we are going to set up our deployment recipe. Open the deploy.rb file. You will notice that the syntax is just pure Ruby syntax. The first set of variables we will configure is required for every deployment recipe.
1
2
3
4
5
|
set :user, "bloguser"
set :application, "blog.example.com"
set :repository, "http://svn.example.org/mephisto/trunk"
set :svn_username, "bloguser"
set(:svn_password) |
We will set the user to 'bloguser' because we specified this user when we set up the apps domain in the Dreamhost panel. You can use whatever user you want to as long as it has access to read, write and delete in and below the blog apps root directory. We also configured the name of the app, the subversion repository URL and user name, and told Capistrano to prompt for the subversion password. It is important to note that Capistrano is going to deploy from the repository, not our development machine, so you must have all code you want deployed committed prior to actually trying to deploy.
The next section of variables we will configure is the roles. You can set up any number of roles that your configuration will require. According to the Capistrano manual, "You can define whatever roles you want, but the default Capistrano tasks look for those three: :app, :web, and :db. The :app role describes which servers are acting as the application servers (the servers running the FastCGI instances). The :web role describes the servers running Apache, and the :db role describes the servers running your database(s)."
1
2
3
|
role :web, "#{application}"
role :app, "#{application}"
role :db, "#{application}", :primary => true |
On Dremhost shared hosting, all of our roles are on the same machine and that machine has the same name as the app, so I reuse the application variable.
The next section of variables are optional, as each of them has a default value if not specified here.
1
2
3
4
5
|
set :user_root, "/home/#{user}"
set :deploy_to, "#{user_root}/#{application}"
set :use_sudo, false
set :checkout, "export"
set :restart_via, :run |
The user root variable is the path to where the workspace for your applications user is. For us, it will be 'home/bloguser/'. So we reuse the user variable to keep our configuration DRY. The deploy to variable is the directory where we want to deploy our application. due to our choices in creating the domain at Dreamhost, the folder name the app will live in is that same as the application variable, so we will reuse it. The user sudo variable let's Capistano know if it should use the sudo utility to perform admin functions, which we cannot do at Dreamhost. the Checkout variable lets Capistrano know whether to check out or export from the repository. Of course, an export will save disk space and bandwidth.
Next comes the user variables section where you can define variable specific for tasks that you add to the deployment.
1
2
3
4
5
6
|
set :db_adapter, "mysql"
set :db_host, "mysql.#{application}"
set :db_name, "blog"
set :db_dev_postfix, ""
set :db_test_postfix, "_test"
set :db_prod_postfix, "" |
We are going to set up variables that will aid us in dynamically creating a database.yml file at deployment time, an idea I got from
Jeremy Voorhis. Using a standard naming convention for your web apps and resources at Dreamhost will help make your life easier. I always name the MySQL host mysql.[application name]. This is reflected in the db host variable. The db name variable is the root name of your database, while the postfix variables are the postfix for each environment's database. Because my production and development database are always on separate machines, I just name them without a post fix. Only my test database has a postfix.
Next we define custom operations using the after_update_code task that Capistrano calls by default.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
desc "Tasks to execute after code update"
task :after_update_code, :roles => [:app, :db, :web] do
# update the...
run "cp #{user_root}/_resources/.htaccess #{release_path}/public/.htaccess"
run "cp #{user_root}/_resources/dispatch.fcgi #{release_path}/public/dispatch.fcgi"
#put the...
run "cp -r #{user_root}/_resources/tmp #{release_path}/tmp"
# delete the..
#run "rm #{release_path}/.project"
# fix permissions...
run "chmod +x #{release_path}/script/process/reaper"
run "chmod +x #{release_path}/script/process/spawner"
run "chmod 755 #{release_path}/public/dispatch.*"
database_yml_change
end |
Here, I am copying the .htaccess and dispatch.fcgi files that I have stored in the _resources/ directory that is located in my user root at Dreamhost. I have to do this because I develop on a windows machine. We also copy the tmp/ directory from this same location. I do not like to source control it so that I do not have to keep ignoring files, etc. I also delete the .project file that Eclipse IDE creates. I like to source control this so that I back up how I have my project set up. You can do this same thing with any IDE files you may have or just comment it out. Next we fix the permissions on several files. Last we call the database_yml_change task, which is a custom task we created and will review later in this article.
Next we touch the dispatch.fcgi files after a deploy and after a rollback. This helps the app to restart on Dreamhost. I have sometimes run into trouble where it takes an app a little while to restart and this seems to help minimize this. I have yet to figure out how to get the reaper or spawner scripts to work correctly, so this is the next best thing.
1
2
3
4
5
6
7
8
9
|
desc "Restarting after deployment"
task :after_deploy, :roles => [:app, :db, :web] do
run "touch /home/#{user}/#{application}/current/public/dispatch.fcgi"
end
desc "Restarting after rollback"
task :after_rollback, :roles => [:app, :db, :web] do
run "touch /home/#{user}/#{application}/current/public/dispatch.fcgi"
end |
In the last part of the deployment recipe we create the database.yml file dynamically from out settings and put it into the shared/config/ directory, as all versions of the app that we roll out can share this file. We then create a database.yml symlink in the current/config/ folder that points to the shared/config/database.yml file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
desc "Create database.yml in shared/config"
task :database_yml_change do
# remove the dev version of database.yml
run "rm #{release_path}/config/database.yml"
database_configuration = render :template => <<-EOF
login: &login
adapter: <%= db_adapter %>
host: <%= db_host %>
username: <%= user %>
password: <%= password %>
development:
database: <%= "#{db_name}#{db_dev_postfix}" %>
<<: *login
test:
database: <%= "#{db_name}#{db_test_postfix}" %>
<<: *login
production:
database: <%= "#{db_name}#{db_prod_postfix}" %>
<<: *login
EOF
run "mkdir -p #{deploy_to}/#{shared_dir}/config"
put database_configuration, "#{deploy_to}/#{shared_dir}/config/database.yml"
# link to the database.yml
run "ln -nfs #{deploy_to}/#{shared_dir}/config/database.yml #{release_path}/config/database.yml"
end |
The next thig we need do is set up the directory strucure on the Dreamhost side. Luckily, Capistrano provides a rake task to do this that will now work as we have configured everything it needs. Simply navigate to the root of your app on your development machine an enter this command.
1
2
|
rake remote:exec ACTION=setup #deprecated
cap setup #this is the newer way |
This will create the releases/ and shared/ directories under the blog.example.com/ directory for our example.
The last thing you must do to start your first deployment is type this command on your development machine (don't forget to commit everything you expect to deploy).
If everything was set up correctly, your app will be deployed to Dreamhost and you should be able to browse to it. The app may delay due to starting up. In addition, you have created a repeatable process that is dependable to roll out all of your changes and even roll back if you create an issue with a deployment.
Here is the full deploy.rb file from this article.