diff --git a/.gitignore b/.gitignore
index 5cc519787..339f1eb5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,6 @@
/config/deploy-secrets.yml
/coverage
+
+# Mac finder artifacts
+.DS_Store
diff --git a/Capfile b/Capfile
index b565fae23..17e03a6f8 100644
--- a/Capfile
+++ b/Capfile
@@ -6,9 +6,11 @@ require 'capistrano/deploy'
require 'capistrano/rvm'
require 'capistrano/bundler'
-require 'capistrano/rails/assets'
+#require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
-require 'capistrano/passenger'
+#require 'capistrano/passenger'
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
+Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
+Dir.glob('lib/capistrano/**/*.rb').each { |r| import r }
diff --git a/Gemfile b/Gemfile
index afa51a69e..edb2e190b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -30,6 +30,7 @@ gem 'simple_captcha2', require: 'simple_captcha'
gem 'ckeditor'
gem 'cancancan'
gem 'social-share-button'
+gem 'unicorn'
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
@@ -49,7 +50,6 @@ group :development, :test do
gem "capistrano-bundler", '1.1.4', require: false
gem "capistrano-rails", '1.1.3', require: false
gem "capistrano-rvm", require: false
- gem "capistrano-passenger", require: false
end
group :test do
diff --git a/Gemfile.lock b/Gemfile.lock
index ad49450cb..a6a269d2e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -61,8 +61,6 @@ GEM
capistrano-bundler (1.1.4)
capistrano (~> 3.1)
sshkit (~> 1.2)
- capistrano-passenger (0.1.1)
- capistrano (~> 3.0)
capistrano-rails (1.1.3)
capistrano (~> 3.1)
capistrano-bundler (~> 1.1)
@@ -147,6 +145,7 @@ GEM
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (1.8.3)
+ kgio (2.9.3)
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.4.1)
@@ -204,6 +203,7 @@ GEM
activesupport (= 4.2.3)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
+ raindrops (0.15.0)
rake (10.4.2)
responders (2.1.0)
railties (>= 4.2.0, < 5)
@@ -274,6 +274,10 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.1)
+ unicorn (4.9.0)
+ kgio (~> 2.6)
+ rack
+ raindrops (~> 0.7)
warden (1.2.3)
rack (>= 1.0)
web-console (2.2.1)
@@ -298,7 +302,6 @@ DEPENDENCIES
cancancan
capistrano (= 3.4.0)
capistrano-bundler (= 1.1.4)
- capistrano-passenger
capistrano-rails (= 1.1.3)
capistrano-rvm
capybara
@@ -326,6 +329,7 @@ DEPENDENCIES
spring
turbolinks
uglifier (>= 1.3.0)
+ unicorn
web-console (~> 2.0)
BUNDLED WITH
diff --git a/config/deploy-secrets.yml.example b/config/deploy-secrets.yml.example
index b69fea225..696f6ce75 100644
--- a/config/deploy-secrets.yml.example
+++ b/config/deploy-secrets.yml.example
@@ -3,6 +3,8 @@ staging:
ssh_port: 21
server: staging.participacion.madrid.es
user: xxxxx
+ server_name: staging.participacion.madrid.es
+ db_server: postgre.participacion.madrid.es
preproduction:
deploy_to: "/var/www/participacion"
diff --git a/config/deploy.rb b/config/deploy.rb
index 8104d742f..9c45bd283 100644
--- a/config/deploy.rb
+++ b/config/deploy.rb
@@ -8,14 +8,20 @@ end
set :rails_env, fetch(:stage)
set :rvm_ruby_version, '2.2.2'
+set :rvm_type, :user
set :application, 'participacion'
-set :repo_url, 'git@github.com:AyuntamientoMadrid/participacion.git'
+set :server_name, deploysecret(:server_name)
+#set :repo_url, 'git@github.com:AyuntamientoMadrid/participacion.git'
+# If ssh access is restricted, probably you need to use https access
+set :repo_url, 'https://github.com/AyuntamientoMadrid/participacion.git'
set :scm, :git
set :revision, `git rev-parse --short #{fetch(:branch)}`.strip
set :log_level, :info
+set :pty, true
+set :use_sudo, false
set :linked_files, %w{config/database.yml config/secrets.yml}
set :linked_dirs, %w{log tmp public/system public/assets}
@@ -24,15 +30,27 @@ set :keep_releases, 5
set :local_user, ENV['USER']
+# Run test before deploy
+set :tests, ["spec"]
+
+# Config files should be copied by deploy:setup_config
+set(:config_files, %w(
+ log_rotation
+ database.yml
+ secrets.yml
+ unicorn.rb
+ sidekiq.yml
+))
+
+
namespace :deploy do
-
- after :restart, :clear_cache do
- on roles(:web), in: :groups, limit: 3, wait: 10 do
- # Here we can do anything such as:
- # within release_path do
- # execute :rake, 'cache:clear'
- # end
- end
- end
-
+ # Check right version of deploy branch
+ before :deploy, "deploy:check_revision"
+ # Run test aund continue only if passed
+ before :deploy, "deploy:run_tests"
+ # Compile assets locally and then rsync
+ after 'deploy:symlink:shared', 'deploy:compile_assets_locally'
+ after :finishing, 'deploy:cleanup'
+ # Restart unicorn
+ after 'deploy:publishing', 'deploy:restart'
end
diff --git a/config/deploy/preproduction.rb b/config/deploy/preproduction.rb
index fb9033c03..7a18c011d 100644
--- a/config/deploy/preproduction.rb
+++ b/config/deploy/preproduction.rb
@@ -1,5 +1,9 @@
set :deploy_to, deploysecret(:deploy_to)
-set :branch, :production
+set :server_name, deploysecret(:server_name)
+set :db_server, deploysecret(:db_server)
+set :branch, :master
set :ssh_options, port: deploysecret(:ssh_port)
+set :stage, :production
+set :rails_env, :production
-server deploysecret(:server), user: deploysecret(:user), roles: %w(web app db importer)
\ No newline at end of file
+server deploysecret(:server), user: deploysecret(:user), roles: %w(web app db importer)
diff --git a/config/deploy/production.rb b/config/deploy/production.rb
index fb9033c03..7a18c011d 100644
--- a/config/deploy/production.rb
+++ b/config/deploy/production.rb
@@ -1,5 +1,9 @@
set :deploy_to, deploysecret(:deploy_to)
-set :branch, :production
+set :server_name, deploysecret(:server_name)
+set :db_server, deploysecret(:db_server)
+set :branch, :master
set :ssh_options, port: deploysecret(:ssh_port)
+set :stage, :production
+set :rails_env, :production
-server deploysecret(:server), user: deploysecret(:user), roles: %w(web app db importer)
\ No newline at end of file
+server deploysecret(:server), user: deploysecret(:user), roles: %w(web app db importer)
diff --git a/config/deploy/sample_config_files/apache_passenger.conf b/config/deploy/sample_config_files/apache_passenger.conf
new file mode 100644
index 000000000..f97fc9b2a
--- /dev/null
+++ b/config/deploy/sample_config_files/apache_passenger.conf
@@ -0,0 +1,19 @@
+
+
+ ServerName yourdomain.com
+ ServerAlias www.yourdomain.com
+ ServerAdmin webmaster@localhost
+
+ DocumentRoot /path/to/deploy_to/current/public
+
+ # RailsEnv production
+
+ ErrorLog ${APACHE_LOG_DIR}/yourdomain.error.log
+ CustomLog ${APACHE_LOG_DIR}/yourdomain.access.log combined
+
+
+ Options FollowSymLinks
+ Require all granted
+
+
+
diff --git a/config/deploy/sample_config_files/apache_unicorn.conf b/config/deploy/sample_config_files/apache_unicorn.conf
new file mode 100644
index 000000000..93aafec23
--- /dev/null
+++ b/config/deploy/sample_config_files/apache_unicorn.conf
@@ -0,0 +1,31 @@
+
+
+ ServerAdmin admin@yourdomain.com
+ ServerName yourdomain.com
+ ServerAlias www.yourdomain.com
+
+ DocumentRoot /path/to/deploy_to/current/public
+
+ RewriteEngine On
+
+
+ BalancerMember http://127.0.0.1:5000
+
+
+ # Redirect all non-static requests to thin
+ RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
+ RewriteRule ^/(.*)$ balancer://unicornservers%{REQUEST_URI} [P,QSA,L]
+
+ ProxyPass / balancer://unicornservers/
+ ProxyPassReverse / balancer://unicornservers/
+ ProxyPreserveHost on
+
+
+ Order deny,allow
+ Allow from all
+
+
+ ErrorLog ${APACHE_LOG_DIR}/yourdomain.error.log
+ CustomLog ${APACHE_LOG_DIR}/yourdomain.access.log combined
+
+
diff --git a/config/deploy/sample_config_files/unicorn_init.sh b/config/deploy/sample_config_files/unicorn_init.sh
new file mode 100644
index 000000000..1cef9110b
--- /dev/null
+++ b/config/deploy/sample_config_files/unicorn_init.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+### BEGIN INIT INFO
+# Provides: unicorn
+# Required-Start: $local_fs $remote_fs $network $syslog
+# Required-Stop: $local_fs $remote_fs $network $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts the unicorn web server
+# Description: starts unicorn
+### END INIT INFO
+
+set -e
+
+TIMEOUT=${TIMEOUT-60}
+APP_ROOT=<%= current_path %>
+PID_DIR=$APP_ROOT/tmp/pids
+PID=$PID_DIR/unicorn.pid
+CMD="cd $APP_ROOT; bundle exec unicorn -D -c /path/to/shared/config/unicorn.rb -E production"
+AS_USER=deploy
+set -u
+
+OLD_PIN="$PID.oldbin"
+
+sig () {
+ test -s "$PID" && kill -$1 `cat $PID`
+}
+
+oldsig () {
+ test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`
+}
+
+workersig () {
+ workerpid="$APP_ROOT/tmp/pids/unicorn.$2.pid"
+
+ test -s "$workerpid" && kill -$1 `cat $workerpid`
+}
+
+run () {
+ if [ "$(id -un)" = "$AS_USER" ]; then
+ eval $1
+ else
+ su -c "$1" - $AS_USER
+ fi
+}
+
+case "$1" in
+start)
+ sig 0 && echo >&2 "Already running" && exit 0
+ run "$CMD"
+ ;;
+stop)
+ sig QUIT && exit 0
+ echo >&2 "Not running"
+ ;;
+force-stop)
+ sig TERM && exit 0
+ echo >&2 "Not running"
+ ;;
+kill_worker)
+ workersig QUIT $2 && exit 0
+ echo >&2 "Worker not running"
+ ;;
+restart|reload)
+ sig USR2 && echo reloaded OK && exit 0
+ echo >&2 "Couldn't reload, starting '$CMD' instead"
+ run "$CMD"
+ ;;
+upgrade)
+ if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
+ then
+ n=$TIMEOUT
+ while test -s $OLD_PIN && test $n -ge 0
+ do
+ printf '.' && sleep 1 && n=$(( $n - 1 ))
+ done
+ echo
+
+ if test $n -lt 0 && test -s $OLD_PIN
+ then
+ echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds"
+ exit 1
+ fi
+ exit 0
+ fi
+ echo >&2 "Couldn't upgrade, starting '$CMD' instead"
+ run "$CMD"
+ ;;
+reopen-logs)
+ sig USR1
+ ;;
+*)
+ echo >&2 "Usage: $0 "
+ exit 1
+ ;;
+esac
diff --git a/config/deploy/shared/database.yml.erb b/config/deploy/shared/database.yml.erb
new file mode 100644
index 000000000..2449ae6f8
--- /dev/null
+++ b/config/deploy/shared/database.yml.erb
@@ -0,0 +1,10 @@
+<%= fetch(:rails_env) %>:
+ adapter: postgresql
+ timeout: 5000
+ encoding: unicode
+ reconnect: false
+ database: <%= "#{fetch(:application)}" %>
+ pool: 5
+ username:
+ password:
+ host: <%= fetch(:db_server) %>
diff --git a/config/deploy/shared/log_rotation.erb b/config/deploy/shared/log_rotation.erb
new file mode 100755
index 000000000..7507de184
--- /dev/null
+++ b/config/deploy/shared/log_rotation.erb
@@ -0,0 +1,11 @@
+<%= fetch(:deploy_to) %>/shared/log/*.log {
+ daily
+ missingok
+ rotate 52
+ compress
+ delaycompress
+ notifempty
+ sharedscripts
+ endscript
+ copytruncate
+}
diff --git a/config/deploy/shared/secrets.yml.erb b/config/deploy/shared/secrets.yml.erb
new file mode 100644
index 000000000..d3c3251af
--- /dev/null
+++ b/config/deploy/shared/secrets.yml.erb
@@ -0,0 +1,4 @@
+<%= fetch(:rails_env) %>:
+ recaptcha_public_key: <%= ENV["MADRID_RECAPTCHA_PUBLIC_KEY"] %>
+ recaptcha_private_key: <%= ENV["MADRID_RECAPTCHA_PRIVATE_KEY"] %>
+ secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
diff --git a/config/deploy/shared/sidekiq.yml.erb b/config/deploy/shared/sidekiq.yml.erb
new file mode 100644
index 000000000..1d4747933
--- /dev/null
+++ b/config/deploy/shared/sidekiq.yml.erb
@@ -0,0 +1,2 @@
+<%= fetch(:rails_env) %>:
+ :concurrency: <%= fetch(:sidekiq_concurrency, 5) %>
diff --git a/config/deploy/shared/unicorn.rb.erb b/config/deploy/shared/unicorn.rb.erb
new file mode 100755
index 000000000..805734276
--- /dev/null
+++ b/config/deploy/shared/unicorn.rb.erb
@@ -0,0 +1,42 @@
+root = "<%= current_path %>"
+working_directory root
+pid "#{root}/tmp/pids/unicorn.pid"
+stderr_path "#{root}/log/unicorn.log"
+stdout_path "#{root}/log/unicorn.log"
+
+listen 5000
+#listen "/tmp/unicorn.<%= fetch(:application) %>.sock"
+worker_processes 4
+timeout 40
+preload_app true
+
+# Force unicorn to look at the Gemfile in the current_path
+# otherwise once we've first started a master process, it
+# will always point to the first one it started.
+before_exec do |server|
+ ENV['BUNDLE_GEMFILE'] = "<%= current_path %>/Gemfile"
+end
+
+before_fork do |server, worker|
+ defined?(ActiveRecord::Base) and
+ ActiveRecord::Base.connection.disconnect!
+ # Quit the old unicorn process
+ old_pid = "#{server.config[:pid]}.oldbin"
+ if File.exists?(old_pid) && server.pid != old_pid
+ puts "We've got an old pid and server pid is not the old pid"
+ begin
+ Process.kill("QUIT", File.read(old_pid).to_i)
+ puts "killing master process (good thing tm)"
+ rescue Errno::ENOENT, Errno::ESRCH
+ puts "unicorn master already killed"
+ end
+ end
+end
+
+after_fork do |server, worker|
+ port = 5000 + worker.nr
+ child_pid = server.config[:pid].sub('.pid', ".#{port}.pid")
+ system("echo #{Process.pid} > #{child_pid}")
+ defined?(ActiveRecord::Base) and
+ ActiveRecord::Base.establish_connection
+end
diff --git a/config/deploy/staging.rb b/config/deploy/staging.rb
index 5ca436c42..09ba7401a 100644
--- a/config/deploy/staging.rb
+++ b/config/deploy/staging.rb
@@ -1,7 +1,11 @@
set :deploy_to, deploysecret(:deploy_to)
+set :server_name, deploysecret(:server_name)
+set :db_server, deploysecret(:db_server)
set :branch, :master
set :ssh_options, port: deploysecret(:ssh_port)
-
-set :passenger_restart_with_sudo, false
+set :stage, :staging
+set :rails_env, :staging
server deploysecret(:server), user: deploysecret(:user), roles: %w(web app db importer)
+
+
diff --git a/lib/capistrano/substitute_strings.rb b/lib/capistrano/substitute_strings.rb
new file mode 100755
index 000000000..d4b244323
--- /dev/null
+++ b/lib/capistrano/substitute_strings.rb
@@ -0,0 +1,12 @@
+# we often want to refer to variables which
+# are defined in subsequent stage files. This
+# let's us use the {{var}} to represent fetch(:var)
+# in strings which are only evaluated at runtime.
+
+def sub_strings(input_string)
+ output_string = input_string
+ input_string.scan(/{{(\w*)}}/).each do |var|
+ output_string.gsub!("{{#{var[0]}}}", fetch(var[0].to_sym))
+ end
+ output_string
+end
diff --git a/lib/capistrano/tasks/apache.cap b/lib/capistrano/tasks/apache.cap
new file mode 100755
index 000000000..3dfbced57
--- /dev/null
+++ b/lib/capistrano/tasks/apache.cap
@@ -0,0 +1,30 @@
+namespace :apache do
+ %w(start stop restart reload).each do |task_name|
+ desc "#{task } Apache"
+ task task_name do
+ on roles(:app), in: :sequence, wait: 5 do
+ sudo "/etc/init.d/apache2 #{task_name}"
+ end
+ end
+ end
+
+ desc "Enable site virual host"
+ task "enable_virtual_host" do
+ on roles(:app) do
+ "cd /etc/apache2/sites-available/"
+ sudo "a2ensite #{fetch(:server_name)}"
+ end
+ end
+
+ desc "Remove default Apache Virtual Host"
+ task "remove_default_vhost" do
+ on roles(:app) do
+ if test("[ -f /etc/apache2/sites-enabled/000-default.conf ]")
+ sudo "rm /etc/apache2/sites-enabled/000-default.conf"
+ puts "removed default Apache Virtualhost"
+ else
+ puts "No default Apache Virtualhost to remove"
+ end
+ end
+ end
+end
diff --git a/lib/capistrano/tasks/check_revision.cap b/lib/capistrano/tasks/check_revision.cap
new file mode 100755
index 000000000..12cf3390d
--- /dev/null
+++ b/lib/capistrano/tasks/check_revision.cap
@@ -0,0 +1,14 @@
+namespace :deploy do
+ desc "checks whether the currently checkout out revision matches the
+ remote one we're trying to deploy from"
+ task :check_revision do
+ branch = fetch(:branch)
+ unless `git rev-parse HEAD` == `git rev-parse origin/#{branch}`
+ puts "WARNING: HEAD is not the same as origin/#{branch}"
+ puts "Run `git push` to sync changes or make sure you've"
+ puts "checked out the branch: #{branch} as you can only deploy"
+ puts "if you've got the target branch checked out"
+ exit
+ end
+ end
+end
diff --git a/lib/capistrano/tasks/compile_assets_locally.cap b/lib/capistrano/tasks/compile_assets_locally.cap
new file mode 100755
index 000000000..5966ff1ea
--- /dev/null
+++ b/lib/capistrano/tasks/compile_assets_locally.cap
@@ -0,0 +1,17 @@
+namespace :deploy do
+ desc "compiles assets locally then rsyncs"
+ task :compile_assets_locally do
+ run_locally do
+ execute "RAILS_ENV=#{fetch(:rails_env)} bundle exec rake assets:precompile"
+ end
+ on roles(:app) do |role|
+ run_locally do
+ execute"rsync -av ./public/assets/ #{role.user}@#{role.hostname}:#{release_path}/public/assets/;"
+ end
+ "chmod -R 755 #{release_path}/public/assets/"
+ end
+ run_locally do
+ execute "rm -rf ./public/assets"
+ end
+ end
+end
diff --git a/lib/capistrano/tasks/logs.cap b/lib/capistrano/tasks/logs.cap
new file mode 100755
index 000000000..7bbfac2e4
--- /dev/null
+++ b/lib/capistrano/tasks/logs.cap
@@ -0,0 +1,14 @@
+namespace :logs do
+ task :tail, :file do |t, args|
+ if args[:file]
+ on roles(:app) do
+ execute "tail -f #{shared_path}/log/#{args[:file]}.log"
+ end
+ else
+ puts "please specify a logfile e.g: 'rake logs:tail[logfile]"
+ puts "will tail 'shared_path/log/logfile.log'"
+ puts "remember if you use zsh you'll need to format it as:"
+ puts "rake 'logs:tail[logfile]' (single quotes)"
+ end
+ end
+end
diff --git a/lib/capistrano/tasks/restart.cap b/lib/capistrano/tasks/restart.cap
new file mode 100644
index 000000000..ff8c028f0
--- /dev/null
+++ b/lib/capistrano/tasks/restart.cap
@@ -0,0 +1,10 @@
+namespace :deploy do
+ desc 'Commands for unicorn application'
+ %w(start stop force-stop restart upgrade reopen-logs).each do |command|
+ task command.to_sym do
+ on roles(:app), in: :sequence, wait: 5 do
+ sudo "/etc/init.d/unicorn_#{fetch(:full_app_name)} #{command}"
+ end
+ end
+ end
+end
diff --git a/lib/capistrano/tasks/run_tests.cap b/lib/capistrano/tasks/run_tests.cap
new file mode 100755
index 000000000..32f26f9ce
--- /dev/null
+++ b/lib/capistrano/tasks/run_tests.cap
@@ -0,0 +1,18 @@
+namespace :deploy do
+ desc "Runs test before deploying, can't deploy unless they pass"
+ task :run_tests do
+ test_log = "log/capistrano.test.log"
+ tests = fetch(:tests)
+ tests.each do |test|
+ puts "--> Running tests: '#{test}', please wait ..."
+ unless system "bundle exec rspec #{test} > #{test_log} 2>&1"
+ puts "--> Tests: '#{test}' failed. Results in: #{test_log} and below:"
+ system "cat #{test_log}"
+ exit;
+ end
+ puts "--> '#{test}' passed"
+ end
+ puts "--> All tests passed"
+ system "rm #{test_log}"
+ end
+end
diff --git a/lib/capistrano/tasks/setup_config.cap b/lib/capistrano/tasks/setup_config.cap
new file mode 100755
index 000000000..27ba96a12
--- /dev/null
+++ b/lib/capistrano/tasks/setup_config.cap
@@ -0,0 +1,27 @@
+namespace :deploy do
+ task :setup_config do
+ on roles(:app) do
+ # make the config dir
+ execute :mkdir, "-p #{shared_path}/config"
+ full_app_name = fetch(:full_app_name)
+
+ # config files to be uploaded to shared/config, see the
+ # definition of smart_template for details of operation.
+ # Essentially looks for #{filename}.erb in deploy/#{full_app_name}/
+ # and if it isn't there, falls back to deploy/#{shared}. Generally
+ # everything should be in deploy/shared with params which differ
+ # set in the stage files
+ config_files = fetch(:config_files)
+ config_files.each do |file|
+ smart_template file
+ end
+
+ # symlink stuff which should be... symlinked
+ symlinks = fetch(:symlinks)
+
+ symlinks.each do |symlink|
+ sudo "ln -nfs #{shared_path}/config/#{symlink[:source]} #{sub_strings(symlink[:link])}"
+ end
+ end
+ end
+end
diff --git a/lib/capistrano/template.rb b/lib/capistrano/template.rb
new file mode 100755
index 000000000..07bd0be37
--- /dev/null
+++ b/lib/capistrano/template.rb
@@ -0,0 +1,32 @@
+# will first try and copy the file:
+# config/deploy/#{full_app_name}/#{from}.erb
+# to:
+# shared/config/to
+# if the original source path doesn exist then it will
+# search in:
+# config/deploy/shared/#{from}.erb
+# this allows files which are common to all enviros to
+# come from a single source while allowing specific
+# ones to be over-ridden
+# if the target file name is the same as the source then
+# the second parameter can be left out
+def smart_template(from, to=nil)
+ to ||= from
+ full_to_path = "#{shared_path}/config/#{to}"
+ if from_erb_path = template_file(from)
+ from_erb = StringIO.new(ERB.new(File.read(from_erb_path)).result(binding))
+ upload! from_erb, full_to_path
+ info "copying: #{from_erb} to: #{full_to_path}"
+ else
+ error "error #{from} not found"
+ end
+end
+
+def template_file(name)
+ if File.exist?((file = "config/deploy/#{fetch(:full_app_name)}/#{name}.erb"))
+ return file
+ elsif File.exist?((file = "config/deploy/shared/#{name}.erb"))
+ return file
+ end
+ return nil
+end