PHP : Deploy Yii app using capistrano

24 Jun 2015

In my last blog post we setup server for capistrano deployment. In this, we will setup the Yii project for capistano deployment.

Setting up Yii project for cap deployment

Yii uses different config files for cli app (console.php) and web app (main.php). We need to unify the common configuration into one file. So we will create a config.php and require it in both main.php and console.php.

<?php
# protected/config/config.php
$config = array();

$db_name = 'sample';
$db_host = 'localhost';
$db_user = 'root';
$db_pass = '';

$config = array(
  'adminEmail' => 'admin@example.com',
  'statusEmails' => array('admin@example.com', 'team@example.com'),
  'accessToken' => '',
);

?>

$config is used for custom configuration values. Since I don’t want git to show configuration file as changes during development, I renamed config.php to config.php.example and added config.php to .gitignore. So when I start development I will copy config.php.example to config.php and fill in the local configuration values.

Now add .gitkeep file to assets/ and protected/runtime/ folder and add to git. This will make sure the folders will exist on server when capistrano pull code from git repository.

Configure capistrano

Next step is to configure capistrano for the deployment. If you don’t have ruby and capistrano in you local machine install those by following the same ruby installation steps we used for seting up server.

apt-get install -y build-essential git-core libyaml-dev 

gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -L get.rvm.io | bash -s stable

source /home/lookup/.rvm/scripts/rvm
rvm reload
rvm install 2.2.2

gem install capistrano --no-ri --no-rdoc

Now Initialize capistrano in the project directory, to generate the configuration files.

cap init

It will create a config directory in project root folder along with configuration files. In config/deploy.rb update the appropriate values for :application, :repo_url and :deploy_to settings.

set :application, 'project'
set :repo_url, 'git@github.com:example/project.git'

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, '/var/www/example'

The default values for linked_directories will be set for rails, so we remove those and set values for Yii application.

# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push('assets/','protected/runtime')

Now we need to specify the production and staging server in the appropriate config file. In config/deploy/production.rb set the production server ip or domain name.

server 'example.com', user: 'deploy', roles: %w{app}

role :app, %w{deploy@example.com}

and for staging, update config/deploy/staging.rb same as we did for production.

server 'staging.example.com', user: 'deploy', roles: %w{app}

role :app, %w{deploy@staging.example.com}

deploy is the user we created on server while setting up.

The basic setup for the deployment of our Yii app is ready. but thats not enough we need to update the production and staging configuration on respective server along with deployments.

In rails we used to put the configuration in the shared folder and symlink to current deployment folder. This doesn’t work in Yii application. It will throw an path error if you try to do so. So we need to upload the production configuration directly to current deployment folder.

Custom rake task to upload configuration.

For uploading production and staging configuration first we need them separately in our development machine. I put my production configuration in protected/config/config-production.php and added to .gitignore so I won’t add those to git by mistake. Same way for staging I put configuration in protected/config/config-staging.php and added to .gitignore.

In order to upload the configuration, we need to write the custom rake task. The cap init command have created lib/capistrano/tasks folder for it.

# lib/capistrano/tasks/custom.rake
namespace :db_access do
  desc 'Copy production config.php from local workstation'
  task :copy_production => :production do
    on roles :app do
      upload! 'protected/config/config-production.php', "#{release_path}/protected/config/config.php"
    end
  end

  task :copy_staging => :staging do
    on roles :app do
      upload! 'protected/config/config-staging.php', "#{release_path}/protected/config/config.php"
    end
  end
end

This task will upload our configuration to appropriate serves into the path /protected/config/config.php. Now we need to tell capistrano to run this task after deployment is finished. To do this in config/deploy/production.rb add the following line

after 'deploy:finishing', 'db_access:copy_production'

and for staging add the following line in config/deploy/staging.rb.

after 'deploy:finishing', 'db_access:copy_staging'

Run Database migration

So now we have the production configuration along with db credentials are in place. Now it time for database migration. Yii supports running migration from commandline using yiic migrate command. Now we need to run it on server using a custom rake task. so we will add

  task :migrate do
    on roles :app do
      execute "#{release_path}/protected/yiic migrate --interactive=0"
    end
  end

in lib/capistrano/tasks/custom.rake with in :db_access namespace. now our custom.rake will look like

# lib/capistrano/tasks/custom.rake
namespace :db_access do
  desc 'Copy production config.php from local workstation'
  task :copy_production => :production do
    on roles :app do
      upload! 'protected/config/config-production.php', "#{release_path}/protected/config/config.php"
    end
  end

  task :copy_staging => :staging do
    on roles :app do
      upload! 'protected/config/config-staging.php', "#{release_path}/protected/config/config.php"
    end
  end

  task :migrate do
    on roles :app do
      execute "#{release_path}/protected/yiic migrate --interactive=0"
    end
  end
end

So our task is ready. Now we need to run soon after our configuration uploading of respective server finishes. To do that in production server add

after 'deploy:copy_production', 'db_access:migrate'

in config/deploy/production.rb and for staging server add

after 'deploy:copy_staging', 'db_access:migrate'

in config/deploy/staging.rb.

Done. We just finished the whole setup and ready to deploy our Yii application with capistrano. Now you can run

cap production deploy

for deploying production server and

cap staging deploy

for deploying application in staging server.

If you find my work helpful, You can buy me a coffee.