edploy 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +92 -0
- data/README.rdoc.fast_remote_cache +48 -0
- data/Rakefile +1 -0
- data/TODO.TXT +17 -0
- data/bin/dploy +15 -0
- data/bin/dploygen +39 -0
- data/bin/dployify +29 -0
- data/edploy.gemspec +21 -0
- data/lib/capistrano/ext/mailer.rb +90 -0
- data/lib/capistrano/ext/multistage.rb +59 -0
- data/lib/capistrano/ext/output_catcher.rb +97 -0
- data/lib/capistrano/ext/output_hooks.rb +54 -0
- data/lib/capistrano/recipes/deploy/strategy/fast_remote_cache.rb +44 -0
- data/lib/capistrano/recipes/deploy/strategy/utilities/copy.rb +53 -0
- data/lib/edploy/environment_capistrano.rb +19 -0
- data/lib/edploy/recipes/apache.rb +8 -0
- data/lib/edploy/recipes/bundle_install.rb +15 -0
- data/lib/edploy/recipes/fast_remote_cache.rb +43 -0
- data/lib/edploy/recipes/fast_remote_cache_extensions.rb +11 -0
- data/lib/edploy/recipes/hooks.rb +24 -0
- data/lib/edploy/recipes/production.rb +56 -0
- data/lib/edploy/recipes/rails.rb +5 -0
- data/lib/edploy/recipes/setup_project.rb +26 -0
- data/lib/edploy/recipes/smart_deploy.rb +47 -0
- data/lib/edploy/recipes/templates.rb +60 -0
- data/lib/edploy/recipes/web_disable_enable.rb +32 -0
- data/lib/edploy.rb +17 -0
- data/lib/edployify/configuration.rb +140 -0
- data/lib/edployify/preferences.rb +176 -0
- data/lib/edployify/project.rb +69 -0
- data/lib/edployify/template.rb +84 -0
- data/lib/edployify.rb +6 -0
- data/lib/edployscripts/archive.rb +27 -0
- data/lib/edployscripts/environment_scripts.rb +47 -0
- data/lib/edployscripts/file_handling.rb +28 -0
- data/lib/edployscripts/git.rb +27 -0
- data/lib/edployscripts.rb +6 -0
- data/templates/config/edploy/copy/extract/.gitkeep +0 -0
- data/templates/config/edploy/copy/init_scripts/.gitkeep +0 -0
- data/templates/config/edploy/copy/project_files/.gitkeep +0 -0
- data/templates/config/edploy/scripts/after_deploy_setup/.gitkeep +0 -0
- data/templates/config/edploy/scripts/after_deploy_symlink/passenger/clear_file_cache.erb +7 -0
- data/templates/config/edploy/scripts/after_deploy_update_code/make_symlinks.erb +74 -0
- data/templates/config/edploy/scripts/edploygen/01_bundle_install.erb +5 -0
- data/templates/config/edploy/scripts/edploygen/01_prepare_servers.post.erb +12 -0
- data/templates/config/edploy/scripts/edploygen/02_whenever_configs.erb +36 -0
- data/templates/config/edploy/templates/passenger/activate.rb.erb +2 -0
- data/templates/config/edploy/templates/passenger/after_config.rb.erb +2 -0
- data/templates/config/edploy/templates/passenger/apache_vhost.erb.erb +107 -0
- data/templates/config/edploy/templates/passenger/config.rb.erb +3 -0
- data/templates/config/edploy/templates/passenger/deactivate.rb.erb +1 -0
- data/templates/config/edploy/whenever/generic.rb.erb +12 -0
- metadata +113 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# ---------------------------------------------------------------------------
|
2
|
+
# A simple copy script for doing hard links and symbolic links instead of
|
3
|
+
# explicit copies. Some OS's will already have a utility to do this, but
|
4
|
+
# some won't; this file suffices in either case.
|
5
|
+
#
|
6
|
+
# Usage: ruby copy.rb <source> <target> <exclude> ...
|
7
|
+
#
|
8
|
+
# The <source> directory is recursively descended, and hard links to all of
|
9
|
+
# the files are created in corresponding locations under <target>. Symbolic
|
10
|
+
# links in <source> map to symbolic links in <target> that point to the same
|
11
|
+
# destination.
|
12
|
+
#
|
13
|
+
# All arguments after <target> are taken to be exclude patterns. Any file
|
14
|
+
# or directory in <source> that matches any of those patterns will be
|
15
|
+
# skipped, and will thus not be present in <target>.
|
16
|
+
# ---------------------------------------------------------------------------
|
17
|
+
# This file is distributed under the terms of the MIT license by 37signals,
|
18
|
+
# LLC, and is copyright (c) 2008 by the same. See the LICENSE file distributed
|
19
|
+
# with this file for the complete text of the license.
|
20
|
+
# ---------------------------------------------------------------------------
|
21
|
+
require 'fileutils'
|
22
|
+
|
23
|
+
from = ARGV.shift or abort "need source directory"
|
24
|
+
to = ARGV.shift or abort "need target directory"
|
25
|
+
|
26
|
+
exclude = ARGV
|
27
|
+
|
28
|
+
from = File.expand_path(from)
|
29
|
+
to = File.expand_path(to)
|
30
|
+
|
31
|
+
Dir.chdir(from) do
|
32
|
+
FileUtils.mkdir_p(to)
|
33
|
+
queue = Dir.glob("*", File::FNM_DOTMATCH)
|
34
|
+
while queue.any?
|
35
|
+
item = queue.shift
|
36
|
+
name = File.basename(item)
|
37
|
+
|
38
|
+
next if name == "." || name == ".."
|
39
|
+
next if exclude.any? { |pattern| File.fnmatch(pattern, item) }
|
40
|
+
|
41
|
+
source = File.join(from, item)
|
42
|
+
target = File.join(to, item)
|
43
|
+
|
44
|
+
if File.symlink?(item)
|
45
|
+
FileUtils.ln_s(File.readlink(source), target)
|
46
|
+
elsif File.directory?(item)
|
47
|
+
queue += Dir.glob("#{item}/*", File::FNM_DOTMATCH)
|
48
|
+
FileUtils.mkdir_p(target, :mode => File.stat(item).mode)
|
49
|
+
else
|
50
|
+
FileUtils.ln(source, target)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
if ENV['BUNDLE_GEMFILE'].nil? || ENV['BUNDLE_BIN_PATH'].nil?
|
2
|
+
abort "You should run this with bundle exec!"
|
3
|
+
elsif !File.exists?(File.join(Dir.getwd, 'Capfile'))
|
4
|
+
abort "You should run this from the rails root!"
|
5
|
+
end
|
6
|
+
|
7
|
+
set :rails_root, Dir.getwd
|
8
|
+
|
9
|
+
require 'Syck'
|
10
|
+
|
11
|
+
Syck.load_file(File.join(rails_root, 'config', 'edploy', 'edploy.yml')).each do |key, value|
|
12
|
+
if key.to_sym == :ssh_options
|
13
|
+
value.each do |ssh_option, value|
|
14
|
+
ssh_options[ssh_option.to_sym] = value
|
15
|
+
end
|
16
|
+
else
|
17
|
+
set key.to_sym, value
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
set :bundle_path, "vendor/bundle"
|
2
|
+
set :bundle_without, "development test"
|
3
|
+
|
4
|
+
namespace :edploy do
|
5
|
+
after 'deploy:update_code', 'edploy:bundle_install'
|
6
|
+
task :bundle_install do
|
7
|
+
commands = []
|
8
|
+
[ bundle_path, ".bundle" ].uniq.each do |link_path|
|
9
|
+
commands << "mkdir -p #{shared_path}/#{link_path}"
|
10
|
+
commands << "ln -nfs #{shared_path}/#{link_path} #{latest_release}/#{link_path}"
|
11
|
+
end
|
12
|
+
commands << "cd #{latest_release} ; bundle install --path #{shared_path}/#{bundle_path} --local --without #{bundle_without} || bundle install --path #{shared_path}/#{bundle_path} --without #{bundle_without}"
|
13
|
+
run commands.join(' ; ')
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# ---------------------------------------------------------------------------
|
2
|
+
# This is a recipe definition file for Capistrano. The tasks are documented
|
3
|
+
# below.
|
4
|
+
# ---------------------------------------------------------------------------
|
5
|
+
# This file is distributed under the terms of the MIT license by 37signals,
|
6
|
+
# LLC, and is copyright (c) 2008 by the same. See the LICENSE file distributed
|
7
|
+
# with this file for the complete text of the license.
|
8
|
+
# ---------------------------------------------------------------------------
|
9
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
10
|
+
|
11
|
+
namespace :fast_remote_cache do
|
12
|
+
|
13
|
+
desc <<-DESC
|
14
|
+
Perform any setup required by fast_remote_cache. This is called
|
15
|
+
automatically after deploy:setup, but may be invoked manually to configure
|
16
|
+
a new machine. It is also necessary to invoke when you are switching to the
|
17
|
+
fast_remote_cache strategy for the first time.
|
18
|
+
DESC
|
19
|
+
task :setup, :except => { :no_release => true } do
|
20
|
+
if deploy_via == :fast_remote_cache
|
21
|
+
strategy.setup!
|
22
|
+
else
|
23
|
+
logger.important "you're including the fast_remote_cache strategy, but not using it!"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc <<-DESC
|
28
|
+
Updates the remote cache. This is handy for either priming a new box so
|
29
|
+
the cache is all set for the first deploy, or for preparing for a large
|
30
|
+
deploy by making sure the cache is updated before the deploy goes through.
|
31
|
+
Either way, this will happen automatically as part of a deploy; this task
|
32
|
+
is purely convenience for giving admins more control over the deployment.
|
33
|
+
DESC
|
34
|
+
task :prepare, :except => { :no_release => true } do
|
35
|
+
if deploy_via == :fast_remote_cache
|
36
|
+
strategy.prepare!
|
37
|
+
else
|
38
|
+
logger.important "#{current_task.fully_qualified_name} only works with the fast_remote_cache strategy"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
after "deploy:setup", "fast_remote_cache:setup"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
namespace :edploy do
|
2
|
+
before 'fast_remote_cache:prepare', 'edploy:gc'
|
3
|
+
task :gc do
|
4
|
+
run "cd #{shared_path}/cached-copy ; git gc"
|
5
|
+
end
|
6
|
+
|
7
|
+
after 'fast_remote_cache:prepare', 'edploy:pre_bundle'
|
8
|
+
task :pre_bundle do
|
9
|
+
run "cd #{shared_path}/cached-copy ; bundle install --path #{shared_path}/vendor/bundle --local --without development test || bundle install --path #{shared_path}/vendor/bundle --without development test"
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
namespace :edploy do
|
2
|
+
|
3
|
+
%w(deploy:update_code deploy:symlink deploy:setup).each do |task_name|
|
4
|
+
[ :before, :after ].each do |milestone|
|
5
|
+
|
6
|
+
subdir = "#{milestone}_#{task_name.gsub(/:/, '_')}"
|
7
|
+
Dir[File.join(rails_root, 'config', 'edploy', 'scripts', subdir, "**", "*")].sort.each do |full_entry|
|
8
|
+
next if File.directory?(full_entry)
|
9
|
+
|
10
|
+
entry = full_entry[File.join(rails_root, 'config', 'edploy', 'scripts', subdir, "/").length..-1]
|
11
|
+
roles = (entry.include?('/') ? File.dirname(entry).split('_').map(&:to_sym) : nil)
|
12
|
+
options = {}
|
13
|
+
options[:roles] = roles if roles
|
14
|
+
script_name = File.basename(entry).to_sym
|
15
|
+
|
16
|
+
self.send(milestone, task_name, "edploy:#{script_name}")
|
17
|
+
task script_name, options do
|
18
|
+
run "cd #{latest_release} ; RAILS_ENV=#{rails_env} PROJECT_PATH=#{latest_release} DEPLOY_USER=#{user} DEPLOY_GROUP=#{group} bundle exec config/edploy/scripts/#{subdir}/#{entry}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
after 'production', 'setup_production'
|
2
|
+
|
3
|
+
task :setup_production do
|
4
|
+
set :branch, "production"
|
5
|
+
set :rails_env, 'production'
|
6
|
+
|
7
|
+
EVEN_PASSENGER_HOSTS = find_servers(:roles => :passenger).each_with_index.map { |s, i| i % 2 == 1 ? s : nil }.compact
|
8
|
+
ODD_PASSENGER_HOSTS = find_servers(:roles => :passenger).each_with_index.map { |s, i| i % 2 == 0 ? s : nil }.compact
|
9
|
+
EVEN_DB_HOSTS = find_servers(:roles => :db).each_with_index.map { |s, i| i % 2 == 1 ? s : nil }.compact
|
10
|
+
ODD_DB_HOSTS = find_servers(:roles => :db).each_with_index.map { |s, i| i % 2 == 0 ? s : nil }.compact
|
11
|
+
|
12
|
+
def filter_hosts(hostfilter)
|
13
|
+
old_hostfilter = ENV['HOSTFILTER']
|
14
|
+
ENV['HOSTFILTER'] = hostfilter.to_s
|
15
|
+
yield
|
16
|
+
if old_hostfilter.nil?
|
17
|
+
ENV.delete('HOSTFILTER')
|
18
|
+
else
|
19
|
+
ENV['HOSTFILTER'] = old_hostfilter
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
namespace :deploy do
|
24
|
+
desc "phased deploy of app on production"
|
25
|
+
task :zero do
|
26
|
+
[ ODD_PASSENGER_HOSTS + ODD_DB_HOSTS, EVEN_PASSENGER_HOSTS + EVEN_DB_HOSTS ].each do |hostlist|
|
27
|
+
filter_hosts(hostlist.join(',')) do
|
28
|
+
top.deploy.zero_downtime
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
task :zero_downtime do
|
34
|
+
top.deploy.update
|
35
|
+
top.deploy.restart
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
namespace :edploy do
|
41
|
+
namespace :apache do
|
42
|
+
desc "phased restart of apache on production"
|
43
|
+
task :restart, :roles => :passenger do
|
44
|
+
[ ODD_PASSENGER_HOSTS, EVEN_PASSENGER_HOSTS ].each do |hostlist|
|
45
|
+
filter_hosts(hostlist.join(',')) do
|
46
|
+
top.deploy.apache.actual_restart
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
task :actual_restart, :roles => :passenger do
|
52
|
+
run "apache2ctl configtest && sudo /etc/init.d/apache2 restart"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path('../../../edployscripts/archive', __FILE__)
|
2
|
+
require File.expand_path('../../../edployscripts/file_handling', __FILE__)
|
3
|
+
|
4
|
+
namespace :edploy do
|
5
|
+
|
6
|
+
after 'deploy:setup', 'edploy:setup_project'
|
7
|
+
task :setup_project do
|
8
|
+
extract_path = File.expand_path('../../config/copy/extract', __FILE__)
|
9
|
+
init_scripts_path = File.expand_path('../../config/copy/init_scripts', __FILE__)
|
10
|
+
|
11
|
+
Edploy::FileHandling.iterate_dir_items(extract_path) do |dirname, basename, relative_entry, fqpn|
|
12
|
+
Edploy::FileHandling.upload_file(fqpn, "#{shared_path}/tmp") do |tmp_file|
|
13
|
+
target_dir = dirname.gsub(/HOME/, '~')
|
14
|
+
Edploy::Archive.extract tmp_file, target_dir, :remove => true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Edploy::FileHandling.iterate_dir_items(init_scripts_path) do |dirname, basename, relative_entry, fqpn|
|
19
|
+
Edploy::FileHandling.upload_file(fqpn, "#{shared_path}/tmp") do |tmp_file|
|
20
|
+
script_name = "#{application}_#{stage}_#{basename}"
|
21
|
+
system("sudo sh -c 'test -f /etc/init.d/#{script_name} || { cp #{tmp_file} /etc/init.d/#{script_name} ; chmod 755 /etc/init.d/#{script_name} ; update-rc.d #{script_name} defaults ; }'")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
namespace :deploy do
|
2
|
+
|
3
|
+
desc <<-DESC
|
4
|
+
Deploys your project. Does smart deploy if this is activated.
|
5
|
+
DESC
|
6
|
+
task :default do
|
7
|
+
if exists?(:smart_deploy) && smart_deploy
|
8
|
+
top.deploy.smart
|
9
|
+
else
|
10
|
+
top.deploy.code
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
desc <<-DESC
|
15
|
+
Deploys your project. This calls both `update' and `restart'. Note that \
|
16
|
+
this will generally only work for applications that have already been deployed \
|
17
|
+
once. For a "cold" deploy, you'll want to take a look at the `deploy:cold' \
|
18
|
+
task, which handles the cold start specifically.
|
19
|
+
DESC
|
20
|
+
task :code do
|
21
|
+
update
|
22
|
+
restart
|
23
|
+
end
|
24
|
+
|
25
|
+
desc <<-DESC
|
26
|
+
Deploys your project. Automagically figures out if deploy:setup, deploy:cold, \
|
27
|
+
deploy:migrations or deploy:default is needed.
|
28
|
+
DESC
|
29
|
+
task :smart do
|
30
|
+
project_is_setup = (capture("find #{deploy_to} -mindepth 1 -maxdepth 1 -type d \\( -name 'releases' -o -name 'shared' \\) 2>/dev/null | wc -l").to_i == 2)
|
31
|
+
top.deploy.setup unless project_is_setup
|
32
|
+
at_least_one_release_exists = capture("find #{releases_path} -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l").to_i > 0
|
33
|
+
unless at_least_one_release_exists
|
34
|
+
top.deploy.cold
|
35
|
+
else
|
36
|
+
remote_migrations = capture("find #{latest_release}/db/migrate -type f -name '*.rb' -exec basename {} .rb \\; 2>/dev/null || true").split("\n")
|
37
|
+
local_migrations = `find db/migrate -type f -name '*.rb' -exec basename {} .rb \\; 2>/dev/null || true`.split("\n")
|
38
|
+
pending_migrations = !(local_migrations - remote_migrations).empty?
|
39
|
+
if pending_migrations
|
40
|
+
top.deploy.migrations
|
41
|
+
else
|
42
|
+
top.deploy.code
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fileutils'
|
3
|
+
def render_template(local_template)
|
4
|
+
template_name = File.basename(local_template)
|
5
|
+
file_name = File.basename(template_name, '.erb')
|
6
|
+
app = File.basename(File.dirname(local_template))
|
7
|
+
path = File.join(deploy_to, app, file_name)
|
8
|
+
|
9
|
+
template = ERB.new(IO.read(local_template))
|
10
|
+
rendered_template = template.result(binding)
|
11
|
+
|
12
|
+
tmp_path = File.join('/tmp/', "#{File.basename(path)}-$CAPISTRANO:HOST$")
|
13
|
+
put(rendered_template, tmp_path, :mode => 0644)
|
14
|
+
send run_method, <<-CMD
|
15
|
+
sh -c "install -m#{sprintf("%3o",0644)} #{tmp_path} #{path} &&
|
16
|
+
rm -f #{tmp_path}"
|
17
|
+
CMD
|
18
|
+
end
|
19
|
+
|
20
|
+
TEMPLATES = {}
|
21
|
+
Dir[File.expand_path("../../config/templates/*", __FILE__)].each do |app|
|
22
|
+
TEMPLATES[File.basename(app).to_sym] = {
|
23
|
+
:templates => Dir[File.join(app, '*.erb')],
|
24
|
+
:config => File.join(app, 'config.rb'),
|
25
|
+
:after_config => File.join(app, 'after_config.rb'),
|
26
|
+
:activate => File.join(app, 'activate.rb'),
|
27
|
+
:deactivate => File.join(app, 'deactivate.rb'),
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
TEMPLATES.each do |application, data|
|
32
|
+
eval File.read(data[:config]) if File.exists?(data[:config])
|
33
|
+
|
34
|
+
namespace :edploy do
|
35
|
+
namespace application do
|
36
|
+
after 'deploy:setup', "edploy:#{application}:config"
|
37
|
+
desc "Push #{application} config files to server"
|
38
|
+
task :config, :roles => application do
|
39
|
+
data[:templates].each do |template|
|
40
|
+
render_template(template)
|
41
|
+
end
|
42
|
+
eval File.read(data[:after_config]) if File.exists?(data[:after_config])
|
43
|
+
end
|
44
|
+
|
45
|
+
if File.exists?(data[:activate])
|
46
|
+
desc "Activate #{application}"
|
47
|
+
task :activate, :roles => application do
|
48
|
+
eval File.read(data[:activate])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if File.exists?(data[:deactivate])
|
53
|
+
desc "Deactivate #{application}"
|
54
|
+
task :deactivate, :roles => application do
|
55
|
+
eval File.read(data[:deactivate])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
namespace :edploy do
|
2
|
+
namespace :web do
|
3
|
+
namespace :disable do
|
4
|
+
before 'deploy', 'edploy:web:disable:maintenance'
|
5
|
+
before 'deploy:migrations', 'edploy:web:disable:maintenance'
|
6
|
+
task :maintenance, :roles => :passenger do
|
7
|
+
on_rollback { run "rm -f #{shared_path}/system/maintenance.html" }
|
8
|
+
run "cp #{current_path}/config/edploy/copy/project_files/#{stage}/$(hostname)/public/maintenance.html #{shared_path}/system/ 2>/dev/null || cp #{current_path}/config/edploy/copy/project_files/#{stage}/public/maintenance.html #{shared_path}/system/ 2>/dev/null || cp #{current_path}/public/maintenance.html #{shared_path}/system/ 2>/dev/null"
|
9
|
+
end
|
10
|
+
|
11
|
+
before 'deploy:zero_downtime', 'edploy:web:disable:zero_downtime'
|
12
|
+
task :zero_downtime, :roles => :passenger do
|
13
|
+
run "mv /var/www/check.txt /var/www/check2.txt 2>/dev/null || true"
|
14
|
+
# sudo "/sbin/iptables -D INPUT -p tcp -s 95.170.77.132/26 -m state --state NEW --dport 443 -j ACCEPT 2>/dev/null || true"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
namespace :enable do
|
19
|
+
after 'deploy', 'edploy:web:enable:maintenance'
|
20
|
+
after 'deploy:migrations', 'edploy:web:enable:maintenance'
|
21
|
+
task :maintenance, :roles => :passenger do
|
22
|
+
run "rm -f #{shared_path}/system/maintenance.html"
|
23
|
+
end
|
24
|
+
|
25
|
+
after 'deploy:zero_downtime', 'edploy:web:enable:zero_downtime'
|
26
|
+
task :zero_downtime, :roles => :passenger do
|
27
|
+
run "mv /var/www/check2.txt /var/www/check.txt 2>/dev/null || true"
|
28
|
+
# sudo "/sbin/iptables -A INPUT -p tcp -s 95.170.77.132/26 -m state --state NEW --dport 443 -j ACCEPT"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/edploy.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Copyright (c) 2009-2012 by Ewout Vonk. All rights reserved.
|
2
|
+
|
3
|
+
# prevent loading when called by Bundler, only load when called by capistrano
|
4
|
+
if caller.any? { |callstack_line| callstack_line =~ /^Capfile:/ }
|
5
|
+
unless Capistrano::Configuration.respond_to?(:instance)
|
6
|
+
abort "edploy requires Capistrano 2"
|
7
|
+
end
|
8
|
+
|
9
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
10
|
+
load File.expand_path('../edploy/environment_capistrano.rb', __FILE__)
|
11
|
+
load File.expand_path('../capistrano/ext/multistage.rb', __FILE__)
|
12
|
+
# load File.expand_path('../capistrano/ext/mailer.rb', __FILE__)
|
13
|
+
# load File.expand_path('../capistrano/ext/output_catcher.rb', __FILE__)
|
14
|
+
# load File.expand_path('../capistrano/ext/output_hooks.rb', __FILE__)
|
15
|
+
Dir[File.expand_path('../edploy/recipes/*.rb', __FILE__)].each { |plugin| load(plugin) }
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'highline'
|
2
|
+
require 'yaml'
|
3
|
+
require 'fileutils'
|
4
|
+
# work around problem where HighLine detects an eof on $stdin and raises an err
|
5
|
+
HighLine.track_eof = false
|
6
|
+
|
7
|
+
module Edployify
|
8
|
+
|
9
|
+
class Configuration
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def configure
|
14
|
+
instance.configure
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_binding
|
18
|
+
instance.get_binding
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def instance
|
24
|
+
@@instance ||= self.new
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_binding
|
30
|
+
binding
|
31
|
+
end
|
32
|
+
|
33
|
+
CONFIGURATION_KEYS = {
|
34
|
+
:application => { :default => Proc.new { |c| File.basename(::RAILS_ROOT).gsub(/[^A-Za-z0-9_]/, '_').gsub(/_+/, '_') }, :type => Symbol },
|
35
|
+
:domain => { :default => Proc.new { |c| "#{c[:application]}.com" } },
|
36
|
+
:scm => { :default => :git, :type => Symbol },
|
37
|
+
:git_shallow_clone => { :default => true, :boolean => true },
|
38
|
+
:repository => { :default => Proc.new { |c| "git@HOSTNAME:#{c[:application]}.git" } },
|
39
|
+
:git_enable_submodules => { :default => false, :boolean => true },
|
40
|
+
:deploy_via => { :default => :fast_remote_cache, :type => Symbol },
|
41
|
+
:deploy_to => { :default => Proc.new { |c| "/apps/#{c[:application]}" } },
|
42
|
+
:user => { :default => 'deploy' },
|
43
|
+
:group => { :default => 'deploy' },
|
44
|
+
:normalize_asset_timestamps => { :default => false, :boolean => true },
|
45
|
+
:paranoid => { :ssh_option => true, :default => false, :boolean => true },
|
46
|
+
:forward_agent => { :ssh_option => true, :default => true, :boolean => true },
|
47
|
+
:notify_email => { :default => Proc.new { |c| "support@#{c[:application]}.com" } }
|
48
|
+
}
|
49
|
+
|
50
|
+
def configure
|
51
|
+
CONFIGURATION_KEYS.each do |variable, options|
|
52
|
+
if self.respond_to?(:"ask_#{variable}")
|
53
|
+
self.send(:"ask_#{variable}", variable, options)
|
54
|
+
elsif options[:ssh_option]
|
55
|
+
ask_ssh_option(variable, options)
|
56
|
+
else
|
57
|
+
ask_value(variable, options)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def ask_repository(variable, options = {})
|
65
|
+
default = base[variable]
|
66
|
+
unless default
|
67
|
+
preset = Edployify::Preferences.request_preset(:use)
|
68
|
+
project = highline.ask("Git project name (without .git): ") { |q| q.default = config[:application] if config[:application] }
|
69
|
+
default = preset.sub(/\{PROJECT\}/, project)
|
70
|
+
options[:default] = default
|
71
|
+
end
|
72
|
+
ask_value(variable, options)
|
73
|
+
end
|
74
|
+
|
75
|
+
def ask_ssh_option(variable, options = {})
|
76
|
+
ask_value(variable, options.merge(:base => config[:ssh_options]))
|
77
|
+
end
|
78
|
+
|
79
|
+
def ask_value(variable, options = {})
|
80
|
+
base = options[:base] || config
|
81
|
+
default = base[variable] || options[:default]
|
82
|
+
if default.is_a?(Proc)
|
83
|
+
default = default.call(config)
|
84
|
+
end
|
85
|
+
|
86
|
+
variable_prompt_name = variable.to_s.gsub(/(^|_)[A-Za-z0-9]/, &:upcase).gsub(/_/, ' ')
|
87
|
+
question_prompt = "#{variable_prompt_name} " # add trailing space so user input happens on the same line
|
88
|
+
|
89
|
+
value = if options[:boolean]
|
90
|
+
highline.agree(question_prompt) do |q|
|
91
|
+
q.default = (default ? "y" : "n") unless default.nil?
|
92
|
+
q.readline = true
|
93
|
+
end
|
94
|
+
else
|
95
|
+
arguments = [question_prompt]
|
96
|
+
arguments << options[:type] if options[:type]
|
97
|
+
highline.ask(*arguments) do |q|
|
98
|
+
q.default = default if default
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
base[variable] = value
|
103
|
+
write_file
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
|
108
|
+
def config
|
109
|
+
unless @config
|
110
|
+
edploy_yml = File.join(::RAILS_ROOT, 'config', 'edploy', 'edploy.yml')
|
111
|
+
FileUtils.touch edploy_yml
|
112
|
+
$edploy_yml = File.open(edploy_yml, "r+")
|
113
|
+
at_exit do
|
114
|
+
$edploy_yml.close unless $edploy_yml.closed?
|
115
|
+
end
|
116
|
+
config_contents = $edploy_yml.read
|
117
|
+
@config = config_contents.length == 0 ? {} : YAML::load(config_contents)
|
118
|
+
$edploy_yml.rewind
|
119
|
+
@config[:ssh_options] = {}
|
120
|
+
end
|
121
|
+
@config
|
122
|
+
end
|
123
|
+
|
124
|
+
def write_file
|
125
|
+
$edploy_yml.write YAML::dump(config)
|
126
|
+
$edploy_yml.truncate $edploy_yml.pos
|
127
|
+
$edploy_yml.rewind
|
128
|
+
end
|
129
|
+
|
130
|
+
def highline
|
131
|
+
@highline ||= HighLine.new
|
132
|
+
end
|
133
|
+
|
134
|
+
def method_missing(method, *args, &block)
|
135
|
+
config[method.to_sym] || config[method.to_s] || super
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|