Deploying CakePHP with Capistrano
I was looking for a good deployment solution for my CakePHP apps and as nothing seems to fit perfectly, Capistrano does a very good job at keeping it simple. My setup is using a Git repository but almost SCM all supported by cap. Here is a basic guide into Capistrano setup configuration for CakePHP.
First install and/or update your Ruby and Capistrano (ruby -v, cap -V). Get to know the basics of Capistrano at http://www.capify.org/index.php/From_The_Beginning. I only describe here how to configure a basic recipe for CakePHP.
This creates a few files. The important one which I'll explain how to configure for CakePHP is config/deploy.rb.
You must set a few configuration first :
Your server(s) with SSH access :
Custom environments shortcut task (optional). It executes tasks and/or change configurations for a specific deploy. In my example, you can use "cap production deploy" which will deploy "production" branch on www and run CakePHP tests. You can also use "cap staging deploy" which will prompt for which branch to deploy and run cake tests. Default "cap deploy", on dev., does not run the tests (you may change all this, its just an example).
There is a default set of tasks executed on deploy, but we can add a few to the chain using before or after functions. I have set web:disable and web:enable which will create a temporary .capistrano-lock file for wich my CakePHP app will detect and display the maintenance page to users. Cleanup is to remove old releases (not executed by default as it would be an unexpected default behaviour).
The tasks I configured :
Put all this code in your deploy.rb and you can now run "cap deploy:setup" once to init the Capistrano structure on the server.
Then point your document root to /home/user/www.exemple.com/current/app/webroot (which is an alias-symlink to /home/user/www.exemple.com/releases/20080819001122/app/webroot).
cd /your/project/root
mkdir config
capify .
This creates a few files. The important one which I'll explain how to configure for CakePHP is config/deploy.rb.
You must set a few configuration first :
set :application, "example.com"
set :repository, "ssh://git@repo.repo.com/repo/repo.git"
set :branch, "master"
set :scm, :git
set :deploy_to, "/home/user/domains/dev.#{application}"
set :deploy_via, :remote_cache # see http://www.capify.org/index.php/Understanding_Deployment_Strategies
# Configuration of you app path in the repo
set :cakephp_app_path, "public_html/app"
set :cakephp_core_path, "public_html/cake"
# Nice optional configurations
set :use_sudo, false # don't need this on most setup
set :keep_releases, 10 # only keep 10 version to save space
set :copy_exclude, [".git",".gitignore"] # or any match like [".svn","/documents-on-repo-but-dont-deploy"]
Your server(s) with SSH access :
# Roles
role :app, "user@exemple.com:12345"
Custom environments shortcut task (optional). It executes tasks and/or change configurations for a specific deploy. In my example, you can use "cap production deploy" which will deploy "production" branch on www and run CakePHP tests. You can also use "cap staging deploy" which will prompt for which branch to deploy and run cake tests. Default "cap deploy", on dev., does not run the tests (you may change all this, its just an example).
# Environements
task :production do
set :deploy_to, "/home/user/domains/www.#{application}/"
set :branch, "production"
after "deploy:finalize_update", "deploy:cakephp:testsuite"
end
task :staging do
set :deploy_to, "/home/user/domains/stating.#{application}/"
set(:branch) { Capistrano::CLI.ui.ask("Branch to stage: ") }
after "deploy:finalize_update", "deploy:cakephp:testsuite"
end
There is a default set of tasks executed on deploy, but we can add a few to the chain using before or after functions. I have set web:disable and web:enable which will create a temporary .capistrano-lock file for wich my CakePHP app will detect and display the maintenance page to users. Cleanup is to remove old releases (not executed by default as it would be an unexpected default behaviour).
# Custom events configuration
before "deploy:update", "deploy:web:disable"
after "deploy:restart", "deploy:web:enable"
after "deploy:update", "deploy:cleanup"
The tasks I configured :
# Custom deployment tasks
namespace :deploy do
desc "This is here to overide the original :restart"
task :restart, :roles => :app do
# do nothing but overide the default because we don't need to restart a RoR app
end
task :finalize_update, :roles => :app do
# link a custom configuration file for environment specifics
run "ln -s #{deploy_to}/#{shared_dir}/bootstrap.remote.php #{release_path}/#{cakephp_app_path}/config/bootstrap.remote.php"
# you may link here upload file folders if you have any, which should be placed in #{deploy_to}/#{shared_dir} which won't be overide on each deployment
# overide the rest of the default method
end
namespace :web do
desc "Lock the current access during deployment"
task :disable, :roles => :app do
run "touch #{current_release}/#{cakephp_app_path}/webroot/.capistrano-lock"
end
desc "Enable the current access after deployment"
task :enable, :roles => :app do
run "rm #{current_release}/#{cakephp_app_path}/webroot/.capistrano-lock"
end
end
namespace :cakephp do
desc "Verify CakePHP TestSuite pass"
task :testsuite, :roles => :app do
run "#{release_path}/#{cakephp_cake_path}/console/cake testsuite app all -app #{release_path}/#{cakephp_app_path}", :env => { :TERM => "linux" } do |channel, stream, data|
if stream == :err then
error = CommandError.new("CakePHP TestSuite failed")
raise error
else
puts data
end
end
end
end
end
Put all this code in your deploy.rb and you can now run "cap deploy:setup" once to init the Capistrano structure on the server.
Then point your document root to /home/user/www.exemple.com/current/app/webroot (which is an alias-symlink to /home/user/www.exemple.com/releases/20080819001122/app/webroot).

capistrano is great: i really like the simplicity. One major advantage is, you can easily extend the script to deploy an application to a serverfarm.
I built a ready-configured space on assembla.com for using their deployment-tool that utilizes capistrano deployments. My deployment is auto-setup and works with instances on amazon ec2, that are preconfigured.
I presented this deploy-ready solution on the cakefest 2009 in Berlin.
Here is a list of all spaces with cakephp so far (currently only SVN, git is in preparation):
http://www.assembla.com/catalog/tag/cakephp?affiliate=d1rk
Note: This link is an affiliate-link (thank you).
You can get more information on how to setup everything in the space-wiki. After setup everything works right out of the box. You can have a look at the deployment script here:
https://www.assembla.com/code/cakephp_svn/subversion/nodes/trunk/deploy/deploy.rb?affiliate=d1rk
And, of you course, you can change and adapt the deployment to your specific project needs.
best regards
One thing, though. Your article relies on CakePHP being part of your repo. I don't like this at all. The "core" has has it's own repo on github I prefer to keep my repo to just the app directory.
For those who feel the same, there is a nice script on github called capcake. Among other things, it helps with separate Cake from your app by putting Cake in shared and downloading it from a separate repo (like the official one or your own fork).
[url= http://github.com/jadb/capcake] [url]http://github.com/jadb/capcake
I'll add a var for the cake path as it may indeed be anywhere on the repo.