Deploying symfony project with Capistrano
May 12th, 2008
As you may know Capistrano is a great tool "originally written to ease the pain of deploying Rails applications". If you are not familiar with it take a look at it's getting started guide.
Even if Rails applications development is the primary way that people use Capistrano it can be used for a lot more than that.
Let me show you how you can use it for easy-and-with-no-pain deployment of symfony project.
First cd to your project and run (I assume that you have Capistrano installed allready)
$ capify . |
So this is how the modifed deploy.rb file that I use to deploy my symfony projects looks like :
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
set :application, "mysfapp" set :repository, "http://path/to/the/version/control/repository" # If you aren't deploying to /u/apps/#{application} on the target # servers (which is the default), you can specify the actual location # via the :deploy_to variable: set :deploy_to, "/path/to/project/#{application}" # If there's no access to the repository from the production server, deploy via uploading tarball to the server #set :deploy_via, :copy # If you aren't using Subversion to manage your source code, specify # your SCM below: # set :scm, :subversion role :app, "codingspree.net" role :web, "codingspree.net" # role :db, "your db-server here", :primary => true set :user, "codingspree" # path to php executable set :php, "/usr/local/php5/bin/php5" # symfony application name (used for migrations) set :sf_app, "frontend" namespace (:deploy) do desc <<-DESC [internal] Overriding original task to fit to symfony project needs DESC task :finalize_update, :except => { :no_release => true } do run "chmod -R g+w #{latest_release}" if fetch(:group_writable, true) run <<-CMD rm -rf #{latest_release}/log && ln -s #{shared_path}/log #{latest_release}/log CMD run <<-CMD rm -rf #{latest_release}/cache && ln -s #{shared_path}/cache #{latest_release}/cache CMD stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S") asset_paths = %w(images css js).map { |p| "#{latest_release}/web/#{p}" }.join(" ") run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" } end desc <<-DESC Overriding original task to exclude restart DESC task :default do update end desc <<-DESC Overriding original task to use symfoy migrations (via sfMigrationsLightPlugin) DESC task :migrations do update sf.migrate end after "deploy:update", 'deploy:customize' desc <<-DESC All custom tasks will be here DESC task :customize do # custmize it here sf.symlinks sf.remove_dev_environments # clear cache sf.cc end end namespace (:sf) do desc <<-DESC Run the "symfony migrate" task DESC task :migrate do run "cd #{current_path} && #{php} symfony migrate #{sf_app}" end desc <<-DESC Run the "symfony cc" task DESC task :cc do run "cd #{current_path} && rm -rf cache/*" end desc <<-DESC Create symlink to symfony specific targets DESC task :symlinks do # symlink to database.yml run "rm -rf #{current_path}/config/databases.yml" run "ln -s #{shared_path}/databases.yml #{current_path}/config/databases.yml" # symlink to config.php run "rm -rf #{current_path}/config/config.php" run "ln -s #{shared_path}/config.php #{current_path}/config/config.php" # symlink to sf data dir run "rm -rf #{current_path}/web/sf" run "ln -s /path/to/sf/data/dir #{current_path}/web/sf" # symlink to uploads run "rm -rf #{current_path}/web/uploads" run "ln -s #{shared_path}/uploads #{current_path}/web/uploads" end desc <<-DESC Remove DEV environments DESC task :remove_dev_environments do run "rm -rf #{current_path}/web/*_dev.php" end desc <<-DESC Disable symfony application DESC task :disable do run "cd #{current_path} && #{php} symfony disable #{sf_app} prod" end desc <<-DESC Enable symfony application DESC task :enable do run "cd #{current_path} && #{php} symfony enable #{sf_app} prod" end end |
1 2 |
# path to php executable set :php, "/usr/local/php5/bin/php5" |
1 2 |
# symfony application name (used for migrations) set :sf_app, "frontend" |
In the next section of the deploy.rb file I overrided few of the original tasks from deploy namespace to fit to my needs - finalize_update to set symlinks to symfony cache and log directories, the default task to exclude restart because I don't need it, migrations to set it to use symfony migrations and at the end there is one hook task that will be executed after deploy:update -it's called customize and I'll place all custom tasks there.
The last part of the file is sf namespace that includes some symfony related tasks as migrate, cc, symlinks, remove_dev_environments, disable, enable and so on. As you see you can wrap any symfony task that you have to execute on the production server with Capistrano task.
All you need now is to set a little bit the production server (run cap deploy:config, symlink document root to /path/to/project/mysfapp/current, place relevant database.yml and config.php in /path/to/project/mysfapp/shared, etc) and you are ready to run
$ cap deploy |
0 comments »
See ya @ EuRuKo 2008
March 20th, 2008
This year's portion of beer and ruby is waiting me in Prague at euruko2008.org
See ya there ;)
0 comments »
How to generate urls for ActionMailer emails
November 13th, 2007
This is a little bit tricky, but I've found an solution in the comments from other article on this problem.
First we need to set default_url_options for ActionMailer::Base. This can be done with before_filter method in ApplicationController
1 2 3 4 5 6 7 8 |
class ApplicationController < ActionController::Base before_filter :set_default_url_options_for_mailers def set_default_url_options_for_mailers ActionMailer::Base.default_url_options[:host] = request.host_with_port end end |
1 2 3 4 5 6 7 8 9 |
def email_confirmation(user) recipients user.email from "example.com <no-reply@example.com>" subject "Confirm your email address" content_type "text/html" body :user => user, :activation_page => url_for(:controller => "main", :action => 'activate', :only_path => false) end |
1 comments »
Adding callbacks for Propel models in symfony project
September 11th, 2007
This is a simple how-to that describes adding callbacks for Propel models in symfony project
(like those usefull methods in ActiveRecord::Callbacks module).
Step 1 : Enable Propel behaviors in config/propel.ini
propel.builder.AddBehaviors = true // Default value is false |
1 2 3 4 5 6 7 8 9 |
class PageTab extends BasePageTab
{
function preSave(BaseObject $object, $con)
{
}
}
sfMixer::register('BasePageTab:save:pre', array('PageTab', 'preSave')); |
$ symfony propel-build-model |
There is much more detailed article on this topic by François Zaninotto called "Understanding Behaviors"
0 comments »
"Skinny Controller, Fat Model" approach in symfony project
September 10th, 2007
A very common error when you first start to write an application in the MVC way is bypassing some of the components or just not using it as it should.
This problem and an example how to do it in the "right way" are described in a one of Jamis Buck's posts called Skinny Controller, Fat Model.
Here is a cover-up version of the code from that post ported to symfony/PHP...
Before (or how-NOT-to-do-it example) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<!-- listSuccess.php -->
<?php
foreach ($path as $breadcrumb=>$name)
{
?>
<a href="<?=url_for($breadcrumb)?>"><?=__($name)?></a> /
<?
}
foreach ($people as $p)
{
?>
<div id="person-<?=$p->getId()?>">
<span class="name">
<?=$p->getLastName()?>, <?=$p->getFirstName()?>
</span>
<span class="age">
<?= (date('Y') - date('Y', strtotime($p->getBirthdate()))) ?>
</span>
</div>
<?
}
?> |
1 2 3 4 5 6 7 8 9 10 11 12 |
# actions.php
class personsActions extends sfActions
{
public function executeList()
{
$c = new Criteria;
$c->add(PersonPeer::ADDED_AT, date('Y-m-d'), Criteria::GREATER_EQUAL);
$c->add(PersonPeer::DELETED, false);
$this->people = Person::doSelect($c);
}
} |
1 2 3 4 |
# PersonPeer.php
class PersonPeer extends BasePersonPeer
{
} |
1 2 3 4 5 6 7 8 9 |
<!-- listSuccess.php -->
<?php echo breadcrumbs($path) ?>
<?php foreach ($people as $p): ?>
<div id="person-<?php echo $p->getId()?>">
<span class="name"><?php echo $p->getFullName()?></span>
<span class="age"><?php echo $p->getAge()?></span>
</div>
<? endforeach;?> |
1 2 3 4 5 6 7 8 |
# actions.php
class personsActions extends sfActions
{
public function executeList()
{
$this->people = PersonPeer::findRecent();
}
} |
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 |
# PersonPeer.php
class PersonPeer extends BasePersonPeer
{
public function findRecent()
{
$c = new Criteria;
$c->add(self::ADDED_AT, date('Y-m-d'), Criteria::GREATER_EQUAL);
$c->add(self::DELETED, false);
return self::doSelect($c);
}
}
# Person.php
class Person extends BasePerson
{
public function getFullName()
{
return $this->getLastName() . ', ' . $p->getFirstName();
}
public function getAge()
{
return (date('Y') - date('Y', strtotime($this->getBirthdate())));
}
} |
0 comments »
Eclipse Regular Expression Tester
August 28th, 2007
Today, while searching for online regular expressions tester I've found this very nice plugin - Eclipse Regular Expression Tester.
Give it a try!
0 comments »
Presentation: Applying Agile to Ruby
August 7th, 2007
Another great talk on infoq.com. By Fred George.
0 comments »
Presentation: The Beauty of Ruby
July 9th, 2007
In this talk Glenn Vanderburg demonstrates some of the subtle beauty that experienced Rubyists know and love.
0 comments »

Hi, my name is Krasimir Angelov. I'm working as web developer for