capistrano-file-transfer-ext 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -64,7 +64,7 @@ All of the options of `upload` are sensible. In addition, there are some extra o
64
64
  * `:install` It must be either `:always` (the default), or `:if_modified`. If `:if_modified` is given, install the file only if the checksums are different.
65
65
  * `:digest` This is a symbol indicating which algorithm should be used to calculate the checksum of files. `:md5` is default.
66
66
  * `:digest_cmd` The command to calculate checksum of files. `md5sum` is default.
67
- * `:via` specify the method to run commands. `:run` by default. you may need to set this value as `:sudo` if you want to overwrite system files.
67
+ * `:run_method` specify the method to run commands. `:run` by default. you may need to set this value as `:sudo` if you want to overwrite system files.
68
68
 
69
69
 
70
70
  ### `safe_put`
@@ -115,11 +115,11 @@ The direction of transfer. `:up` or `:down` is sensible.
115
115
 
116
116
  **from**
117
117
 
118
- Source file.
118
+ Thie may be either a String, or an IO object (e.g. an open file handle, or a StringIO instance).
119
119
 
120
120
  **to**
121
121
 
122
- Destination file.
122
+ This must be a string indicating the path on the remote server that should be uploaded to.
123
123
 
124
124
  **options**
125
125
 
@@ -139,24 +139,24 @@ All of the options of `transfer` are sensible. In addition, there are some extra
139
139
 
140
140
  Capistrano::Configuration::Actions::FileTransferExt
141
141
 
142
- Install files on remote servers. This method acts just like `mv`.
142
+ Install files on remote servers. This method acts like `mv`, but with little enhancements.
143
143
 
144
144
  #### Arguments
145
145
 
146
146
  **from**
147
147
 
148
- Source file on remote server.
148
+ This must be a string indicating the path on the remote server that should be installed from.
149
149
 
150
150
  **to**
151
151
 
152
- Destination file on remote server.
152
+ This must be a string indicating the path on the remote server that should be installed to.
153
153
 
154
154
  **options**
155
155
 
156
156
  * `:via` specify the method to run commands. `:run` by default. you may need to set this value as `:sudo` if you want to overwrite system files.
157
- * `:mode` The mode of destination file. If not given, preserve original mode of `to`.
158
- * `:owner` The owner of destination file. If not given and `:via` is `:sudo`, preserve original mode of `to`.
159
- * `:group` The group of destination file. If not given and `:via` is `:sudo`, preserve original mode of `to`.
157
+ * `:mode` The mode of destination file. If `:preserve` is given, preserve original mode of `to`.
158
+ * `:owner` The owner of destination file. If `:preserve` is given and `:via` is `:sudo`, preserve original owner of `to`.
159
+ * `:group` The group of destination file. If `:preserve` is given and `:via` is `:sudo`, preserve original group of `to`.
160
160
 
161
161
 
162
162
  ### `install_if_modified`
@@ -176,18 +176,18 @@ Install files on remote servers only if they are different.
176
176
 
177
177
  **from**
178
178
 
179
- Source file on remote server.
179
+ This must be a string indicating the path on the remote server that should be installed from.
180
180
 
181
181
  **to**
182
182
 
183
- Destination file on remote server.
183
+ This must be a string indicating the path on the remote server that should be installed to.
184
184
 
185
185
  **options**
186
186
 
187
187
  * `:via` specify the method to run commands. `:run` by default. you may need to set this value as `:sudo` if you want to overwrite system files.
188
188
  * `:mode` The mode of destination file. If not given, preserve original mode of `to`.
189
- * `:owner` The owner of destination file. If not given and `:via` is `:sudo`, preserve original mode of `to`.
190
- * `:group` The group of destination file. If not given and `:via` is `:sudo`, preserve original mode of `to`.
189
+ * `:owner` The owner of destination file. If not given and `:via` is `:sudo`, preserve original owner of `to`.
190
+ * `:group` The group of destination file. If not given and `:via` is `:sudo`, preserve original group of `to`.
191
191
  * `:digest` This is a symbol indicating which algorithm should be used to calculate the checksum of files. `:md5` is default.
192
192
  * `:digest_cmd` The command to calculate checksum of files. `md5sum` is default.
193
193
 
@@ -1,6 +1,8 @@
1
1
  require "capistrano/configuration/actions/file_transfer_ext/version"
2
2
  require "capistrano/configuration"
3
3
  require "capistrano/transfer"
4
+ require "find"
5
+ require "pathname"
4
6
  require "stringio"
5
7
 
6
8
  module Capistrano
@@ -36,6 +38,7 @@ module Capistrano
36
38
  # * :install - use install_if_modified if :if_modified is set
37
39
  # * :run_method - the default is :run.
38
40
  def safe_upload(from, to, options={}, &block)
41
+ options = options.dup
39
42
  transfer_method = options.delete(:transfer) == :if_modified ? :transfer_if_modified : :transfer
40
43
  if options.has_key?(:install)
41
44
  install_method = options.delete(:install) == :if_modified ? :install_if_modified : :install
@@ -64,35 +67,47 @@ module Capistrano
64
67
  # * :digest_cmd - the digest command. the default is "#{digest}sum".
65
68
  #
66
69
  def transfer_if_modified(direction, from, to, options={}, &block)
67
- digest_method = options.fetch(:digest, :md5).to_s
68
- digest_cmd = options.fetch(:digest_cmd, "#{digest_method.downcase}sum")
69
- require "digest/#{digest_method.downcase}"
70
- target = direction == :up ? from : to
70
+ safe_options = options.dup
71
+ digest_method = safe_options.fetch(:digest, :md5).to_s
72
+ digest_cmd = safe_options.fetch(:digest_cmd, "#{digest_method.downcase}sum")
73
+ target = direction == :up ? from : to
71
74
  remote_target = direction == :up ? to : from
72
- if target.respond_to?(:read)
73
- pos = target.pos
74
- digest = Digest.const_get(digest_method.upcase).hexdigest(target.read)
75
- target.pos = pos
76
- else
77
- begin
78
- digest = Digest.const_get(digest_method.upcase).hexdigest(File.read(target))
79
- rescue SystemCallError
80
- digest = nil
75
+ require "digest/#{digest_method.downcase}"
76
+ target_is_io = target.respond_to?(:read)
77
+ if not(target_is_io) and FileTest.directory?(target)
78
+ Find.find(target) do |_target|
79
+ relative_path = Pathname.new(_target).relative_path_from(Pathname.new(target))
80
+ _remote_target = File.join(remote_target, relative_path)
81
+ from = direction == :up ? _target : _remote_target
82
+ to = direction == :up ? _remote_target : _target
83
+ transfer_if_modified(direction, from, to, options, &block) unless FileTest.directory?(_target)
81
84
  end
82
- end
83
- logger.debug("#{digest_method.upcase}(#{target}) = #{digest}")
84
- if dry_run
85
- logger.debug("transfering: #{[direction, from, to] * ', '}")
86
85
  else
87
- execute_on_servers(options) do |servers|
88
- targets = servers.map { |server| sessions[server] }.reject { |session|
89
- remote_digest = session.exec!("test -f #{remote_target.dump} && #{digest_cmd} #{remote_target.dump} | #{DIGEST_FILTER_CMD}")
90
- logger.debug("#{session.xserver.host}: #{digest_method.upcase}(#{remote_target}) = #{remote_digest}")
91
- result = !( digest.nil? or remote_digest.nil? ) && digest.strip == remote_digest.strip
92
- logger.info("#{session.xserver.host}: skip transfering since no changes: #{[direction, from, to] * ', '}") if result
93
- result
94
- }
95
- Capistrano::Transfer.process(direction, from, to, targets, options.merge(:logger => logger), &block) unless targets.empty?
86
+ if target_is_io
87
+ pos = target.pos
88
+ digest = Digest.const_get(digest_method.upcase).hexdigest(target.read)
89
+ target.pos = pos
90
+ else
91
+ begin
92
+ digest = Digest.const_get(digest_method.upcase).hexdigest(File.read(target))
93
+ rescue SystemCallError
94
+ digest = nil
95
+ end
96
+ end
97
+ logger.debug("#{digest_method.upcase}(#{target}) = #{digest}")
98
+ if dry_run
99
+ logger.debug("transfering: #{[direction, from, to] * ', '}")
100
+ else
101
+ execute_on_servers(safe_options) do |servers|
102
+ targets = servers.map { |server| sessions[server] }.reject { |session|
103
+ remote_digest = session.exec!("test -f #{remote_target.dump} && #{digest_cmd} #{remote_target.dump} | #{DIGEST_FILTER_CMD}")
104
+ logger.debug("#{session.xserver.host}: #{digest_method.upcase}(#{remote_target}) = #{remote_digest}")
105
+ result = !( digest.nil? or remote_digest.nil? ) && digest.strip == remote_digest.strip
106
+ logger.info("#{session.xserver.host}: skip transfering since no changes: #{[direction, from, to] * ', '}") if result
107
+ result
108
+ }
109
+ Capistrano::Transfer.process(direction, from, to, targets, safe_options.merge(:logger => logger), &block) unless targets.empty?
110
+ end
96
111
  end
97
112
  end
98
113
  end
@@ -105,60 +120,75 @@ module Capistrano
105
120
  # * :via - :run by default.
106
121
  #
107
122
  def install(from, to, options={}, &block)
123
+ options = options.dup
108
124
  via = options.delete(:via)
109
125
  if via == :sudo or options.delete(:sudo) # check :sudo for backward compatibility
110
126
  # ignore {:via => :sudo} since `sudo()` cannot handle multiple commands properly.
111
- try_sudo = sudo
127
+ _try_sudo = sudo
112
128
  else
113
- try_sudo = ""
114
- options[:via] = via
129
+ _try_sudo = ""
130
+ options[:via] = via if via
115
131
  end
116
- if options.key?(:mode)
117
- mode = options.delete(:mode)
118
- elsif fetch(:install_preserve_mode, true)
119
- begin
120
- # respect mode of original file
121
- # `stat -c` for GNU, `stat -f` for BSD
122
- s = capture("test -f #{to.dump} && ( stat -c '%a' #{to.dump} || stat -f '%p' #{to.dump} )", options)
123
- mode = s.to_i(8) & 0777 if /^[0-7]+$/ =~ s
124
- logger.debug("preserve original file mode #{mode.to_s(8)}.")
125
- rescue
126
- # nop
132
+ # * If :mode is :preserve or :mode is not given, preserve original mode.
133
+ # * Otherwise, just respect given :mode.
134
+ mode_given = options.key?(:mode)
135
+ mode = options.delete(:mode)
136
+ if mode_given ? mode == :preserve : true
137
+ if fetch(:install_preserve_mode, true)
138
+ begin
139
+ # respect mode of original file
140
+ # `stat -c` for GNU, `stat -f` for BSD
141
+ s = capture("test -f #{to.dump} && ( stat -c '%a' #{to.dump} || stat -f '%p' #{to.dump} )", options)
142
+ mode = s.to_i(8) & 0777 if /^[0-7]+$/ =~ s
143
+ logger.debug("preserve original file mode #{mode.to_s(8)}.")
144
+ rescue
145
+ mode = nil
146
+ end
147
+ else
148
+ mode = nil
127
149
  end
128
150
  end
129
- if options.key?(:owner)
130
- owner = options.delete(:owner)
131
- elsif fetch(:install_preserve_owner, true) and via == :sudo
132
- begin
133
- owner = capture("test -f #{to.dump} && ( stat -c '%u' #{to.dump} || stat -f '%u' #{to.dump} )", options).strip
134
- logger.debug("preserve original file owner #{owner.dump}.")
135
- rescue
136
- # nop
151
+ owner_given = options.key?(:owner)
152
+ owner = options.delete(:owner)
153
+ if owner_given ? owner == :preserve : true
154
+ if fetch(:install_preserve_owner, true) and via == :sudo
155
+ begin
156
+ owner = capture("test -f #{to.dump} && ( stat -c '%u' #{to.dump} || stat -f '%u' #{to.dump} )", options).strip
157
+ logger.debug("preserve original file owner #{owner.dump}.")
158
+ rescue
159
+ owner = nil
160
+ end
161
+ else
162
+ owner = nil
137
163
  end
138
164
  end
139
- if options.key?(:group)
140
- group = options.delete(:group)
141
- elsif fetch(:install_preserve_group, true) and via == :sudo
142
- begin
143
- group = capture("test -f #{to.dump} && ( stat -c '%g' #{to.dump} || stat -f '%g' #{to.dump} )", options).strip
144
- logger.debug("preserve original file grop #{group.dump}.")
145
- rescue
146
- # nop
165
+ group_given = options.key?(:group)
166
+ group = options.delete(:group)
167
+ if group_given ? group == :preserve : true
168
+ if fetch(:install_preserve_group, true) and via == :sudo
169
+ begin
170
+ group = capture("test -f #{to.dump} && ( stat -c '%g' #{to.dump} || stat -f '%g' #{to.dump} )", options).strip
171
+ logger.debug("preserve original file grop #{group.dump}.")
172
+ rescue
173
+ group = nil
174
+ end
175
+ else
176
+ group = nil
147
177
  end
148
178
  end
149
179
  execute = []
150
180
  if block_given?
151
181
  execute << yield(from, to)
152
182
  else
153
- execute << "( test -d #{File.dirname(to).dump} || #{try_sudo} mkdir -p #{File.dirname(to).dump} )"
154
- execute << "#{try_sudo} mv -f #{from.dump} #{to.dump}"
183
+ execute << "( test -d #{File.dirname(to).dump} || #{_try_sudo} mkdir -p #{File.dirname(to).dump} )"
184
+ execute << "#{_try_sudo} mv -f #{from.dump} #{to.dump}"
155
185
  end
156
186
  if mode
157
187
  mode = mode.is_a?(Numeric) ? mode.to_s(8) : mode.to_s
158
- execute << "#{try_sudo} chmod #{mode.dump} #{to.dump}"
188
+ execute << "#{_try_sudo} chmod #{mode.dump} #{to.dump}"
159
189
  end
160
- execute << "#{try_sudo} chown #{owner.to_s.dump} #{to.dump}" if owner
161
- execute << "#{try_sudo} chgrp #{group.to_s.dump} #{to.dump}" if group
190
+ execute << "#{_try_sudo} chown #{owner.to_s.dump} #{to.dump}" if owner
191
+ execute << "#{_try_sudo} chgrp #{group.to_s.dump} #{to.dump}" if group
162
192
  invoke_command(execute.join(" && "), options)
163
193
  end
164
194
  alias place install
@@ -173,11 +203,18 @@ module Capistrano
173
203
  # * :digest_cmd - the digest command. the default is "#{digest}sum".
174
204
  #
175
205
  def install_if_modified(from, to, options={}, &block)
206
+ options = options.dup
207
+ if options[:via] == :sudo or options.delete(:sudo)
208
+ _try_sudo = sudo
209
+ options[:via] = :sudo
210
+ else
211
+ _try_sudo = ""
212
+ end
176
213
  digest_method = options.fetch(:digest, :md5).to_s
177
214
  digest_cmd = options.fetch(:digest_cmd, "#{digest_method.downcase}sum")
178
215
  install(from, to, options) do |from, to|
179
216
  execute = []
180
- execute << %{( test -d #{File.dirname(to).dump} || #{try_sudo} mkdir -p #{File.dirname(to).dump} )}
217
+ execute << %{( test -d #{File.dirname(to).dump} || #{_try_sudo} mkdir -p #{File.dirname(to).dump} )}
181
218
  # calculate hexdigest of `from'
182
219
  execute << %{from=$(#{digest_cmd} #{from.dump} 2>/dev/null | #{DIGEST_FILTER_CMD} || true)}
183
220
  execute << %{echo %s} % ["#{digest_method.upcase}(#{from}) = ${from}".dump]
@@ -189,7 +226,7 @@ module Capistrano
189
226
  if [ -n "${from}" -a "${to}" ] && [ "${from}" = "${to}" ]; then
190
227
  echo "skip installing since no changes.";
191
228
  else
192
- #{try_sudo} mv -f #{from.dump} #{to.dump};
229
+ #{_try_sudo} mv -f #{from.dump} #{to.dump};
193
230
  fi
194
231
  EOS
195
232
  execute.join(" && ")
@@ -2,7 +2,7 @@ module Capistrano
2
2
  class Configuration
3
3
  module Actions
4
4
  module FileTransferExt
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.1"
6
6
  end
7
7
  end
8
8
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  bundle exec vagrant up
4
4
  bundle exec cap test_all
5
- bundle exec vagrant destroy -f
5
+ bundle exec vagrant halt
6
6
 
7
7
  # vim:set ft=sh :
@@ -153,6 +153,20 @@ namespace(:test_default) {
153
153
  run("test -f #{to.dump}")
154
154
  run("test #{body.dump} = $(cat #{to.dump})")
155
155
  }
156
+
157
+ task(:test_safe_upload_directory) {
158
+ files = %w(x y z)
159
+ body = "baz"
160
+ from = "tmp/baz"
161
+ from_files = files.map { |x| File.join(from, x) }
162
+ to = "tmp/rbaz"
163
+ to_files = files.map { |x| File.join(to, x) }
164
+ run_locally("rm -rf #{from.dump}; mkdir -p #{from.dump}")
165
+ run_locally(from_files.map { |file| "echo #{body.dump} > #{file.dump}" }.join(" && "))
166
+ run("rm -rf #{to.dump}")
167
+ safe_upload(from, to)
168
+ run(to_files.map { |file| "test -f #{file.dump}" }.join(" && "))
169
+ }
156
170
  }
157
171
 
158
172
  namespace(:test_transfer_if_modified) {
@@ -2,6 +2,6 @@
2
2
 
3
3
  bundle exec vagrant up
4
4
  bundle exec cap test_all
5
- bundle exec vagrant destroy -f
5
+ bundle exec vagrant halt
6
6
 
7
7
  # vim:set ft=sh :
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capistrano-file-transfer-ext
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-14 00:00:00.000000000 Z
12
+ date: 2013-03-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: capistrano