├── Capfile ├── Gemfile ├── README.md ├── config ├── deploy.rb └── deploy │ └── production.rb └── lib └── capistrano └── tasks ├── assets_precompile.rake ├── config_files.rake ├── maintenance.rake └── restart.rake /Capfile: -------------------------------------------------------------------------------- 1 | # Load DSL and set up stages 2 | require 'capistrano/setup' 3 | 4 | # Include default deployment tasks 5 | require 'capistrano/deploy' 6 | 7 | # Include tasks from other gems included in your Gemfile 8 | # 9 | # For documentation on these, see for example: 10 | # 11 | # https://github.com/capistrano/rvm 12 | # https://github.com/capistrano/rbenv 13 | # https://github.com/capistrano/chruby 14 | # https://github.com/capistrano/bundler 15 | # https://github.com/capistrano/rails 16 | # https://github.com/capistrano/passenger 17 | # 18 | require 'capistrano/rvm' 19 | # require 'capistrano/rbenv' 20 | # require 'capistrano/chruby' 21 | #require 'capistrano/bundler' 22 | require 'capistrano/rails/assets' 23 | require 'capistrano/rails/migrations' 24 | #require 'capistrano/passenger' 25 | #require 'capistrano/touch-linked-files' 26 | #require 'capistrano/upload-config' 27 | #require "capistrano/rsync" 28 | 29 | 30 | # Load custom tasks from `lib/capistrano/tasks' if you have any defined 31 | Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } 32 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby "2.0.0" 4 | 5 | gem 'rails', '4.1.6' 6 | 7 | ... 8 | 9 | group :development do 10 | gem 'capistrano', '~> 3.1' 11 | gem 'capistrano-rails', '~> 1.1' 12 | #gem 'capistrano-bundler', '~> 1.1' 13 | gem 'capistrano-rvm', '~> 0.1' 14 | 15 | end 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | deploy rails app with capistrano3 2 | ======================= 3 | 4 | Example of deploying Rails application to a Linux server with Nginx+Passenger. 5 | 6 | Find all necessary files in the repository and read about features below: 7 | 8 | * [Setup](#setup) 9 | * [Restart application](#restart) 10 | * [Restart Passenger](#restart-passenger) 11 | 12 | * [Linked files, dirs](#linked) 13 | * [Config files](#config_files) 14 | 15 | * [Precompile assets](#assets-precompile) 16 | * [Skip precompile assets](#assets-precompile-skip) 17 | * [Precompile assets only if files were changed](#assets-precompile-changes) 18 | * [Precompile assets locally](#assets-precompile-locally) 19 | 20 | * [Show maintenance page while deploying](#maintenance-page) 21 | 22 | * [Delete old repos](#delete-old-repos) 23 | 24 | * [Deploy](#deploy) 25 | 26 | 27 | 28 | # Capistrano 3 29 | 30 | ## Initialize Capistrano 31 | 32 | Gemfile 33 | ```ruby 34 | group :development do 35 | gem 'capistrano', '~> 3.1' 36 | gem 'capistrano-rails', '~> 1.1' 37 | ... 38 | end 39 | 40 | ``` 41 | 42 | Run the command 43 | ```bash 44 | cap install 45 | ``` 46 | 47 | This command will create files: 48 | ``` 49 | Capfile 50 | config/deploy.rb 51 | config/deploy/production.rb 52 | config/deploy/staging.rb 53 | lib/capistrano/tasks # directory 54 | ``` 55 | 56 | 57 | 58 | ## Setup 59 | 60 | Gemfile 61 | ```ruby 62 | group :development do 63 | gem 'capistrano', '~> 3.1' 64 | gem 'capistrano-rails', '~> 1.1' 65 | #gem 'capistrano-bundler', '~> 1.1' 66 | gem 'capistrano-rvm', '~> 0.1' 67 | 68 | end 69 | 70 | ``` 71 | 72 | 73 | Capfile 74 | ```ruby 75 | require 'capistrano/rvm' 76 | # require 'capistrano/rbenv' 77 | # require 'capistrano/chruby' 78 | #require 'capistrano/bundler' 79 | require 'capistrano/rails/assets' 80 | require 'capistrano/rails/migrations' 81 | ``` 82 | 83 | 84 | 85 | ## Restart server 86 | 87 | 88 | 89 | ### Passenger 90 | 91 | Restart application after deploy: 92 | 93 | ```ruby 94 | namespace :deploy do 95 | desc 'Restart application' 96 | task :restart do 97 | on roles(:app), in: :sequence, wait: 5 do 98 | execute :touch, release_path.join('tmp/restart.txt') 99 | end 100 | end 101 | after :publishing, :restart 102 | end 103 | 104 | ``` 105 | 106 | You can run this command manually to restart the app 107 | ``` 108 | cap production deploy:restart 109 | ``` 110 | 111 | This gem provides more functionality on working with Passenger: https://github.com/capistrano/passenger 112 | 113 | 114 | 115 | ## Linked files, dirs 116 | 117 | Some of the files should be stored in shared folder and shared across all releases. 118 | File from the 'linked_files' list are symlinked to files in shared folder. 119 | 120 | For example, you may want to store users' uploaded files in public/uploads directory. This directory should not new in each release after deploy. 121 | 122 | ```ruby 123 | set :linked_dirs, fetch(:linked_dirs, []).push('bin', 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system') 124 | set :linked_dirs, fetch(:linked_dirs) + %w{public/uploads} 125 | 126 | set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/secrets.yml') 127 | 128 | ``` 129 | 130 | Note! You need to copy linked files to the server before deploy _manually_. The files are not copied to the shared folder by Capistrano. 131 | 132 | 133 | 134 | ## Config files 135 | 136 | Task to copy files to shared directory: 137 | 138 | ```ruby 139 | 140 | set :config_dirs, %W{config config/environments/#{fetch(:stage)} public/uploads} 141 | set :config_files, %w{config/database.yml config/secrets.yml} 142 | 143 | 144 | namespace :deploy do 145 | desc 'Copy files from application to shared directory' 146 | ## copy the files to the shared directories 147 | task :copy_config_files do 148 | on roles(:app) do 149 | # create dirs 150 | fetch(:config_dirs).each do |dirname| 151 | path = File.join shared_path, dirname 152 | execute "mkdir -p #{path}" 153 | end 154 | 155 | # copy config files 156 | fetch(:config_files).each do |filename| 157 | remote_path = File.join shared_path, filename 158 | upload! filename, remote_path 159 | end 160 | 161 | end 162 | end 163 | end 164 | 165 | ``` 166 | 167 | 168 | run the command: 169 | ``` 170 | cap production deploy:copy_config_files 171 | ``` 172 | 173 | 174 | 175 | 176 | ## Precompile assets 177 | 178 | 179 | By default assets are precompiled every time during the deploy process. 180 | rake assets:precompile can take up a noticable amount of time of a deploy. 181 | 182 | Run the task manually: 183 | ``` 184 | cap production deploy:assets:precompile 185 | ``` 186 | 187 | 188 | 189 | ### Skip Precompile assets 190 | 191 | to skip assets precompilation during deploy add this line to deploy.rb: 192 | 193 | ``` 194 | Rake::Task["deploy:assets:precompile"].clear_actions 195 | ``` 196 | 197 | * if we want to skip it only sometimes, we can add env variable: 198 | 199 | ``` 200 | if ENV['skip_assets']=='1' 201 | Rake::Task["deploy:assets:precompile"].clear_actions 202 | end 203 | ``` 204 | 205 | and run it as: 206 | ``` 207 | # this will skip precompilation 208 | 209 | skip_assets=1 cap production deploy 210 | 211 | # this will precompile assets 212 | cap production deploy 213 | ``` 214 | 215 | 216 | 217 | ### Precompile assets only after changes 218 | 219 | 220 | We will redefine default precompile task to check for changed files. 221 | Assets will be precompiled only if changes are detected in certain files or folders defined by variable :assets_dependencies. 222 | 223 | 224 | ```ruby 225 | 226 | # set the locations that we will look for changed assets to determine whether to precompile 227 | set :assets_dependencies, %w(app/assets lib/assets vendor/assets Gemfile config/routes.rb) 228 | 229 | 230 | # clear the previous precompile task 231 | Rake::Task["deploy:assets:precompile"].clear_actions 232 | class PrecompileRequired < StandardError; end 233 | 234 | 235 | namespace :deploy do 236 | namespace :assets do 237 | desc "Precompile assets if changed" 238 | task :precompile_changed do 239 | on roles(:app) do 240 | within release_path do 241 | with rails_env: fetch(:rails_env) do 242 | begin 243 | 244 | # find the most recent release 245 | latest_release = capture(:ls, '-xr', releases_path).split[1] 246 | 247 | # precompile if this is the first deploy 248 | raise PrecompileRequired unless latest_release 249 | 250 | # 251 | latest_release_path = releases_path.join(latest_release) 252 | 253 | # precompile if the previous deploy failed to finish precompiling 254 | execute(:ls, latest_release_path.join('assets_manifest_backup')) rescue raise(PrecompileRequired) 255 | 256 | fetch(:assets_dependencies).each do |dep| 257 | #execute(:du, '-b', release_path.join(dep)) rescue raise(PrecompileRequired) 258 | #execute(:du, '-b', latest_release_path.join(dep)) rescue raise(PrecompileRequired) 259 | 260 | # execute raises if there is a diff 261 | execute(:diff, '-Naur', release_path.join(dep), latest_release_path.join(dep)) rescue raise(PrecompileRequired) 262 | end 263 | 264 | warn("-----Skipping asset precompile, no asset diff found") 265 | 266 | # copy over all of the assets from the last release 267 | execute(:cp, '-rf', latest_release_path.join('public', fetch(:assets_prefix)), release_path.join('public', fetch(:assets_prefix))) 268 | 269 | rescue PrecompileRequired 270 | warn("----Run assets precompile") 271 | 272 | execute(:rake, "assets:precompile") 273 | end 274 | end 275 | end 276 | end 277 | end 278 | end 279 | end 280 | 281 | 282 | ``` 283 | 284 | This solution was found in the post 285 | https://coderwall.com/p/aridag/only-precompile-assets-when-necessary-rails-4-capistrano-3 286 | 287 | 288 | Use the new task instead of the default precompile task: 289 | 290 | ```ruby 291 | namespace :deploy do 292 | namespace :assets do 293 | desc "Precompile assets if changed" 294 | task :precompile do 295 | on roles(:app) do 296 | invoke 'deploy:assets:precompile_changed' 297 | #Rake::Task["deploy:assets:precompile_changed"].invoke 298 | end 299 | end 300 | end 301 | end 302 | 303 | ``` 304 | 305 | 306 | 307 | ### Precompile assets locally 308 | 309 | Sometimes you need to precompile assets locally and upload them to the server. 310 | 311 | Define a task to compile assets locally and copy them to the server. If your local machine is on Windows, make sure you have zip archiver (for example, 7zip). 312 | 313 | 314 | ```ruby 315 | 316 | namespace :deploy do 317 | namespace :assets do 318 | 319 | desc 'Run the precompile task locally and upload to server' 320 | task :precompile_locally_archive do 321 | on roles(:app) do 322 | run_locally do 323 | if RUBY_PLATFORM =~ /(win32)|(i386-mingw32)/ 324 | execute 'del "tmp/assets.tar.gz"' rescue nil 325 | execute 'rd /S /Q "public/assets/"' rescue nil 326 | 327 | # precompile 328 | with rails_env: fetch(:rails_env) do 329 | execute 'rake assets:precompile' 330 | end 331 | #execute "RAILS_ENV=#{rails_env} rake assets:precompile" 332 | 333 | # use 7zip to archive 334 | execute '7z a -ttar assets.tar public/assets/' 335 | execute '7z a -tgzip assets.tar.gz assets.tar' 336 | execute 'del assets.tar' 337 | execute 'move assets.tar.gz tmp/' 338 | else 339 | execute 'rm tmp/assets.tar.gz' rescue nil 340 | execute 'rm -rf public/assets/*' 341 | 342 | with rails_env: fetch(:rails_env) do 343 | execute 'rake assets:precompile' 344 | end 345 | 346 | execute 'touch assets.tar.gz && rm assets.tar.gz' 347 | execute 'tar zcvf assets.tar.gz public/assets/' 348 | execute 'mv assets.tar.gz tmp/' 349 | end 350 | end 351 | 352 | # Upload precompiled assets 353 | execute 'rm -rf public/assets/*' 354 | upload! "tmp/assets.tar.gz", "#{release_path}/assets.tar.gz" 355 | execute "cd #{release_path} && tar zxvf assets.tar.gz && rm assets.tar.gz" 356 | end 357 | end 358 | 359 | end 360 | end 361 | 362 | ``` 363 | 364 | 365 | 366 | If you don't want to archive the assets before upload, use this task which will copy folder /public/assets to the server file by file. 367 | 368 | ```ruby 369 | namespace :deploy do 370 | namespace :assets do 371 | 372 | desc 'Precompile assets locally and upload to server' 373 | task :precompile_locally_copy do 374 | on roles(:app) do 375 | run_locally do 376 | with rails_env: fetch(:rails_env) do 377 | #execute 'rake assets:precompile' 378 | end 379 | end 380 | 381 | execute "cd #{release_path} && mkdir public" rescue nil 382 | execute "cd #{release_path} && mkdir public/assets" rescue nil 383 | execute 'rm -rf public/assets/*' 384 | 385 | upload! 'public/assets', "#{release_path}/public", recursive: true 386 | 387 | end 388 | end 389 | end 390 | end 391 | ``` 392 | 393 | Run tasks : 394 | ```ruby 395 | 396 | cap production deploy:assets:precompile_locally_archive 397 | 398 | or 399 | 400 | cap production deploy:assets:precompile_locally_copy 401 | ``` 402 | 403 | 404 | To replace default precompile task with the new task: 405 | 406 | ```ruby 407 | namespace :deploy do 408 | namespace :assets do 409 | desc "Precompile assets" 410 | task :precompile do 411 | on roles(:app) do 412 | invoke 'deploy:assets:precompile_locally_archive' 413 | #Rake::Task["deploy:assets:precompile_locally_archive"].invoke 414 | end 415 | end 416 | end 417 | end 418 | ``` 419 | 420 | 421 | 422 | 423 | 424 | ## Maintenance page 425 | 426 | Use these tasks if you need to show a certain page on site to visitors while the app is updating: 427 | 428 | ```ruby 429 | 430 | cap production deploy:web:enable 431 | cap production deploy:web:disable 432 | 433 | ``` 434 | 435 | Create a page 'app/views/admin/maintenance.html.haml' 436 | ```ruby 437 |
We're currently offline for <%= reason ? reason : 'maintenance' %> as of <%= Time.now.utc.strftime('%H:%M %Z') %>.
441 |Sorry for the inconvenience. 442 |
We'll be back <%= deadline ? "by #{deadline}" : 'shortly' %>.
443 | 444 |