Skip to content
This repository
Browse code

Capistrano support, built for daemons

  • Loading branch information...
commit 189936caa2f5064048f1aedcd891b14ee1fe9a2c 1 parent 1d76e21
Kenneth Kalmer authored May 26, 2009
9  Manifest.txt
@@ -28,6 +28,13 @@ daemon_generators/cron/USAGE
28 28
 daemon_generators/cron/cron_generator.rb
29 29
 daemon_generators/cron/templates/config/initializers/cron.rb
30 30
 daemon_generators/cron/templates/libexec/daemon.rb
  31
+daemon_generators/deploy_capistrano/USAGE
  32
+daemon_generators/deploy_capistrano/deploy_capistrano_generator.rb
  33
+daemon_generators/deploy_capistrano/templates/Capfile
  34
+daemon_generators/deploy_capistrano/templates/config/deploy.rb
  35
+daemon_generators/deploy_capistrano/templates/config/deploy/production.rb
  36
+daemon_generators/deploy_capistrano/templates/config/deploy/staging.rb
  37
+daemon_generators/deploy_capistrano/templates/config/environments/staging.rb
31 38
 daemon_generators/jabber/USAGE
32 39
 daemon_generators/jabber/jabber_generator.rb
33 40
 daemon_generators/jabber/templates/config/initializers/jabber.rb
@@ -44,6 +51,7 @@ lib/daemon_kit/amqp.rb
44 51
 lib/daemon_kit/application.rb
45 52
 lib/daemon_kit/config.rb
46 53
 lib/daemon_kit/cron.rb
  54
+lib/daemon_kit/deployment/capistrano.rb
47 55
 lib/daemon_kit/error_handlers/base.rb
48 56
 lib/daemon_kit/error_handlers/hoptoad.rb
49 57
 lib/daemon_kit/error_handlers/mail.rb
@@ -75,6 +83,7 @@ test/test_amqp_generator.rb
75 83
 test/test_cron_generator.rb
76 84
 test/test_daemon-kit_generator.rb
77 85
 test/test_daemon_kit_config.rb
  86
+test/test_deploy_capistrano_generator.rb
78 87
 test/test_generator_helper.rb
79 88
 test/test_helper.rb
80 89
 test/test_jabber_generator.rb
24  app_generators/daemon_kit/daemon_kit_generator.rb
@@ -5,10 +5,13 @@ class DaemonKitGenerator < RubiGen::Base
5 5
 
6 6
   VALID_GENERATORS = ['default', 'jabber', 'cron', 'amqp', 'nanite_agent']
7 7
 
  8
+  DEPLOYERS = ['none', 'capistrano']
  9
+
8 10
   default_options :shebang => DEFAULT_SHEBANG, :author => nil
9 11
 
10 12
   attr_reader :daemon_name
11 13
   attr_reader :installer
  14
+  attr_reader :deployer
12 15
 
13 16
   def initialize(runtime_args, runtime_options = {})
14 17
     super
@@ -26,6 +29,12 @@ def manifest
26 29
       exit 1
27 30
     end
28 31
 
  32
+    unless DEPLOYERS.include?( deployer )
  33
+      $stderr.puts "Invalid deployment mechanism: '#{deployer}'."
  34
+      $stderr.puts "Valid deployers are: #{DEPLOYERS.join(', ')}"
  35
+      exit 1
  36
+    end
  37
+
29 38
     script_options = { :chmod => 0755, :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] }
30 39
 
31 40
     record do |m|
@@ -75,6 +84,11 @@ def manifest
75 84
       # Tests
76 85
       m.dependency "install_rspec", [daemon_name], :destination => destination_root, :collision => :force
77 86
 
  87
+      # Deployers
  88
+      unless deployer == 'none'
  89
+        m.dependency "deploy_#{deployer}", [daemon_name], :destination => destination_root, :collision => :force
  90
+      end
  91
+
78 92
       # Others
79 93
       m.directory "log"
80 94
       m.directory "tmp"
@@ -102,12 +116,21 @@ def add_options!(opts)
102 116
       # opts.on("-a", "--author=\"Your Name\"", String,
103 117
       #         "Some comment about this option",
104 118
       #         "Default: none") { |o| options[:author] = o }
  119
+
105 120
       opts.on("-i", "--install=generator", String,
106 121
               "Select a generator to use (other than the default).",
107 122
               "Available generators: #{VALID_GENERATORS.join(', ')}",
108 123
               "Defaults to: default") do |installer|
109 124
         options[:installer] = installer
110 125
       end
  126
+
  127
+      opts.on("-d", "--deploy-with=config", String,
  128
+              "Select an optional deployment mechanism.",
  129
+              "Available deployers: #{DEPLOYERS.join(', ')}",
  130
+              "Defaults to: none") do |deploy|
  131
+        options[:deployer] = deploy
  132
+      end
  133
+
111 134
       opts.on("-r", "--ruby=path", String,
112 135
               "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).",
113 136
               "Default: #{DEFAULT_SHEBANG}") { |x| options[:shebang] = x }
@@ -120,6 +143,7 @@ def extract_options
120 143
       # raw instance variable value.
121 144
       # @author = options[:author]
122 145
       @installer = options[:installer] || 'default'
  146
+      @deployer  = (options[:deployer] || 'none').strip
123 147
     end
124 148
 
125 149
 end
8  daemon-kit.gemspec
@@ -2,17 +2,17 @@
2 2
 
3 3
 Gem::Specification.new do |s|
4 4
   s.name = %q{daemon-kit}
5  
-  s.version = "0.1.6"
  5
+  s.version = "0.1.7"
6 6
 
7 7
   s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8 8
   s.authors = ["Kenneth Kalmer"]
9  
-  s.date = %q{2009-05-25}
  9
+  s.date = %q{2009-05-26}
10 10
   s.default_executable = %q{daemon_kit}
11 11
   s.description = %q{Daemon Kit aims to simplify creating Ruby daemons by providing a sound application skeleton (through a generator), task specific generators (jabber bot, etc) and robust environment management code.  Using simple built-in generators it is easy to created evented and non-evented daemons that perform a multitude of different tasks.  Supported generators:  * Evented and non-evented Jabber Bot (coming next) * Evented and non-evented loops (coming soon) * Queue poller (SQS, AMQP, etc) (coming soon)}
12 12
   s.email = ["kenneth.kalmer@gmail.com"]
13 13
   s.executables = ["daemon_kit"]
14 14
   s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "TODO.txt"]
15  
-  s.files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "TODO.txt", "app_generators/daemon_kit/USAGE", "app_generators/daemon_kit/daemon_kit_generator.rb", "app_generators/daemon_kit/templates/README", "app_generators/daemon_kit/templates/Rakefile", "app_generators/daemon_kit/templates/bin/daemon.erb", "app_generators/daemon_kit/templates/config/boot.rb", "app_generators/daemon_kit/templates/config/environment.rb", "app_generators/daemon_kit/templates/config/environments/development.rb", "app_generators/daemon_kit/templates/config/environments/production.rb", "app_generators/daemon_kit/templates/config/environments/test.rb", "app_generators/daemon_kit/templates/config/post-daemonize/readme", "app_generators/daemon_kit/templates/config/pre-daemonize/readme", "app_generators/daemon_kit/templates/lib/daemon.rb", "app_generators/daemon_kit/templates/libexec/daemon.erb", "bin/daemon_kit", "daemon_generators/amqp/USAGE", "daemon_generators/amqp/amqp_generator.rb", "daemon_generators/amqp/templates/config/amqp.yml", "daemon_generators/amqp/templates/config/initializers/amqp.rb", "daemon_generators/amqp/templates/libexec/daemon.rb", "daemon_generators/cron/USAGE", "daemon_generators/cron/cron_generator.rb", "daemon_generators/cron/templates/config/initializers/cron.rb", "daemon_generators/cron/templates/libexec/daemon.rb", "daemon_generators/jabber/USAGE", "daemon_generators/jabber/jabber_generator.rb", "daemon_generators/jabber/templates/config/initializers/jabber.rb", "daemon_generators/jabber/templates/config/jabber.yml", "daemon_generators/jabber/templates/libexec/daemon.rb", "daemon_generators/nanite_agent/USAGE", "daemon_generators/nanite_agent/nanite_agent_generator.rb", "daemon_generators/nanite_agent/templates/config/initializers/nanite_agent.rb", "daemon_generators/nanite_agent/templates/config/nanite.yml", "daemon_generators/nanite_agent/templates/lib/actors/sample.rb", "daemon_generators/nanite_agent/templates/libexec/daemon.rb", "lib/daemon_kit.rb", "lib/daemon_kit/amqp.rb", "lib/daemon_kit/application.rb", "lib/daemon_kit/config.rb", "lib/daemon_kit/cron.rb", "lib/daemon_kit/error_handlers/base.rb", "lib/daemon_kit/error_handlers/hoptoad.rb", "lib/daemon_kit/error_handlers/mail.rb", "lib/daemon_kit/initializer.rb", "lib/daemon_kit/jabber.rb", "lib/daemon_kit/nanite.rb", "lib/daemon_kit/nanite/agent.rb", "lib/daemon_kit/patches/force_kill_wait.rb", "lib/daemon_kit/safety.rb", "lib/daemon_kit/tasks.rb", "lib/daemon_kit/tasks/environment.rake", "lib/daemon_kit/tasks/framework.rake", "rubygems_generators/install_rspec/USAGE", "rubygems_generators/install_rspec/install_rspec_generator.rb", "rubygems_generators/install_rspec/templates/spec.rb", "rubygems_generators/install_rspec/templates/spec/spec.opts", "rubygems_generators/install_rspec/templates/spec/spec_helper.rb", "rubygems_generators/install_rspec/templates/tasks/rspec.rake", "script/console", "script/destroy", "script/generate", "script/txt2html", "spec/daemon_kit_spec.rb", "spec/initializer_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/rspec.rake", "test/test_amqp_generator.rb", "test/test_cron_generator.rb", "test/test_daemon-kit_generator.rb", "test/test_daemon_kit_config.rb", "test/test_generator_helper.rb", "test/test_helper.rb", "test/test_jabber_generator.rb", "test/test_nanite_agent_generator.rb"]
  15
+  s.files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "TODO.txt", "app_generators/daemon_kit/USAGE", "app_generators/daemon_kit/daemon_kit_generator.rb", "app_generators/daemon_kit/templates/README", "app_generators/daemon_kit/templates/Rakefile", "app_generators/daemon_kit/templates/bin/daemon.erb", "app_generators/daemon_kit/templates/config/boot.rb", "app_generators/daemon_kit/templates/config/environment.rb", "app_generators/daemon_kit/templates/config/environments/development.rb", "app_generators/daemon_kit/templates/config/environments/production.rb", "app_generators/daemon_kit/templates/config/environments/test.rb", "app_generators/daemon_kit/templates/config/post-daemonize/readme", "app_generators/daemon_kit/templates/config/pre-daemonize/readme", "app_generators/daemon_kit/templates/lib/daemon.rb", "app_generators/daemon_kit/templates/libexec/daemon.erb", "bin/daemon_kit", "daemon_generators/amqp/USAGE", "daemon_generators/amqp/amqp_generator.rb", "daemon_generators/amqp/templates/config/amqp.yml", "daemon_generators/amqp/templates/config/initializers/amqp.rb", "daemon_generators/amqp/templates/libexec/daemon.rb", "daemon_generators/cron/USAGE", "daemon_generators/cron/cron_generator.rb", "daemon_generators/cron/templates/config/initializers/cron.rb", "daemon_generators/cron/templates/libexec/daemon.rb", "daemon_generators/deploy_capistrano/USAGE", "daemon_generators/deploy_capistrano/deploy_capistrano_generator.rb", "daemon_generators/deploy_capistrano/templates/Capfile", "daemon_generators/deploy_capistrano/templates/config/deploy.rb", "daemon_generators/deploy_capistrano/templates/config/deploy/production.rb", "daemon_generators/deploy_capistrano/templates/config/deploy/staging.rb", "daemon_generators/deploy_capistrano/templates/config/environments/staging.rb", "daemon_generators/jabber/USAGE", "daemon_generators/jabber/jabber_generator.rb", "daemon_generators/jabber/templates/config/initializers/jabber.rb", "daemon_generators/jabber/templates/config/jabber.yml", "daemon_generators/jabber/templates/libexec/daemon.rb", "daemon_generators/nanite_agent/USAGE", "daemon_generators/nanite_agent/nanite_agent_generator.rb", "daemon_generators/nanite_agent/templates/config/initializers/nanite_agent.rb", "daemon_generators/nanite_agent/templates/config/nanite.yml", "daemon_generators/nanite_agent/templates/lib/actors/sample.rb", "daemon_generators/nanite_agent/templates/libexec/daemon.rb", "lib/daemon_kit.rb", "lib/daemon_kit/amqp.rb", "lib/daemon_kit/application.rb", "lib/daemon_kit/config.rb", "lib/daemon_kit/cron.rb", "lib/daemon_kit/deployment/capistrano.rb", "lib/daemon_kit/error_handlers/base.rb", "lib/daemon_kit/error_handlers/hoptoad.rb", "lib/daemon_kit/error_handlers/mail.rb", "lib/daemon_kit/initializer.rb", "lib/daemon_kit/jabber.rb", "lib/daemon_kit/nanite.rb", "lib/daemon_kit/nanite/agent.rb", "lib/daemon_kit/patches/force_kill_wait.rb", "lib/daemon_kit/safety.rb", "lib/daemon_kit/tasks.rb", "lib/daemon_kit/tasks/environment.rake", "lib/daemon_kit/tasks/framework.rake", "rubygems_generators/install_rspec/USAGE", "rubygems_generators/install_rspec/install_rspec_generator.rb", "rubygems_generators/install_rspec/templates/spec.rb", "rubygems_generators/install_rspec/templates/spec/spec.opts", "rubygems_generators/install_rspec/templates/spec/spec_helper.rb", "rubygems_generators/install_rspec/templates/tasks/rspec.rake", "script/console", "script/destroy", "script/generate", "script/txt2html", "spec/daemon_kit_spec.rb", "spec/initializer_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/rspec.rake", "test/test_amqp_generator.rb", "test/test_cron_generator.rb", "test/test_daemon-kit_generator.rb", "test/test_daemon_kit_config.rb", "test/test_deploy_capistrano_generator.rb", "test/test_generator_helper.rb", "test/test_helper.rb", "test/test_jabber_generator.rb", "test/test_nanite_agent_generator.rb"]
16 16
   s.has_rdoc = true
17 17
   s.homepage = %q{http://kit.rubyforge.org/daemon (coming soon)}
18 18
   s.post_install_message = %q{
@@ -27,7 +27,7 @@ To get started quickly run 'daemon_kit' without any arguments
27 27
   s.rubyforge_project = %q{daemon-kit}
28 28
   s.rubygems_version = %q{1.3.1}
29 29
   s.summary = %q{Daemon Kit aims to simplify creating Ruby daemons by providing a sound application skeleton (through a generator), task specific generators (jabber bot, etc) and robust environment management code.}
30  
-  s.test_files = ["test/test_generator_helper.rb", "test/test_jabber_generator.rb", "test/test_cron_generator.rb", "test/test_amqp_generator.rb", "test/test_nanite_agent_generator.rb", "test/test_daemon-kit_generator.rb", "test/test_daemon_kit_config.rb", "test/test_helper.rb"]
  30
+  s.test_files = ["test/test_generator_helper.rb", "test/test_jabber_generator.rb", "test/test_cron_generator.rb", "test/test_amqp_generator.rb", "test/test_nanite_agent_generator.rb", "test/test_daemon-kit_generator.rb", "test/test_daemon_kit_config.rb", "test/test_helper.rb", "test/test_deploy_capistrano_generator.rb"]
31 31
 
32 32
   if s.respond_to? :specification_version then
33 33
     current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
5  daemon_generators/deploy_capistrano/USAGE
... ...
@@ -0,0 +1,5 @@
  1
+Description:
  2
+  
  3
+  
  4
+Usage:
  5
+  
54  daemon_generators/deploy_capistrano/deploy_capistrano_generator.rb
... ...
@@ -0,0 +1,54 @@
  1
+class DeployCapistranoGenerator < RubiGen::Base
  2
+
  3
+  default_options :author => nil
  4
+
  5
+  attr_reader :name
  6
+
  7
+  def initialize(runtime_args, runtime_options = {})
  8
+    super
  9
+    usage if args.empty?
  10
+    @name = args.shift
  11
+    extract_options
  12
+  end
  13
+
  14
+  def manifest
  15
+    record do |m|
  16
+
  17
+      m.file      "Capfile", "Capfile"
  18
+      m.directory "config"
  19
+      m.template  "config/deploy.rb", "config/deploy.rb"
  20
+      m.directory "config/deploy"
  21
+      m.template  "config/deploy/staging.rb", "config/deploy/staging.rb"
  22
+      m.template  "config/deploy/production.rb", "config/deploy/production.rb"
  23
+      m.directory "config/environments"
  24
+      m.file      "config/environments/staging.rb", "config/environments/staging.rb", :collision => :skip
  25
+    end
  26
+  end
  27
+
  28
+  protected
  29
+    def banner
  30
+      <<-EOS
  31
+Creates a ...
  32
+
  33
+USAGE: #{$0} #{spec.name} name
  34
+EOS
  35
+    end
  36
+
  37
+    def add_options!(opts)
  38
+      # opts.separator ''
  39
+      # opts.separator 'Options:'
  40
+      # For each option below, place the default
  41
+      # at the top of the file next to "default_options"
  42
+      # opts.on("-a", "--author=\"Your Name\"", String,
  43
+      #         "Some comment about this option",
  44
+      #         "Default: none") { |o| options[:author] = o }
  45
+      # opts.on("-v", "--version", "Show the #{File.basename($0)} version number and quit.")
  46
+    end
  47
+
  48
+    def extract_options
  49
+      # for each option, extract it into a local variable (and create an "attr_reader :author" at the top)
  50
+      # Templates can access these value via the attr_reader-generated methods, but not the
  51
+      # raw instance variable value.
  52
+      # @author = options[:author]
  53
+    end
  54
+end
10  daemon_generators/deploy_capistrano/templates/Capfile
... ...
@@ -0,0 +1,10 @@
  1
+unless respond_to?(:namespace) # cap2 differentiator
  2
+  $stderr.puts "Requires capistrano version 2"
  3
+  exit 1
  4
+end
  5
+
  6
+require 'config/boot'
  7
+load DaemonKit.framework_root + '/lib/daemon_kit/deployment/capistrano.rb'
  8
+
  9
+Dir['config/deploy/recipes/*.rb'].each { |plugin| load(plugin) }
  10
+load 'config/deploy.rb'
51  daemon_generators/deploy_capistrano/templates/config/deploy.rb
... ...
@@ -0,0 +1,51 @@
  1
+# Modified capistrano recipe, based on the standard 'deploy' recipe
  2
+# provided by capistrano but without the Rails-specific dependencies
  3
+
  4
+set :stages, %w(staging production)
  5
+set :default_stage, "staging"
  6
+require "capistrano/ext/multistage"
  7
+
  8
+# Set some globals
  9
+default_run_options[:pty] = true
  10
+set :application, "<%= name %>"
  11
+
  12
+# Deployment
  13
+set :deploy_to, "/svc/#{application}"
  14
+#set :user, 'someone'
  15
+
  16
+# Get repo configuration
  17
+set :repository, "git@github.com:yourname/#{application}.git"
  18
+set :scm, "git"
  19
+set :branch, "master"
  20
+set :deploy_via, :remote_cache
  21
+set :git_enable_submodules, 1
  22
+
  23
+# No sudo
  24
+set :use_sudo, false
  25
+
  26
+# File list in the config_files setting will be copied from the
  27
+# 'deploy_to' directory into config, overwriting files from the repo
  28
+# with the same name
  29
+set :config_files, %w{}
  30
+
  31
+# List any work directories here that you need persisted between
  32
+# deployments. They are created in 'deploy_to'/shared and symlinked
  33
+# into the root directory of the deployment.
  34
+set :shared_children, %w{log tmp}
  35
+
  36
+# Record our dependencies
  37
+depend :remote, :gem, "daemon-kit", ">=0.0.0"
  38
+
  39
+# Hook into capistrano's events
  40
+before "deploy:update_code", "deploy:check"
  41
+
  42
+# Create some tasks related to deployment
  43
+namespace :deploy do
  44
+
  45
+  desc "Get the current revision of the deployed code"
  46
+  task :get_current_version do
  47
+    run "cat #{current_path}/REVISION" do |ch, stream, out|
  48
+      puts "Current revision: " + out.chomp
  49
+    end
  50
+  end
  51
+end
6  daemon_generators/deploy_capistrano/templates/config/deploy/production.rb
... ...
@@ -0,0 +1,6 @@
  1
+#set :deploy_to, "/svc/<%= name %>" # defaults to "/u/apps/#{application}"
  2
+#set :user, "<% name %>"            # defaults to the currently logged in user
  3
+set :daemon_env, 'production'
  4
+
  5
+set :domain, 'example.com'
  6
+server domain
6  daemon_generators/deploy_capistrano/templates/config/deploy/staging.rb
... ...
@@ -0,0 +1,6 @@
  1
+#set :deploy_to, "/svc/<%= name %>" # defaults to "/u/apps/#{application}"
  2
+#set :user, "<% name %>"            # defaults to the currently logged in user
  3
+set :daemon_env, 'staging'
  4
+
  5
+set :domain, 'example.com'
  6
+server domain
0  daemon_generators/deploy_capistrano/templates/config/environments/staging.rb
No changes.
2  lib/daemon_kit.rb
@@ -4,7 +4,7 @@
4 4
 require 'rubygems'
5 5
 
6 6
 module DaemonKit
7  
-  VERSION = '0.1.6'
  7
+  VERSION = '0.1.7'
8 8
 
9 9
   autoload :Initializer, 'daemon_kit/initializer'
10 10
   autoload :Application, 'daemon_kit/application'
485  lib/daemon_kit/deployment/capistrano.rb
... ...
@@ -0,0 +1,485 @@
  1
+require 'yaml'
  2
+require 'capistrano/recipes/deploy/scm'
  3
+require 'capistrano/recipes/deploy/strategy'
  4
+
  5
+def _cset(name, *args, &block)
  6
+  unless exists?(name)
  7
+    set(name, *args, &block)
  8
+  end
  9
+end
  10
+
  11
+# =========================================================================
  12
+# These variables MUST be set in the client capfiles. If they are not set,
  13
+# the deploy will fail with an error.
  14
+# =========================================================================
  15
+
  16
+_cset(:application) { abort "Please specify the name of your application, set :application, 'foo'" }
  17
+_cset(:repository)  { abort "Please specify the repository that houses your application's code, set :repository, 'foo'" }
  18
+
  19
+# =========================================================================
  20
+# These variables may be set in the client capfile if their default values
  21
+# are not sufficient.
  22
+# =========================================================================
  23
+
  24
+_cset :scm, :subversion
  25
+_cset :deploy_via, :checkout
  26
+
  27
+_cset(:deploy_to) { "/u/apps/#{application}" }
  28
+_cset(:revision)  { source.head }
  29
+
  30
+# =========================================================================
  31
+# These variables should NOT be changed unless you are very confident in
  32
+# what you are doing. Make sure you understand all the implications of your
  33
+# changes if you do decide to muck with these!
  34
+# =========================================================================
  35
+
  36
+_cset(:source)            { Capistrano::Deploy::SCM.new(scm, self) }
  37
+_cset(:real_revision)     { source.local.query_revision(revision) { |cmd| with_env("LC_ALL", "C") { run_locally(cmd) } } }
  38
+
  39
+_cset(:strategy)          { Capistrano::Deploy::Strategy.new(deploy_via, self) }
  40
+
  41
+_cset(:release_name)      { set :deploy_timestamped, true; Time.now.utc.strftime("%Y%m%d%H%M%S") }
  42
+
  43
+_cset :version_dir,       "releases"
  44
+_cset :shared_dir,        "shared"
  45
+_cset :shared_children,   %w(log tmp)
  46
+_cset :current_dir,       "current"
  47
+
  48
+_cset(:releases_path)     { File.join(deploy_to, version_dir) }
  49
+_cset(:shared_path)       { File.join(deploy_to, shared_dir) }
  50
+_cset(:current_path)      { File.join(deploy_to, current_dir) }
  51
+_cset(:release_path)      { File.join(releases_path, release_name) }
  52
+
  53
+_cset(:releases)          { capture("ls -xt #{releases_path}").split.reverse }
  54
+_cset(:current_release)   { File.join(releases_path, releases.last) }
  55
+_cset(:previous_release)  { releases.length > 1 ? File.join(releases_path, releases[-2]) : nil }
  56
+
  57
+_cset(:current_revision)  { capture("cat #{current_path}/REVISION").chomp }
  58
+_cset(:latest_revision)   { capture("cat #{current_release}/REVISION").chomp }
  59
+_cset(:previous_revision) { capture("cat #{previous_release}/REVISION").chomp }
  60
+
  61
+_cset(:run_method)        { fetch(:use_sudo, true) ? :sudo : :run }
  62
+
  63
+# some tasks, like symlink, need to always point at the latest release, but
  64
+# they can also (occassionally) be called standalone. In the standalone case,
  65
+# the timestamped release_path will be inaccurate, since the directory won't
  66
+# actually exist. This variable lets tasks like symlink work either in the
  67
+# standalone case, or during deployment.
  68
+_cset(:latest_release) { exists?(:deploy_timestamped) ? release_path : current_release }
  69
+
  70
+# =========================================================================
  71
+# These are helper methods that will be available to your recipes.
  72
+# =========================================================================
  73
+
  74
+# Auxiliary helper method for the `deploy:check' task. Lets you set up your
  75
+# own dependencies.
  76
+def depend(location, type, *args)
  77
+  deps = fetch(:dependencies, {})
  78
+  deps[location] ||= {}
  79
+  deps[location][type] ||= []
  80
+  deps[location][type] << args
  81
+  set :dependencies, deps
  82
+end
  83
+
  84
+# Temporarily sets an environment variable, yields to a block, and restores
  85
+# the value when it is done.
  86
+def with_env(name, value)
  87
+  saved, ENV[name] = ENV[name], value
  88
+  yield
  89
+ensure
  90
+  ENV[name] = saved
  91
+end
  92
+
  93
+# logs the command then executes it locally.
  94
+# returns the command output as a string
  95
+def run_locally(cmd)
  96
+  logger.trace "executing locally: #{cmd.inspect}" if logger
  97
+  `#{cmd}`
  98
+end
  99
+
  100
+# If a command is given, this will try to execute the given command, as
  101
+# described below. Otherwise, it will return a string for use in embedding in
  102
+# another command, for executing that command as described below.
  103
+#
  104
+# If :run_method is :sudo (or :use_sudo is true), this executes the given command
  105
+# via +sudo+. Otherwise is uses +run+. If :as is given as a key, it will be
  106
+# passed as the user to sudo as, if using sudo. If the :as key is not given,
  107
+# it will default to whatever the value of the :admin_runner variable is,
  108
+# which (by default) is unset.
  109
+#
  110
+# THUS, if you want to try to run something via sudo, and what to use the
  111
+# root user, you'd just to try_sudo('something'). If you wanted to try_sudo as
  112
+# someone else, you'd just do try_sudo('something', :as => "bob"). If you
  113
+# always wanted sudo to run as a particular user, you could do
  114
+# set(:admin_runner, "bob").
  115
+def try_sudo(*args)
  116
+  options = args.last.is_a?(Hash) ? args.pop : {}
  117
+  command = args.shift
  118
+  raise ArgumentError, "too many arguments" if args.any?
  119
+
  120
+  as = options.fetch(:as, fetch(:admin_runner, nil))
  121
+  via = fetch(:run_method, :sudo)
  122
+  if command
  123
+    invoke_command(command, :via => via, :as => as)
  124
+  elsif via == :sudo
  125
+    sudo(:as => as)
  126
+  else
  127
+    ""
  128
+  end
  129
+end
  130
+
  131
+# Same as sudo, but tries sudo with :as set to the value of the :runner
  132
+# variable (which defaults to "app").
  133
+def try_runner(*args)
  134
+  options = args.last.is_a?(Hash) ? args.pop : {}
  135
+  args << options.merge(:as => fetch(:runner, "app"))
  136
+  try_sudo(*args)
  137
+end
  138
+
  139
+# =========================================================================
  140
+# These are the tasks that are available to help with deploying web apps,
  141
+# and specifically, Rails applications. You can have cap give you a summary
  142
+# of them with `cap -T'.
  143
+# =========================================================================
  144
+
  145
+namespace :deploy do
  146
+  desc <<-DESC
  147
+    Deploys your project. This calls both `update' and `restart'. Note that \
  148
+    this will generally only work for applications that have already been deployed \
  149
+    once. For a "cold" deploy, you'll want to take a look at the `deploy:cold' \
  150
+    task, which handles the cold start specifically.
  151
+  DESC
  152
+  task :default do
  153
+    update
  154
+    restart
  155
+  end
  156
+
  157
+  desc <<-DESC
  158
+    Prepares one or more servers for deployment. Before you can use any \
  159
+    of the Capistrano deployment tasks with your project, you will need to \
  160
+    make sure all of your servers have been prepared with `cap deploy:setup'. When \
  161
+    you add a new server to your cluster, you can easily run the setup task \
  162
+    on just that server by specifying the HOSTS environment variable:
  163
+
  164
+      $ cap HOSTS=new.server.com deploy:setup
  165
+
  166
+    It is safe to run this task on servers that have already been set up; it \
  167
+    will not destroy any deployed revisions or data.
  168
+  DESC
  169
+  task :setup, :except => { :no_release => true } do
  170
+    dirs = [deploy_to, releases_path, shared_path]
  171
+    dirs += shared_children.map { |d| File.join(shared_path, d) }
  172
+    run "#{try_sudo} mkdir -p #{dirs.join(' ')} && #{try_sudo} chmod g+w #{dirs.join(' ')}"
  173
+  end
  174
+
  175
+  desc <<-DESC
  176
+    Copies your project and updates the symlink. It does this in a \
  177
+    transaction, so that if either `update_code' or `symlink' fail, all \
  178
+    changes made to the remote servers will be rolled back, leaving your \
  179
+    system in the same state it was in before `update' was invoked. Usually, \
  180
+    you will want to call `deploy' instead of `update', but `update' can be \
  181
+    handy if you want to deploy, but not immediately restart your application.
  182
+  DESC
  183
+  task :update do
  184
+    transaction do
  185
+      update_code
  186
+      symlink
  187
+    end
  188
+  end
  189
+
  190
+  desc <<-DESC
  191
+    Copies your project to the remote servers. This is the first stage \
  192
+    of any deployment; moving your updated code and assets to the deployment \
  193
+    servers. You will rarely call this task directly, however; instead, you \
  194
+    should call the `deploy' task (to do a complete deploy) or the `update' \
  195
+    task (if you want to perform the `restart' task separately).
  196
+
  197
+    You will need to make sure you set the :scm variable to the source \
  198
+    control software you are using (it defaults to :subversion), and the \
  199
+    :deploy_via variable to the strategy you want to use to deploy (it \
  200
+    defaults to :checkout).
  201
+  DESC
  202
+  task :update_code, :except => { :no_release => true } do
  203
+    on_rollback { run "rm -rf #{release_path}; true" }
  204
+    strategy.deploy!
  205
+    finalize_update
  206
+  end
  207
+
  208
+  desc <<-DESC
  209
+    [internal] Touches up the released code. This is called by update_code \
  210
+    after the basic deploy finishes. It assumes a Rails project was deployed, \
  211
+    so if you are deploying something else, you may want to override this \
  212
+    task with your own environment's requirements.
  213
+
  214
+    This task will make the release group-writable (if the :group_writable \
  215
+    variable is set to true, which is the default). It will then set up \
  216
+    symlinks to the shared directory for the :shared_children \
  217
+    directories, and will lastly touch all assets in public/images, \
  218
+    public/stylesheets, and public/javascripts so that the times are \
  219
+    consistent (so that asset timestamping works).  This touch process \
  220
+    is only carried out if the :normalize_asset_timestamps variable is \
  221
+    set to true, which is the default.
  222
+  DESC
  223
+  task :finalize_update, :except => { :no_release => true } do
  224
+    run "chmod -R g+w #{latest_release}" if fetch(:group_writable, true)
  225
+
  226
+    # mkdir -p is making sure that the directories are there for some SCM's that don't
  227
+    # save empty folders
  228
+    dirs = fetch(:shared_children, [])
  229
+    cmd = dirs.inject([]) do |c,d|
  230
+      c.push "rm -rf #{latest_release}/#{d}"
  231
+      c.push "ln -s #{shared_path}/#{d} #{latest_release}/#{d}"
  232
+      c
  233
+    end
  234
+
  235
+    run cmd.join( ' && ' )
  236
+
  237
+    if fetch(:normalize_asset_timestamps, false)
  238
+      stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
  239
+      asset_paths = %w(images stylesheets javascripts).map { |p| "#{latest_release}/public/#{p}" }.join(" ")
  240
+      run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" }
  241
+    end
  242
+
  243
+    copy_configs
  244
+  end
  245
+
  246
+  desc <<-DESC
  247
+    Copies any shared configuration files from :deploy_to/config into \
  248
+    the current release's config directory. Original files, if present, \
  249
+    are renamed to the same name with .orig appended.
  250
+
  251
+    Specify a list of files by setting :config_files to an array in your \
  252
+    deployment configuration.
  253
+  DESC
  254
+  task :copy_configs do
  255
+    fetch(:config_files, []).each do |f|
  256
+      run "[ -f #{release_path}/config/#{f} ]; mv #{release_path}/config/#{f} #{release_path}/config/#{f}.orig"
  257
+      run "[ -f #{deploy_to}/config/#{f} ]; cp #{deploy_to}/config/#{f} #{release_path}/config/#{f}"
  258
+    end
  259
+  end
  260
+
  261
+  desc <<-DESC
  262
+    Updates the symlink to the most recently deployed version. Capistrano works \
  263
+    by putting each new release of your application in its own directory. When \
  264
+    you deploy a new version, this task's job is to update the `current' symlink \
  265
+    to point at the new version. You will rarely need to call this task \
  266
+    directly; instead, use the `deploy' task (which performs a complete \
  267
+    deploy, including `restart') or the 'update' task (which does everything \
  268
+    except `restart').
  269
+  DESC
  270
+  task :symlink, :except => { :no_release => true } do
  271
+    on_rollback do
  272
+      if previous_release
  273
+        run "rm -f #{current_path}; ln -s #{previous_release} #{current_path}; true"
  274
+      else
  275
+        logger.important "no previous release to rollback to, rollback of symlink skipped"
  276
+      end
  277
+    end
  278
+
  279
+    run "rm -f #{current_path} && ln -s #{latest_release} #{current_path}"
  280
+  end
  281
+
  282
+  desc <<-DESC
  283
+    Copy files to the currently deployed version. This is useful for updating \
  284
+    files piecemeal, such as when you need to quickly deploy only a single \
  285
+    file. Some files, such as updated templates, images, or stylesheets, \
  286
+    might not require a full deploy, and especially in emergency situations \
  287
+    it can be handy to just push the updates to production, quickly.
  288
+
  289
+    To use this task, specify the files and directories you want to copy as a \
  290
+    comma-delimited list in the FILES environment variable. All directories \
  291
+    will be processed recursively, with all files being pushed to the \
  292
+    deployment servers.
  293
+
  294
+      $ cap deploy:upload FILES=templates,controller.rb
  295
+
  296
+    Dir globs are also supported:
  297
+
  298
+      $ cap deploy:upload FILES='config/apache/*.conf'
  299
+  DESC
  300
+  task :upload, :except => { :no_release => true } do
  301
+    files = (ENV["FILES"] || "").split(",").map { |f| Dir[f.strip] }.flatten
  302
+    abort "Please specify at least one file or directory to update (via the FILES environment variable)" if files.empty?
  303
+
  304
+    files.each { |file| top.upload(file, File.join(current_path, file)) }
  305
+  end
  306
+
  307
+  desc <<-DESC
  308
+    Restarts your application. This works by calling the bin/:application \
  309
+    script under the current path with 'restart'
  310
+
  311
+    By default, this will be invoked via sudo as the `app' user. If \
  312
+    you wish to run it as a different user, set the :runner variable to \
  313
+    that user. If you are in an environment where you can't use sudo, set \
  314
+    the :use_sudo variable to false:
  315
+
  316
+      set :use_sudo, false
  317
+  DESC
  318
+  task :restart, :except => { :no_release => true } do
  319
+    try_runner "/usr/bin/env DAEMON_ENV=#{fetch(:daemon_env)} #{current_path}/bin/#{application} restart"
  320
+  end
  321
+
  322
+  namespace :rollback do
  323
+    desc <<-DESC
  324
+      [internal] Points the current symlink at the previous revision.
  325
+      This is called by the rollback sequence, and should rarely (if
  326
+      ever) need to be called directly.
  327
+    DESC
  328
+    task :revision, :except => { :no_release => true } do
  329
+      if previous_release
  330
+        run "rm #{current_path}; ln -s #{previous_release} #{current_path}"
  331
+      else
  332
+        abort "could not rollback the code because there is no prior release"
  333
+      end
  334
+    end
  335
+
  336
+    desc <<-DESC
  337
+      [internal] Removes the most recently deployed release.
  338
+      This is called by the rollback sequence, and should rarely
  339
+      (if ever) need to be called directly.
  340
+    DESC
  341
+    task :cleanup, :except => { :no_release => true } do
  342
+      run "if [ `readlink #{current_path}` != #{current_release} ]; then rm -rf #{current_release}; fi"
  343
+    end
  344
+
  345
+    desc <<-DESC
  346
+      Rolls back to the previously deployed version. The `current' symlink will \
  347
+      be updated to point at the previously deployed version, and then the \
  348
+      current release will be removed from the servers. You'll generally want \
  349
+      to call `rollback' instead, as it performs a `restart' as well.
  350
+    DESC
  351
+    task :code, :except => { :no_release => true } do
  352
+      revision
  353
+      cleanup
  354
+    end
  355
+
  356
+    desc <<-DESC
  357
+      Rolls back to a previous version and restarts. This is handy if you ever \
  358
+      discover that you've deployed a lemon; `cap rollback' and you're right \
  359
+      back where you were, on the previously deployed version.
  360
+    DESC
  361
+    task :default do
  362
+      revision
  363
+      restart
  364
+      cleanup
  365
+    end
  366
+  end
  367
+
  368
+  desc <<-DESC
  369
+    Clean up old releases. By default, the last 5 releases are kept on each \
  370
+    server (though you can change this with the keep_releases variable). All \
  371
+    other deployed revisions are removed from the servers. By default, this \
  372
+    will use sudo to clean up the old releases, but if sudo is not available \
  373
+    for your environment, set the :use_sudo variable to false instead.
  374
+  DESC
  375
+  task :cleanup, :except => { :no_release => true } do
  376
+    count = fetch(:keep_releases, 5).to_i
  377
+    if count >= releases.length
  378
+      logger.important "no old releases to clean up"
  379
+    else
  380
+      logger.info "keeping #{count} of #{releases.length} deployed releases"
  381
+
  382
+      directories = (releases - releases.last(count)).map { |release|
  383
+        File.join(releases_path, release) }.join(" ")
  384
+
  385
+      try_sudo "rm -rf #{directories}"
  386
+    end
  387
+  end
  388
+
  389
+  desc <<-DESC
  390
+    Test deployment dependencies. Checks things like directory permissions, \
  391
+    necessary utilities, and so forth, reporting on the things that appear to \
  392
+    be incorrect or missing. This is good for making sure a deploy has a \
  393
+    chance of working before you actually run `cap deploy'.
  394
+
  395
+    You can define your own dependencies, as well, using the `depend' method:
  396
+
  397
+      depend :remote, :gem, "tzinfo", ">=0.3.3"
  398
+      depend :local, :command, "svn"
  399
+      depend :remote, :directory, "/u/depot/files"
  400
+  DESC
  401
+  task :check, :except => { :no_release => true } do
  402
+    dependencies = strategy.check!
  403
+
  404
+    other = fetch(:dependencies, {})
  405
+    other.each do |location, types|
  406
+      types.each do |type, calls|
  407
+        if type == :gem
  408
+          dependencies.send(location).command(fetch(:gem_command, "gem")).or("`gem' command could not be found. Try setting :gem_command")
  409
+        end
  410
+
  411
+        calls.each do |args|
  412
+          dependencies.send(location).send(type, *args)
  413
+        end
  414
+      end
  415
+    end
  416
+
  417
+    if dependencies.pass?
  418
+      puts "You appear to have all necessary dependencies installed"
  419
+    else
  420
+      puts "The following dependencies failed. Please check them and try again:"
  421
+      dependencies.reject { |d| d.pass? }.each do |d|
  422
+        puts "--> #{d.message}"
  423
+      end
  424
+      abort
  425
+    end
  426
+  end
  427
+
  428
+  desc <<-DESC
  429
+    Deploys and starts a `cold' application. This is useful if you have not \
  430
+    deployed your application before, or if your application is (for some \
  431
+    other reason) not currently running. It will deploy the code, and then \
  432
+    instead of invoking `deploy:restart', it will invoke `deploy:start' to \
  433
+    fire up the application servers.
  434
+  DESC
  435
+  task :cold do
  436
+    update
  437
+    start
  438
+  end
  439
+
  440
+  desc <<-DESC
  441
+    Start the daemon processes. This will attempt to invoke a script \
  442
+    in your application called `bin/:application', with the 'start' parameter.
  443
+
  444
+    By default, the script will be executed via sudo as the `app' user. If \
  445
+    you wish to run it as a different user, set the :runner variable to \
  446
+    that user. If you are in an environment where you can't use sudo, set \
  447
+    the :use_sudo variable to false.
  448
+  DESC
  449
+  task :start do
  450
+    try_runner "/usr/bin/env DAEMON_ENV=#{fetch(:daemon_env)} #{current_path}/bin/#{application} start"
  451
+  end
  452
+
  453
+  desc <<-DESC
  454
+    Stop the daemon processes. This will call 'bin/:application stop'.
  455
+
  456
+    By default, the script will be executed via sudo as the `app' user. If \
  457
+    you wish to run it as a different user, set the :runner variable to \
  458
+    that user. If you are in an environment where you can't use sudo, set \
  459
+    the :use_sudo variable to false.
  460
+  DESC
  461
+  task :stop do
  462
+    try_runner "/usr/bin/env DAEMON_ENV=#{fetch(:daemon_env)} #{current_path}/bin/#{application} stop"
  463
+  end
  464
+
  465
+  namespace :pending do
  466
+    desc <<-DESC
  467
+      Displays the `diff' since your last deploy. This is useful if you want \
  468
+      to examine what changes are about to be deployed. Note that this might \
  469
+      not be supported on all SCM's.
  470
+    DESC
  471
+    task :diff, :except => { :no_release => true } do
  472
+      system(source.local.diff(current_revision))
  473
+    end
  474
+
  475
+    desc <<-DESC
  476
+      Displays the commits since your last deploy. This is good for a summary \
  477
+      of the changes that have occurred since the last deploy. Note that this \
  478
+      might not be supported on all SCM's.
  479
+    DESC
  480
+    task :default, :except => { :no_release => true } do
  481
+      from = source.next_revision(current_revision)
  482
+      system(source.local.log(from))
  483
+    end
  484
+  end
  485
+end
9  test/test_daemon-kit_generator.rb
@@ -53,6 +53,15 @@ def test_generator_without_options
53 53
     assert_directory_exists "tasks"
54 54
     assert_directory_exists "vendor"
55 55
     assert_directory_exists "tmp"
  56
+
  57
+    assert !File.exists?("#{APP_ROOT}/Capfile"), 'Capfile generated, but should not be'
  58
+  end
  59
+
  60
+  def test_generator_with_capistrano_deployer
  61
+    run_generator('daemon_kit', [APP_ROOT, '-d capistrano'], sources)
  62
+
  63
+    assert_generated_file "Capfile"
  64
+    assert_generated_file "config/deploy.rb"
56 65
   end
57 66
 
58 67
   private
49  test/test_deploy_capistrano_generator.rb
... ...
@@ -0,0 +1,49 @@
  1
+require File.join(File.dirname(__FILE__), "test_generator_helper.rb")
  2
+
  3
+
  4
+class TestDeployCapistranoGenerator < Test::Unit::TestCase
  5
+  include RubiGen::GeneratorTestHelper
  6
+
  7
+  def setup
  8
+    bare_setup
  9
+  end
  10
+
  11
+  def teardown
  12
+    bare_teardown
  13
+  end
  14
+
  15
+  # Some generator-related assertions:
  16
+  #   assert_generated_file(name, &block) # block passed the file contents
  17
+  #   assert_directory_exists(name)
  18
+  #   assert_generated_class(name, &block)
  19
+  #   assert_generated_module(name, &block)
  20
+  #   assert_generated_test_for(name, &block)
  21
+  # The assert_generated_(class|module|test_for) &block is passed the body of the class/module within the file
  22
+  #   assert_has_method(body, *methods) # check that the body has a list of methods (methods with parentheses not supported yet)
  23
+  #
  24
+  # Other helper methods are:
  25
+  #   app_root_files - put this in teardown to show files generated by the test method (e.g. p app_root_files)
  26
+  #   bare_setup - place this in setup method to create the APP_ROOT folder for each test
  27
+  #   bare_teardown - place this in teardown method to destroy the TMP_ROOT or APP_ROOT folder after each test
  28
+
  29
+  def test_generator_without_options
  30
+    name = "myapp"
  31
+    run_generator('deploy_capistrano', [name], sources)
  32
+    assert_generated_file   "Capfile"
  33
+    assert_directory_exists "config"
  34
+    assert_generated_file   "config/deploy.rb"
  35
+    assert_directory_exists "config/deploy"
  36
+    assert_generated_file   "config/deploy/staging.rb"
  37
+    assert_generated_file   "config/deploy/production.rb"
  38
+  end
  39
+
  40
+  private
  41
+  def sources
  42
+    [RubiGen::PathSource.new(:test, File.join(File.dirname(__FILE__),"..", generator_path))
  43
+    ]
  44
+  end
  45
+
  46
+  def generator_path
  47
+    "daemon_generators"
  48
+  end
  49
+end

0 notes on commit 189936c

Please sign in to comment.
Something went wrong with that request. Please try again.