capistrano 2.1.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +7 -0
  4. data/CHANGELOG.md +89 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +674 -0
  7. data/README.md +226 -0
  8. data/Rakefile +5 -0
  9. data/bin/cap +2 -3
  10. data/bin/capify +7 -77
  11. data/capistrano-public_cert.pem +22 -0
  12. data/capistrano.gemspec +35 -0
  13. data/features/deploy.feature +52 -0
  14. data/features/installation.feature +16 -0
  15. data/features/remote_file_task.feature +14 -0
  16. data/features/step_definitions/assertions.rb +90 -0
  17. data/features/step_definitions/cap_commands.rb +8 -0
  18. data/features/step_definitions/setup.rb +25 -0
  19. data/features/support/env.rb +12 -0
  20. data/features/support/remote_command_helpers.rb +20 -0
  21. data/lib/Capfile +3 -0
  22. data/lib/capistrano/all.rb +16 -0
  23. data/lib/capistrano/application.rb +60 -0
  24. data/lib/capistrano/configuration/question.rb +42 -0
  25. data/lib/capistrano/configuration/server.rb +133 -0
  26. data/lib/capistrano/configuration/servers/role_filter.rb +86 -0
  27. data/lib/capistrano/configuration/servers.rb +53 -58
  28. data/lib/capistrano/configuration.rb +84 -30
  29. data/lib/capistrano/console.rb +1 -0
  30. data/lib/capistrano/defaults.rb +13 -0
  31. data/lib/capistrano/deploy.rb +3 -0
  32. data/lib/capistrano/dotfile.rb +3 -0
  33. data/lib/capistrano/dsl/env.rb +64 -0
  34. data/lib/capistrano/dsl/paths.rb +94 -0
  35. data/lib/capistrano/dsl/stages.rb +15 -0
  36. data/lib/capistrano/dsl/task_enhancements.rb +53 -0
  37. data/lib/capistrano/dsl.rb +48 -0
  38. data/lib/capistrano/git.rb +1 -0
  39. data/lib/capistrano/hg.rb +1 -0
  40. data/lib/capistrano/i18n.rb +34 -0
  41. data/lib/capistrano/install.rb +1 -0
  42. data/lib/capistrano/setup.rb +21 -0
  43. data/lib/capistrano/tasks/console.rake +21 -0
  44. data/lib/capistrano/tasks/deploy.rake +204 -0
  45. data/lib/capistrano/tasks/framework.rake +67 -0
  46. data/lib/capistrano/tasks/git.rake +62 -0
  47. data/lib/capistrano/tasks/hg.rake +39 -0
  48. data/lib/capistrano/tasks/install.rake +39 -0
  49. data/lib/capistrano/templates/Capfile +26 -0
  50. data/lib/capistrano/templates/deploy.rb.erb +40 -0
  51. data/lib/capistrano/templates/stage.rb.erb +42 -0
  52. data/lib/capistrano/version.rb +1 -20
  53. data/lib/capistrano/version_validator.rb +37 -0
  54. data/lib/capistrano.rb +0 -2
  55. data/spec/integration/dsl_spec.rb +344 -0
  56. data/spec/integration_spec_helper.rb +7 -0
  57. data/spec/lib/capistrano/application_spec.rb +61 -0
  58. data/spec/lib/capistrano/configuration/question_spec.rb +54 -0
  59. data/spec/lib/capistrano/configuration/server_spec.rb +249 -0
  60. data/spec/lib/capistrano/configuration/servers/role_filter_spec.rb +140 -0
  61. data/spec/lib/capistrano/configuration/servers_spec.rb +184 -0
  62. data/spec/lib/capistrano/configuration_spec.rb +101 -0
  63. data/spec/lib/capistrano/dsl/env_spec.rb +10 -0
  64. data/spec/lib/capistrano/dsl/paths_spec.rb +69 -0
  65. data/spec/lib/capistrano/dsl_spec.rb +63 -0
  66. data/spec/lib/capistrano/version_validator_spec.rb +103 -0
  67. data/spec/lib/capistrano_spec.rb +8 -0
  68. data/spec/spec_helper.rb +15 -0
  69. data/spec/support/.gitignore +1 -0
  70. data/spec/support/Vagrantfile +13 -0
  71. data/spec/support/matchers.rb +5 -0
  72. data/spec/support/tasks/database.cap +11 -0
  73. data/spec/support/test_app.rb +138 -0
  74. metadata +251 -179
  75. data/CHANGELOG +0 -512
  76. data/MIT-LICENSE +0 -20
  77. data/README +0 -43
  78. data/examples/sample.rb +0 -14
  79. data/lib/capistrano/callback.rb +0 -45
  80. data/lib/capistrano/cli/execute.rb +0 -82
  81. data/lib/capistrano/cli/help.rb +0 -102
  82. data/lib/capistrano/cli/help.txt +0 -53
  83. data/lib/capistrano/cli/options.rb +0 -183
  84. data/lib/capistrano/cli/ui.rb +0 -28
  85. data/lib/capistrano/cli.rb +0 -47
  86. data/lib/capistrano/command.rb +0 -161
  87. data/lib/capistrano/configuration/actions/file_transfer.rb +0 -35
  88. data/lib/capistrano/configuration/actions/inspect.rb +0 -46
  89. data/lib/capistrano/configuration/actions/invocation.rb +0 -134
  90. data/lib/capistrano/configuration/callbacks.rb +0 -148
  91. data/lib/capistrano/configuration/connections.rb +0 -159
  92. data/lib/capistrano/configuration/execution.rb +0 -126
  93. data/lib/capistrano/configuration/loading.rb +0 -198
  94. data/lib/capistrano/configuration/namespaces.rb +0 -196
  95. data/lib/capistrano/configuration/roles.rb +0 -51
  96. data/lib/capistrano/configuration/variables.rb +0 -127
  97. data/lib/capistrano/errors.rb +0 -15
  98. data/lib/capistrano/extensions.rb +0 -57
  99. data/lib/capistrano/gateway.rb +0 -131
  100. data/lib/capistrano/logger.rb +0 -59
  101. data/lib/capistrano/recipes/compat.rb +0 -32
  102. data/lib/capistrano/recipes/deploy/dependencies.rb +0 -44
  103. data/lib/capistrano/recipes/deploy/local_dependency.rb +0 -46
  104. data/lib/capistrano/recipes/deploy/remote_dependency.rb +0 -96
  105. data/lib/capistrano/recipes/deploy/scm/accurev.rb +0 -169
  106. data/lib/capistrano/recipes/deploy/scm/base.rb +0 -192
  107. data/lib/capistrano/recipes/deploy/scm/bzr.rb +0 -86
  108. data/lib/capistrano/recipes/deploy/scm/cvs.rb +0 -151
  109. data/lib/capistrano/recipes/deploy/scm/darcs.rb +0 -85
  110. data/lib/capistrano/recipes/deploy/scm/git.rb +0 -191
  111. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +0 -129
  112. data/lib/capistrano/recipes/deploy/scm/perforce.rb +0 -126
  113. data/lib/capistrano/recipes/deploy/scm/subversion.rb +0 -114
  114. data/lib/capistrano/recipes/deploy/scm.rb +0 -19
  115. data/lib/capistrano/recipes/deploy/strategy/base.rb +0 -64
  116. data/lib/capistrano/recipes/deploy/strategy/checkout.rb +0 -20
  117. data/lib/capistrano/recipes/deploy/strategy/copy.rb +0 -144
  118. data/lib/capistrano/recipes/deploy/strategy/export.rb +0 -20
  119. data/lib/capistrano/recipes/deploy/strategy/remote.rb +0 -52
  120. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +0 -47
  121. data/lib/capistrano/recipes/deploy/strategy.rb +0 -19
  122. data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +0 -53
  123. data/lib/capistrano/recipes/deploy.rb +0 -494
  124. data/lib/capistrano/recipes/standard.rb +0 -37
  125. data/lib/capistrano/recipes/templates/maintenance.rhtml +0 -53
  126. data/lib/capistrano/recipes/upgrade.rb +0 -33
  127. data/lib/capistrano/server_definition.rb +0 -51
  128. data/lib/capistrano/shell.rb +0 -256
  129. data/lib/capistrano/ssh.rb +0 -109
  130. data/lib/capistrano/task_definition.rb +0 -69
  131. data/lib/capistrano/upload.rb +0 -146
  132. data/test/cli/execute_test.rb +0 -132
  133. data/test/cli/help_test.rb +0 -139
  134. data/test/cli/options_test.rb +0 -226
  135. data/test/cli/ui_test.rb +0 -28
  136. data/test/cli_test.rb +0 -17
  137. data/test/command_test.rb +0 -309
  138. data/test/configuration/actions/file_transfer_test.rb +0 -40
  139. data/test/configuration/actions/inspect_test.rb +0 -62
  140. data/test/configuration/actions/invocation_test.rb +0 -202
  141. data/test/configuration/callbacks_test.rb +0 -206
  142. data/test/configuration/connections_test.rb +0 -288
  143. data/test/configuration/execution_test.rb +0 -159
  144. data/test/configuration/loading_test.rb +0 -127
  145. data/test/configuration/namespace_dsl_test.rb +0 -297
  146. data/test/configuration/roles_test.rb +0 -47
  147. data/test/configuration/servers_test.rb +0 -90
  148. data/test/configuration/variables_test.rb +0 -180
  149. data/test/configuration_test.rb +0 -81
  150. data/test/deploy/scm/accurev_test.rb +0 -23
  151. data/test/deploy/scm/base_test.rb +0 -55
  152. data/test/deploy/scm/git_test.rb +0 -112
  153. data/test/deploy/strategy/copy_test.rb +0 -147
  154. data/test/extensions_test.rb +0 -69
  155. data/test/fixtures/cli_integration.rb +0 -5
  156. data/test/fixtures/config.rb +0 -5
  157. data/test/fixtures/custom.rb +0 -3
  158. data/test/gateway_test.rb +0 -167
  159. data/test/logger_test.rb +0 -123
  160. data/test/server_definition_test.rb +0 -108
  161. data/test/shell_test.rb +0 -64
  162. data/test/ssh_test.rb +0 -97
  163. data/test/task_definition_test.rb +0 -101
  164. data/test/upload_test.rb +0 -131
  165. data/test/utils.rb +0 -42
  166. data/test/version_test.rb +0 -24
@@ -1,131 +0,0 @@
1
- if RUBY_VERSION == "1.8.6"
2
- begin
3
- require 'fastthread'
4
- rescue LoadError
5
- warn "You are running Ruby 1.8.6, which has a bug in its threading implementation."
6
- warn "You are liable to encounter deadlocks running Capistrano, unless you install"
7
- warn "the fastthread library, which is available as a gem:"
8
- warn " gem install fastthread"
9
- end
10
- end
11
-
12
- require 'thread'
13
- require 'capistrano/errors'
14
- require 'capistrano/ssh'
15
- require 'capistrano/server_definition'
16
-
17
- Thread.abort_on_exception = true
18
-
19
- module Capistrano
20
-
21
- # Black magic. It uses threads and Net::SSH to set up a connection to a
22
- # gateway server, through which connections to other servers may be
23
- # tunnelled.
24
- #
25
- # It is used internally by Capistrano, but may be useful on its own, as well.
26
- #
27
- # Usage:
28
- #
29
- # gateway = Capistrano::Gateway.new(Capistrano::ServerDefinition.new('gateway.example.com'))
30
- #
31
- # sess1 = gateway.connect_to(Capistrano::ServerDefinition.new('hidden.example.com'))
32
- # sess2 = gateway.connect_to(Capistrano::ServerDefinition.new('other.example.com'))
33
- class Gateway
34
- # The Thread instance driving the gateway connection.
35
- attr_reader :thread
36
-
37
- # The Net::SSH session representing the gateway connection.
38
- attr_reader :session
39
-
40
- MAX_PORT = 65535
41
- MIN_PORT = 1024
42
-
43
- def initialize(server, options={}) #:nodoc:
44
- @options = options
45
- @next_port = MAX_PORT
46
- @terminate_thread = false
47
- @port_guard = Mutex.new
48
-
49
- mutex = Mutex.new
50
- waiter = ConditionVariable.new
51
-
52
- mutex.synchronize do
53
- @thread = Thread.new do
54
- logger.trace "starting connection to gateway `#{server}'" if logger
55
- SSH.connect(server, @options) do |@session|
56
- logger.trace "gateway connection established" if logger
57
- mutex.synchronize { waiter.signal }
58
- @session.loop do
59
- !@terminate_thread
60
- end
61
- end
62
- end
63
-
64
- waiter.wait(mutex)
65
- end
66
- end
67
-
68
- # Shuts down all forwarded connections and terminates the gateway.
69
- def shutdown!
70
- # cancel all active forward channels
71
- session.forward.active_locals.each do |lport, host, port|
72
- session.forward.cancel_local(lport)
73
- end
74
-
75
- # terminate the gateway thread
76
- @terminate_thread = true
77
-
78
- # wait for the gateway thread to stop
79
- thread.join
80
- end
81
-
82
- # Connects to the given server by opening a forwarded port from the local
83
- # host to the server, via the gateway, and then opens and returns a new
84
- # Net::SSH connection via that port.
85
- def connect_to(server)
86
- connection = nil
87
- logger.debug "establishing connection to `#{server}' via gateway" if logger
88
- local_port = next_port
89
-
90
- thread = Thread.new do
91
- begin
92
- local_host = ServerDefinition.new("127.0.0.1", :user => server.user, :port => local_port)
93
- session.forward.local(local_port, server.host, server.port || 22)
94
- connection = SSH.connect(local_host, @options)
95
- connection.xserver = server
96
- logger.trace "connected: `#{server}' (via gateway)" if logger
97
- rescue Errno::EADDRINUSE
98
- local_port = next_port
99
- retry
100
- rescue Exception => e
101
- warn "#{e.class}: #{e.message}"
102
- warn e.backtrace.join("\n")
103
- end
104
- end
105
-
106
- thread.join
107
- if connection.nil?
108
- error = ConnectionError.new("could not establish connection to `#{server}'")
109
- error.hosts = [server]
110
- raise error
111
- end
112
-
113
- connection
114
- end
115
-
116
- private
117
-
118
- def logger
119
- @options[:logger]
120
- end
121
-
122
- def next_port
123
- @port_guard.synchronize do
124
- port = @next_port
125
- @next_port -= 1
126
- @next_port = MAX_PORT if @next_port < MIN_PORT
127
- port
128
- end
129
- end
130
- end
131
- end
@@ -1,59 +0,0 @@
1
- module Capistrano
2
- class Logger #:nodoc:
3
- attr_accessor :level
4
- attr_reader :device
5
-
6
- IMPORTANT = 0
7
- INFO = 1
8
- DEBUG = 2
9
- TRACE = 3
10
-
11
- MAX_LEVEL = 3
12
-
13
- def initialize(options={})
14
- output = options[:output] || $stderr
15
- if output.respond_to?(:puts)
16
- @device = output
17
- else
18
- @device = File.open(output.to_str, "a")
19
- @needs_close = true
20
- end
21
-
22
- @options = options
23
- @level = 0
24
- end
25
-
26
- def close
27
- device.close if @needs_close
28
- end
29
-
30
- def log(level, message, line_prefix=nil)
31
- if level <= self.level
32
- indent = "%*s" % [MAX_LEVEL, "*" * (MAX_LEVEL - level)]
33
- message.each do |line|
34
- if line_prefix
35
- device.puts "#{indent} [#{line_prefix}] #{line.strip}\n"
36
- else
37
- device.puts "#{indent} #{line.strip}\n"
38
- end
39
- end
40
- end
41
- end
42
-
43
- def important(message, line_prefix=nil)
44
- log(IMPORTANT, message, line_prefix)
45
- end
46
-
47
- def info(message, line_prefix=nil)
48
- log(INFO, message, line_prefix)
49
- end
50
-
51
- def debug(message, line_prefix=nil)
52
- log(DEBUG, message, line_prefix)
53
- end
54
-
55
- def trace(message, line_prefix=nil)
56
- log(TRACE, message, line_prefix)
57
- end
58
- end
59
- end
@@ -1,32 +0,0 @@
1
- # A collection of compatibility scripts, to ease the transition between
2
- # Capistrano 1.x and Capistrano 2.x.
3
-
4
- # Depends on the deployment system
5
- load 'deploy'
6
-
7
- map = { "diff_from_last_deploy" => "deploy:pending:diff",
8
- "update" => "deploy:update",
9
- "update_code" => "deploy:update_code",
10
- "symlink" => "deploy:symlink",
11
- "restart" => "deploy:restart",
12
- "rollback" => "deploy:rollback",
13
- "cleanup" => "deploy:cleanup",
14
- "disable_web" => "deploy:web:disable",
15
- "enable_web" => "deploy:web:enable",
16
- "cold_deploy" => "deploy:cold",
17
- "deploy_with_migrations" => "deploy:migrations" }
18
-
19
- map.each do |old, new|
20
- desc "DEPRECATED: See #{new}."
21
- eval "task(#{old.inspect}) do
22
- warn \"[DEPRECATED] `#{old}' is deprecated. Use `#{new}' instead.\"
23
- find_and_execute_task(#{new.inspect})
24
- end"
25
- end
26
-
27
- desc "DEPRECATED: See deploy:start."
28
- task :spinner do
29
- warn "[DEPRECATED] `spinner' is deprecated. Use `deploy:start' instead."
30
- set :runner, fetch(:spinner_user, "app")
31
- deploy.start
32
- end
@@ -1,44 +0,0 @@
1
- require 'capistrano/recipes/deploy/local_dependency'
2
- require 'capistrano/recipes/deploy/remote_dependency'
3
-
4
- module Capistrano
5
- module Deploy
6
- class Dependencies
7
- include Enumerable
8
-
9
- attr_reader :configuration
10
-
11
- def initialize(configuration)
12
- @configuration = configuration
13
- @dependencies = []
14
- yield self if block_given?
15
- end
16
-
17
- def check
18
- yield self
19
- self
20
- end
21
-
22
- def remote
23
- dep = RemoteDependency.new(configuration)
24
- @dependencies << dep
25
- dep
26
- end
27
-
28
- def local
29
- dep = LocalDependency.new(configuration)
30
- @dependencies << dep
31
- dep
32
- end
33
-
34
- def each
35
- @dependencies.each { |d| yield d }
36
- self
37
- end
38
-
39
- def pass?
40
- all? { |d| d.pass? }
41
- end
42
- end
43
- end
44
- end
@@ -1,46 +0,0 @@
1
- module Capistrano
2
- module Deploy
3
- class LocalDependency
4
- attr_reader :configuration
5
- attr_reader :message
6
-
7
- def initialize(configuration)
8
- @configuration = configuration
9
- @success = true
10
- end
11
-
12
- def command(command)
13
- @message ||= "`#{command}' could not be found in the path on the local host"
14
- @success = find_in_path(command)
15
- self
16
- end
17
-
18
- def or(message)
19
- @message = message
20
- self
21
- end
22
-
23
- def pass?
24
- @success
25
- end
26
-
27
- private
28
-
29
- # Searches the path, looking for the given utility. If an executable
30
- # file is found that matches the parameter, this returns true.
31
- def find_in_path(utility)
32
- path = (ENV['PATH'] || "").split(File::PATH_SEPARATOR)
33
- suffixes = RUBY_PLATFORM =~ /mswin/ ? %w(.bat .exe .com .cmd) : [""]
34
-
35
- path.each do |dir|
36
- suffixes.each do |sfx|
37
- file = File.join(dir, utility + sfx)
38
- return true if File.executable?(file)
39
- end
40
- end
41
-
42
- false
43
- end
44
- end
45
- end
46
- end
@@ -1,96 +0,0 @@
1
- module Capistrano
2
- module Deploy
3
- class RemoteDependency
4
- attr_reader :configuration
5
- attr_reader :hosts
6
-
7
- def initialize(configuration)
8
- @configuration = configuration
9
- @success = true
10
- end
11
-
12
- def directory(path, options={})
13
- @message ||= "`#{path}' is not a directory"
14
- try("test -d #{path}", options)
15
- self
16
- end
17
-
18
- def writable(path, options={})
19
- @message ||= "`#{path}' is not writable"
20
- try("test -w #{path}", options)
21
- self
22
- end
23
-
24
- def command(command, options={})
25
- @message ||= "`#{command}' could not be found in the path"
26
- try("which #{command}", options)
27
- self
28
- end
29
-
30
- def gem(name, version, options={})
31
- @message ||= "gem `#{name}' #{version} could not be found"
32
- gem_cmd = configuration.fetch(:gem_command, "gem")
33
- try("#{gem_cmd} specification --version '#{version}' #{name} 2>&1 | awk 'BEGIN { s = 0 } /^name:/ { s = 1; exit }; END { if(s == 0) exit 1 }'", options)
34
- self
35
- end
36
-
37
- def match(command, expect, options={})
38
- expect = Regexp.new(Regexp.escape(expect.to_s)) unless expect.is_a?(Regexp)
39
-
40
- output_per_server = {}
41
- try("#{command} ", options) do |ch, stream, out|
42
- output_per_server[ch[:server]] ||= ''
43
- output_per_server[ch[:server]] += out
44
- end
45
-
46
- # It is possible for some of these commands to return a status != 0
47
- # (for example, rake --version exits with a 1). For this check we
48
- # just care if the output matches, so we reset the success flag.
49
- @success = true
50
-
51
- errored_hosts = []
52
- output_per_server.each_pair do |server, output|
53
- next if output =~ expect
54
- errored_hosts << server
55
- end
56
-
57
- if errored_hosts.any?
58
- @hosts = errored_hosts.join(', ')
59
- output = output_per_server[errored_hosts.first]
60
- @message = "the output #{output.inspect} from #{command.inspect} did not match #{expect.inspect}"
61
- @success = false
62
- end
63
-
64
- self
65
- end
66
-
67
- def or(message)
68
- @message = message
69
- self
70
- end
71
-
72
- def pass?
73
- @success
74
- end
75
-
76
- def message
77
- s = @message.dup
78
- s << " (#{@hosts})" if @hosts && @hosts.any?
79
- s
80
- end
81
-
82
- private
83
-
84
- def try(command, options)
85
- return unless @success # short-circuit evaluation
86
- configuration.run(command, options) do |ch,stream,out|
87
- warn "#{ch[:server]}: #{out}" if stream == :err
88
- yield ch, stream, out if block_given?
89
- end
90
- rescue Capistrano::CommandError => e
91
- @success = false
92
- @hosts = e.hosts.join(', ')
93
- end
94
- end
95
- end
96
- end
@@ -1,169 +0,0 @@
1
- require 'capistrano/recipes/deploy/scm/base'
2
- require 'rexml/xpath'
3
- require 'rexml/document'
4
-
5
- module Capistrano
6
- module Deploy
7
- module SCM
8
- # Accurev bridge for use by Capistrano. This implementation does not
9
- # implement all features of a Capistrano SCM module. The ones that are
10
- # left out are either exceedingly difficult to implement with Accurev
11
- # or are considered bad form.
12
- #
13
- # When using this module in a project, the following variables are used:
14
- # * :repository - This should match the depot that code lives in. If your code
15
- # exists in a subdirectory, you can append the path depot.
16
- # eg. foo-depot/bar_dir
17
- # * :stream - The stream in the depot that code should be pulled from. If
18
- # left blank, the depot stream will be used
19
- # * :revision - Should be in the form 'stream/transaction'.
20
- class Accurev < Base
21
- include REXML
22
- default_command 'accurev'
23
-
24
- # Defines pseudo-revision value for the most recent changes to be deployed.
25
- def head
26
- "#{stream}/highest"
27
- end
28
-
29
- # Given an Accurev revision identifier, this method returns an identifier that
30
- # can be used for later SCM calls. This returned identifier will not
31
- # change as a result of further SCM activity.
32
- def query_revision(revision)
33
- internal_revision = InternalRevision.parse(revision)
34
- return revision unless internal_revision.psuedo_revision?
35
-
36
- logger.debug("Querying for real revision for #{internal_revision}")
37
- rev_stream = internal_revision.stream
38
-
39
- logger.debug("Determining what type of stream #{rev_stream} is...")
40
- stream_xml = yield show_streams_for(rev_stream)
41
- stream_doc = Document.new(stream_xml)
42
- type = XPath.first(stream_doc, '//streams/stream/@type').value
43
-
44
- case type
45
- when 'snapshot'
46
- InternalRevision.new(rev_stream, 'highest').to_s
47
- else
48
- logger.debug("Getting latest transaction id in #{rev_stream}")
49
- # Doing another yield for a second Accurev call. Hopefully this is ok.
50
- hist_xml = yield scm(:hist, '-ftx', '-s', rev_stream, '-t', 'now.1')
51
- hist_doc = Document.new(hist_xml)
52
- transaction_id = XPath.first(hist_doc, '//AcResponse/transaction/@id').value
53
- InternalRevision.new(stream, transaction_id).to_s
54
- end
55
- end
56
-
57
- # Pops a copy of the code for the specified Accurev revision identifier.
58
- # The revision identifier is represented as a stream & transaction ID combo.
59
- # Accurev can only pop a particular transaction if a stream is created on the server
60
- # with a time basis of that transaction id. Therefore, we will create a stream with
61
- # the required criteria and pop that.
62
- def export(revision_id, destination)
63
- revision = InternalRevision.parse(revision_id)
64
- logger.debug("Exporting #{revision.stream}/#{revision.transaction_id} to #{destination}")
65
-
66
- commands = [
67
- change_or_create_stream("#{revision.stream}-capistrano-deploy", revision),
68
- "mkdir -p #{destination}",
69
- scm_quiet(:pop, "-Rv #{stream}", "-L #{destination}", "'/./#{subdir}'")
70
- ]
71
- if subdir
72
- commands.push(
73
- "mv #{destination}/#{subdir}/* #{destination}",
74
- "rm -rf #{File.join(destination, subdir)}"
75
- )
76
- end
77
- commands.join(' && ')
78
- end
79
-
80
- # Returns the command needed to show the changes that exist between the two revisions.
81
- def log(from, to=head)
82
- logger.info("Getting transactions between #{from} and #{to}")
83
- from_rev = InternalRevision.parse(from)
84
- to_rev = InternalRevision.parse(to)
85
-
86
- [
87
- scm(:hist, '-s', from_rev.stream, '-t', "#{to_rev.transaction_id}-#{from_rev.transaction_id}"),
88
- "sed -e '/transaction #{from_rev.transaction_id}/ { Q }'"
89
- ].join(' | ')
90
- end
91
-
92
- # Returns the command needed to show the diff between what is deployed and what is
93
- # pending. Because Accurev can not do this task without creating some streams,
94
- # two time basis streams will be created for the purposes of doing the diff.
95
- def diff(from, to=head)
96
- from = InternalRevision.parse(from)
97
- to = InternalRevision.parse(to)
98
-
99
- from_stream = "#{from.stream}-capistrano-diff-from"
100
- to_stream = "#{to.stream}-capistrano-diff-to"
101
-
102
- [
103
- change_or_create_stream(from_stream, from),
104
- change_or_create_stream(to_stream, to),
105
- scm(:diff, '-v', from_stream, '-V', to_stream, '-a')
106
- ].join(' && ')
107
- end
108
-
109
- private
110
- def depot
111
- repository.split('/')[0]
112
- end
113
-
114
- def stream
115
- variable(:stream) || depot
116
- end
117
-
118
- def subdir
119
- repository.split('/')[1..-1].join('/') unless repository.index('/').nil?
120
- end
121
-
122
- def change_or_create_stream(name, revision)
123
- [
124
- scm_quiet(:mkstream, '-b', revision.stream, '-s', name, '-t', revision.transaction_id),
125
- scm_quiet(:chstream, '-b', revision.stream, '-s', name, '-t', revision.transaction_id)
126
- ].join('; ')
127
- end
128
-
129
- def show_streams_for(stream)
130
- scm :show, '-fx', '-s', stream, :streams
131
- end
132
-
133
- def scm_quiet(*args)
134
- scm(*args) + (variable(:scm_verbose) ? '' : '&> /dev/null')
135
- end
136
-
137
- class InternalRevision
138
- attr_reader :stream, :transaction_id
139
-
140
- def self.parse(string)
141
- match = /([^\/]+)(\/(.+)){0,1}/.match(string)
142
- raise "Unrecognized revision identifier: #{string}" unless match
143
-
144
- stream = match[1]
145
- transaction_id = match[3] || 'highest'
146
- InternalRevision.new(stream, transaction_id)
147
- end
148
-
149
- def initialize(stream, transaction_id)
150
- @stream = stream
151
- @transaction_id = transaction_id
152
- end
153
-
154
- def psuedo_revision?
155
- @transaction_id == 'highest'
156
- end
157
-
158
- def to_s
159
- "#{stream}/#{transaction_id}"
160
- end
161
-
162
- def ==(other)
163
- (stream == other.stream) && (transaction_id == other.transaction_id)
164
- end
165
- end
166
- end
167
- end
168
- end
169
- end