Module: Capistrano::ReDeploy

Defined in:
lib/capistrano-redeploy.rb,
lib/capistrano-redeploy/version.rb

Constant Summary collapse

VERSION =
"0.1.0"

Class Method Summary collapse

Class Method Details

.extended(configuration) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/capistrano-redeploy.rb', line 5

def self.extended(configuration)
  configuration.load {
    namespace(:redeploy) {
      desc("Redeploy current running application.")
      task(:default, :except => { :no_release => true }) {
        update
      }

      task(:update, :except => { :no_release => true }) {
        transaction do
          update_code
          finalize_update
        end
      }

      _cset(:redeploy_path) { current_release }
      task(:update_code, :except => { :no_release => true }) {
        begin
          tmpdir = capture("mktemp -d /tmp/redeploy.XXXXXXXXXX").strip
          run("rm -rf #{tmpdir.dump} && mkdir -p #{tmpdir}")
          deploy!(tmpdir)
          redeploy!(tmpdir, redeploy_path)
        ensure
          run("rm -rf #{tmpdir.dump}")
        end
      }
      on(:load) {
        if fetch(:redeploy_use_assets, false)
          before "redeploy:finalize_update",   "deploy:assets:symlink"
          after  "redeploy:update_code",       "deploy:assets:precompile"
          before "redeploy:assets:precompile", "deploy:assets:update_asset_mtimes"
        end
      }

      # Return a copy of given object
      # Unlike Object#dup, this also duplicates instance variables.
      def _middle_copy(object)
        o = object.clone
        object.instance_variables.each do |k|
          v = object.instance_variable_get(k)
          o.instance_variable_set(k, v ? v.clone : v)
        end
        o
      end

      _cset(:redeploy_variables, {})
      def deploy!(destination, options={})
        begin
          releases_path = capture("mktemp -d /tmp/releases.XXXXXXXXXX", options).strip
          release_path = File.join(releases_path, release_name)
          run("rm -rf #{releases_path.dump} && mkdir -p #{releases_path.dump}", options)
          c = _middle_copy(top)
          c.instance_eval do
            set(:deploy_to, File.dirname(releases_path))
            set(:releases_path, releases_path)
            set(:release_path, release_path)
            set(:revision) { source.head }
            set(:source) { ::Capistrano::Deploy::SCM.new(scm, self) }
            set(:real_revision) { source.local.query_revision(revision) { |cmd| with_env("LC_ALL", "C") { run_locally(cmd) } } }
            set(:strategy) { ::Capistrano::Deploy::Strategy.new(deploy_via, self) }
            # merge variables
            redeploy_variables.each do |key, val|
              set(key, val)
            end
            logger.debug("Fetching source for re-deployment from #{repository} (#{scm}) via #{deploy_via}.")
            strategy.deploy!
          end
          run("rsync -lrpt #{(release_path + "/").dump} #{destination.dump}", options)
        ensure
          run("rm -rf #{releases_path.dump}", options)
        end
      end

      _cset(:redeploy_children, %w(public))
      _cset(:redeploy_exclusions, %w(assets system))
      _cset(:redeploy_path_map) {
        s = fetch(:redeploy_source, ".").to_s
        d = fetch(:redeploy_destination, ".").to_s
        Hash[redeploy_children.map { |c| [File.join(s, c), File.join(d, c)] }]
      }
      def absolute_path_map(source, destination)
        map = redeploy_path_map.map { |s_subdir, d_subdir|
          [ File.expand_path(File.join(source, s_subdir)), File.expand_path(File.join(destination, d_subdir)) ]
        }
        Hash[map]
      end

      def redeploy!(source, destination, options={})
        exclusions = redeploy_exclusions.map { |e| "--exclude=#{e.dump}" }.join(" ")
        absolute_path_map(source, destination).each do |s, d|
          logger.debug("Re-deploying from `#{s}' to `#{d}'.")
          run("mkdir -p #{s.dump} #{d.dump}", options)
          run("rsync -lrpt #{exclusions} #{(s + "/").dump} #{d.dump}", options)
        end
      end

      task(:finalize_update, :except => { :no_release => true }) {
        escaped_release = redeploy_path.to_s.shellescape
        commands = []
        commands << "chmod -R -- g+w #{escaped_release}" if fetch(:group_writable, true)

        # mkdir -p is making sure that the directories are there for some SCM's that don't
        # save empty folders
        shared_children.map do |dir|
          d = dir.shellescape
          if (dir.rindex('/')) then
            commands += ["rm -rf -- #{escaped_release}/#{d}",
                         "mkdir -p -- #{escaped_release}/#{dir.slice(0..(dir.rindex('/'))).shellescape}"]
          else
            commands << "rm -rf -- #{escaped_release}/#{d}"
          end
          commands << "ln -s -- #{shared_path}/#{dir.split('/').last.shellescape} #{escaped_release}/#{d}"
        end

        run commands.join(' && ') if commands.any?

        if fetch(:normalize_asset_timestamps, true)
          stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
          asset_paths = fetch(:public_children, %w(images stylesheets javascripts)).map { |p| "#{escaped_release}/public/#{p}" }
          run("find #{asset_paths.join(" ")} -exec touch -t #{stamp} -- {} ';'; true",
              :env => { "TZ" => "UTC" }) if asset_paths.any?
        end
      }
    }
  }
end