deployment with Capistrano 3 (affects foodcoops#148)
This commit is contained in:
parent
b2f032ac8e
commit
ca234f0b70
5 changed files with 199 additions and 48 deletions
8
Gemfile
8
Gemfile
|
@ -65,9 +65,11 @@ group :development do
|
||||||
gem 'quiet_assets'
|
gem 'quiet_assets'
|
||||||
|
|
||||||
# Deploy with Capistrano
|
# Deploy with Capistrano
|
||||||
gem 'capistrano', '2.13.5', require: false
|
gem 'capistrano', '~> 3.0', require: false
|
||||||
gem 'capistrano-ext', require: false
|
# https://github.com/capistrano/rails/issues/48#issuecomment-31443739
|
||||||
#gem 'common_deploy', require: false, path: '../../common_deploy' # pending foodcoops/foodsoft#34, git: 'git://github.com/fsmanuel/common_deploy.git'
|
gem 'capistrano-rvm', github: 'capistrano/rvm', require: false
|
||||||
|
gem 'capistrano-bundler', '>= 1.1.0', require: false
|
||||||
|
gem 'capistrano-rails', require: false
|
||||||
# Avoid having content-length warnings
|
# Avoid having content-length warnings
|
||||||
gem 'thin'
|
gem 'thin'
|
||||||
end
|
end
|
||||||
|
|
41
Gemfile.lock
41
Gemfile.lock
|
@ -4,6 +4,14 @@ GIT
|
||||||
specs:
|
specs:
|
||||||
localize_input (0.1.0)
|
localize_input (0.1.0)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: git://github.com/capistrano/rvm.git
|
||||||
|
revision: 6aa7cb9d75361c802f466b54d0e345b7237ea3bb
|
||||||
|
specs:
|
||||||
|
capistrano-rvm (0.1.1)
|
||||||
|
capistrano (~> 3.0)
|
||||||
|
sshkit (~> 1.2)
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: git://github.com/fnando/i18n-js.git
|
remote: git://github.com/fnando/i18n-js.git
|
||||||
revision: eab4137f83777963f0ebe6960704a7f64fd8911d
|
revision: eab4137f83777963f0ebe6960704a7f64fd8911d
|
||||||
|
@ -74,14 +82,16 @@ GEM
|
||||||
bullet (4.7.1)
|
bullet (4.7.1)
|
||||||
activesupport
|
activesupport
|
||||||
uniform_notifier (>= 1.4.0)
|
uniform_notifier (>= 1.4.0)
|
||||||
capistrano (2.13.5)
|
capistrano (3.1.0)
|
||||||
highline
|
i18n
|
||||||
net-scp (>= 1.0.0)
|
rake (>= 10.0.0)
|
||||||
net-sftp (>= 2.0.0)
|
sshkit (~> 1.3)
|
||||||
net-ssh (>= 2.0.14)
|
capistrano-bundler (1.1.1)
|
||||||
net-ssh-gateway (>= 1.1.0)
|
capistrano (~> 3.0)
|
||||||
capistrano-ext (1.2.1)
|
sshkit (>= 1.2.0)
|
||||||
capistrano (>= 1.0.0)
|
capistrano-rails (1.1.0)
|
||||||
|
capistrano (>= 3.0.0)
|
||||||
|
capistrano-bundler (>= 1.0.0)
|
||||||
capybara (2.2.1)
|
capybara (2.2.1)
|
||||||
mime-types (>= 1.16)
|
mime-types (>= 1.16)
|
||||||
nokogiri (>= 1.3.3)
|
nokogiri (>= 1.3.3)
|
||||||
|
@ -143,7 +153,6 @@ GEM
|
||||||
actionpack (>= 3.2, < 5)
|
actionpack (>= 3.2, < 5)
|
||||||
activesupport (>= 3.2, < 5)
|
activesupport (>= 3.2, < 5)
|
||||||
hashery (2.1.1)
|
hashery (2.1.1)
|
||||||
highline (1.6.20)
|
|
||||||
hike (1.2.3)
|
hike (1.2.3)
|
||||||
i18n (0.6.9)
|
i18n (0.6.9)
|
||||||
i18n-spec (0.4.0)
|
i18n-spec (0.4.0)
|
||||||
|
@ -191,11 +200,7 @@ GEM
|
||||||
mysql2 (0.3.14)
|
mysql2 (0.3.14)
|
||||||
net-scp (1.1.2)
|
net-scp (1.1.2)
|
||||||
net-ssh (>= 2.6.5)
|
net-ssh (>= 2.6.5)
|
||||||
net-sftp (2.1.2)
|
|
||||||
net-ssh (>= 2.6.5)
|
|
||||||
net-ssh (2.7.0)
|
net-ssh (2.7.0)
|
||||||
net-ssh-gateway (1.2.0)
|
|
||||||
net-ssh (>= 2.6.5)
|
|
||||||
nokogiri (1.6.1)
|
nokogiri (1.6.1)
|
||||||
mini_portile (~> 0.5.0)
|
mini_portile (~> 0.5.0)
|
||||||
pdf-reader (1.3.3)
|
pdf-reader (1.3.3)
|
||||||
|
@ -321,6 +326,10 @@ GEM
|
||||||
rack (~> 1.0)
|
rack (~> 1.0)
|
||||||
tilt (~> 1.1, != 1.3.0)
|
tilt (~> 1.1, != 1.3.0)
|
||||||
sqlite3 (1.3.8)
|
sqlite3 (1.3.8)
|
||||||
|
sshkit (1.3.0)
|
||||||
|
net-scp (>= 1.1.2)
|
||||||
|
net-ssh
|
||||||
|
term-ansicolor
|
||||||
term-ansicolor (1.2.2)
|
term-ansicolor (1.2.2)
|
||||||
tins (~> 0.8)
|
tins (~> 0.8)
|
||||||
therubyracer (0.12.0)
|
therubyracer (0.12.0)
|
||||||
|
@ -370,8 +379,10 @@ DEPENDENCIES
|
||||||
binding_of_caller
|
binding_of_caller
|
||||||
bootstrap-datepicker-rails
|
bootstrap-datepicker-rails
|
||||||
bullet
|
bullet
|
||||||
capistrano (= 2.13.5)
|
capistrano (~> 3.0)
|
||||||
capistrano-ext
|
capistrano-bundler (>= 1.1.0)
|
||||||
|
capistrano-rails
|
||||||
|
capistrano-rvm!
|
||||||
capybara
|
capybara
|
||||||
client_side_validations
|
client_side_validations
|
||||||
client_side_validations-simple_form
|
client_side_validations-simple_form
|
||||||
|
|
|
@ -1,43 +1,56 @@
|
||||||
require 'bundler/setup'
|
#
|
||||||
require 'common_deploy'
|
# Capistrano 3 deployment configuration
|
||||||
|
#
|
||||||
|
# http://www.capistranorb.com/
|
||||||
|
# https://semaphoreapp.com/blog/2013/11/26/capistrano-3-upgrade-guide.html
|
||||||
|
|
||||||
set :application, 'foodsoft'
|
# defaults that can be updated from the environment
|
||||||
set :domain, 'foodsoft.com'
|
set :branch, ENV["REVISION"] || ENV["BRANCH_NAME"] || "master"
|
||||||
set :user, 'foodsoft'
|
|
||||||
set :default_stage, 'staging' # staging and production are available via (set :stages, ["staging", "production"])
|
|
||||||
set :keep_releases, 5
|
|
||||||
set :repository, 'git://github.com/foodcoops/foodsoft.git'
|
|
||||||
set(:deploy_to) { "/mnt/apps/#{application}_#{stage}" }
|
|
||||||
|
|
||||||
|
# you probably want to change these
|
||||||
|
set :application, 'foodsoft' # application name (whatever you like)
|
||||||
|
set :domain, 'order.foodcoop.test' # host
|
||||||
|
set :user, 'deploy' # ssh deploy user
|
||||||
|
set :default_stage, 'staging' # default environment, see config/deploy/
|
||||||
|
set :keep_releases, 10
|
||||||
|
set :repo_url, 'git://github.com/foodcoops/foodsoft.git'
|
||||||
|
set :unique_app_name, fetch(:application) # for more complex setups, this can be customised
|
||||||
|
# XXX how to get rails environment in here?
|
||||||
|
set :deploy_to, "/www/apps/#{fetch :unique_app_name}"
|
||||||
|
|
||||||
# resque worker
|
# more settings which are probably ok
|
||||||
role :resque_worker, domain
|
set :log_level, :info
|
||||||
role :resque_scheduler, domain
|
set :linked_files, %w{config/database.yml config/app_config.yml config/initializers/secret_token.rb}
|
||||||
set :workers, { "foodsoft_notifier" => 1 }
|
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
|
||||||
|
|
||||||
|
# assuming one server for everything, with one user for deploy and one for resque
|
||||||
|
server fetch(:domain), user: fetch(:user), roles: [:web, :app, :resque, :db]
|
||||||
|
|
||||||
# rvm
|
# if you use RVM, uncomment the line in Capfile, and optionally uncomment rvm settings
|
||||||
# set :rvm_ruby_string, :local
|
# set :rvm_ruby_string, :local
|
||||||
|
|
||||||
|
# task hooks
|
||||||
|
namespace :deploy do
|
||||||
|
|
||||||
server domain, :web, :app, :db
|
desc 'Restart application'
|
||||||
|
task :restart do
|
||||||
|
on roles(:app), in: :sequence, wait: 5 do
|
||||||
|
# tell mod_passenger to reload the application
|
||||||
|
execute :touch, release_path.join('tmp/restart.txt')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Loads all needed capistrano extensions
|
after :restart, 'resque:restart'
|
||||||
load_extensions :bundler,
|
|
||||||
# :rvm, # if you are using rvm on your server uncomment this line
|
|
||||||
:passenger,
|
|
||||||
:multistage,
|
|
||||||
:resque,
|
|
||||||
:whenever,
|
|
||||||
:assets
|
|
||||||
|
|
||||||
|
after :restart, :clear_cache do
|
||||||
|
on roles(:web), in: :groups, limit: 3, wait: 10 do
|
||||||
|
within release_path do
|
||||||
|
execute :rake, 'cache:clear'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# clean up old releases on each deploy
|
after :finishing, 'deploy:cleanup'
|
||||||
after "deploy:restart", "deploy:cleanup"
|
|
||||||
|
|
||||||
# restart resque
|
end
|
||||||
after "deploy:restart", "resque:restart"
|
|
||||||
|
|
||||||
# install rvm and ruby on every deploy
|
|
||||||
# before 'deploy', 'rvm:install_rvm' # update RVM
|
|
||||||
# before 'deploy', 'rvm:install_ruby' # install Ruby and create gemset (both if missing)
|
|
101
lib/capistrano/tasks/deploy_initial.cap
Normal file
101
lib/capistrano/tasks/deploy_initial.cap
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
# Capistrano tasks for the initial setup
|
||||||
|
|
||||||
|
namespace :deploy do
|
||||||
|
|
||||||
|
desc 'Creates and initialises a new foodsoft instance'
|
||||||
|
task :initial do
|
||||||
|
before 'deploy:check:linked_files', 'deploy:initial:touch_shared'
|
||||||
|
before 'deploy:updated', 'deploy:initial:secret_token'
|
||||||
|
before 'deploy:updated', 'deploy:initial:app_config'
|
||||||
|
before 'deploy:updated', 'deploy:initial:db:config'
|
||||||
|
before 'deploy:updated', 'deploy:initial:db:create'
|
||||||
|
before 'deploy:migrate', 'deploy:initial:db:load'
|
||||||
|
end
|
||||||
|
after :initial, :deploy
|
||||||
|
|
||||||
|
|
||||||
|
namespace :initial do
|
||||||
|
|
||||||
|
namespace :db do
|
||||||
|
|
||||||
|
desc 'Generate new database.yml with random password'
|
||||||
|
task :config => ['deploy:set_rails_env'] do
|
||||||
|
require 'securerandom'
|
||||||
|
on roles(:app), in: :groups do
|
||||||
|
db_name = fetch(:unique_app_name).gsub(/[^-_a-z0-9]/i, '')
|
||||||
|
db_passwd = SecureRandom.urlsafe_base64(24).to_s
|
||||||
|
db_yaml = {
|
||||||
|
fetch(:rails_env).to_s => {
|
||||||
|
'adapter' => 'mysql2',
|
||||||
|
'encoding' => 'utf8',
|
||||||
|
'database' => db_name,
|
||||||
|
'username' => db_name,
|
||||||
|
'password' => db_passwd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
execute :mkdir, '-p', shared_path.join("config")
|
||||||
|
upload! StringIO.new(db_yaml.to_yaml), shared_path.join("config/database.yml")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# assumes mysql access setup (~/.my.cnf), with permissions
|
||||||
|
desc 'Create database new database'
|
||||||
|
task :create => ['deploy:set_rails_env'] do
|
||||||
|
on roles(:app), in: :sequence do
|
||||||
|
config = capture :cat, shared_path.join("config/database.yml")
|
||||||
|
config = YAML.load(config)[fetch(:rails_env).to_s]
|
||||||
|
# http://www.grahambrooks.com/blog/create-mysql-database-with-capistrano/
|
||||||
|
execute :mysql, "--execute='CREATE DATABASE IF NOT EXISTS `#{config['database']}`';"
|
||||||
|
execute :mysql, "--execute='GRANT ALL ON `#{config['database']}`.* TO \"#{config['username']}\" IDENTIFIED BY \"#{config['password']}\";'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Load database schema'
|
||||||
|
task :load => ['deploy:set_rails_env'] do
|
||||||
|
on roles(:app), in: :groups do
|
||||||
|
# workaround nonexistent release_path on first deploy
|
||||||
|
path = releases_path.join(capture(:ls, releases_path).split("\n").sort.last)
|
||||||
|
within path do
|
||||||
|
with rails_env: fetch(:rails_env) do
|
||||||
|
execute :rake, 'db:schema:load'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Writes a new secret token'
|
||||||
|
task :secret_token do
|
||||||
|
require 'securerandom'
|
||||||
|
on roles(:app), in: :groups do
|
||||||
|
secret = SecureRandom.hex(64)
|
||||||
|
text = "Foodsoft::Application.config.secret_token = \"#{secret}\""
|
||||||
|
execute :mkdir, '-p', shared_path.join("config/initializers")
|
||||||
|
upload! StringIO.new(text), shared_path.join("config/initializers/secret_token.rb")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Creates a default app_config.yml'
|
||||||
|
task :app_config do
|
||||||
|
on roles(:app), in: :groups do
|
||||||
|
execute :mkdir, '-p', shared_path.join("config")
|
||||||
|
# workaround nonexistent release_path on first deploy
|
||||||
|
path = releases_path.join(capture(:ls, releases_path).split("\n").sort.last)
|
||||||
|
execute :cp, path.join("config/app_config.yml.SAMPLE"), shared_path.join("config/app_config.yml")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Touches the shared configuration files (for initial deploy)'
|
||||||
|
task :touch_shared do
|
||||||
|
on roles(:app), in: :groups do
|
||||||
|
execute :mkdir, '-p', shared_path.join("config/initializers")
|
||||||
|
execute :touch, shared_path.join("config/initializers/secret_token.rb")
|
||||||
|
execute :touch, shared_path.join("config/app_config.yml")
|
||||||
|
execute :touch, shared_path.join("config/database.yml")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
24
lib/capistrano/tasks/resque.cap
Normal file
24
lib/capistrano/tasks/resque.cap
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
# capistrano-resque could be used, but it does not support running resque as another user.
|
||||||
|
# If you want to run resque as another user, setup sudo to allow running commands as that user:
|
||||||
|
# deploy ALL=(foodsoft_user) NOPASSWD: ALL
|
||||||
|
# and set `:run_user` to the foodsoft user.
|
||||||
|
namespace :resque do
|
||||||
|
|
||||||
|
%w{start stop restart}.each do |action|
|
||||||
|
desc "#{action.capitalize} Resque workers"
|
||||||
|
task action => ['deploy:set_rails_env'] do
|
||||||
|
on roles(:resque), in: :groups do
|
||||||
|
within current_path do
|
||||||
|
cmd = command(:rake, "resque:#{action}_workers", "RAILS_ENV=#{fetch(:rails_env)}")
|
||||||
|
if fetch(:run_user).nil? or fetch(:run_user) == local_user
|
||||||
|
execute cmd
|
||||||
|
else
|
||||||
|
execute 'sudo', '-u', fetch(:run_user), cmd
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue