capistrano-file-transfer-ext 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -26,6 +26,9 @@ Now you can use them in your tasks.
26
26
 
27
27
  * `safe_upload` - Transfers a file from the local host to multiple remote hosts. ([more...](#safe_upload))
28
28
  * `safe_put` - Store the contents of multiple servers. ([more...](#safe_put))
29
+ * `transfer_if_modified` - Transfer files from/to remote servers only if they are different. ([more...](#transfer_if_modified))
30
+ * `install` - Install files on remote servers. ([more...](#install))
31
+ * `install_if_modified` - Install files on remote servers only if they are different. ([more...](#install_if_modified))
29
32
 
30
33
  ## Reference
31
34
 
@@ -59,9 +62,9 @@ This must be a string indicating the path on the remote server that should be up
59
62
  All of the options of `upload` are sensible. In addition, there are some extra options.
60
63
 
61
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.
62
- * `:digest` Thi is a symbol indicating which algorithm should be used to calculate the checksum of files. `:md5` is default.
65
+ * `:digest` This is a symbol indicating which algorithm should be used to calculate the checksum of files. `:md5` is default.
63
66
  * `:digest_cmd` The command to calculate checksum of files. `md5sum` is default.
64
- * `:sudo` It must be a boolean. If set true, use `sudo` on placing files. `false` by 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.
65
68
 
66
69
 
67
70
  ### `safe_put`
@@ -91,6 +94,104 @@ This is a string naming the file on the remote server(s) that should be created
91
94
  All of the options of `safe_upload` are sensible.
92
95
 
93
96
 
97
+ ### `transfer_if_modified`
98
+
99
+ **Definition**
100
+
101
+ transfer_if_modified(direction, from, to, options={}, &block)
102
+
103
+ **Module**
104
+
105
+ Capistrano::Configuration::Actions::FileTransferExt
106
+
107
+ The `transfer_if_modified` action is `transfer`'s senior brother.
108
+ Transfer files from/to remote servers only if they are different.
109
+
110
+ #### Arguments
111
+
112
+ **direction**
113
+
114
+ The direction of transfer. `:up` or `:down` is sensible.
115
+
116
+ **from**
117
+
118
+ Source file.
119
+
120
+ **to**
121
+
122
+ Destination file.
123
+
124
+ **options**
125
+
126
+ All of the options of `transfer` are sensible. In addition, there are some extra options.
127
+
128
+ * `:digest` This is a symbol indicating which algorithm should be used to calculate the checksum of files. `:md5` is default.
129
+ * `:digest_cmd` The command to calculate checksum of files. `md5sum` is default.
130
+
131
+
132
+ ### `install`
133
+
134
+ **Definition**
135
+
136
+ install(from, to, options={}, &block)
137
+
138
+ **Module**
139
+
140
+ Capistrano::Configuration::Actions::FileTransferExt
141
+
142
+ Install files on remote servers. This method acts just like `mv`.
143
+
144
+ #### Arguments
145
+
146
+ **from**
147
+
148
+ Source file on remote server.
149
+
150
+ **to**
151
+
152
+ Destination file on remote server.
153
+
154
+ **options**
155
+
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`.
160
+
161
+
162
+ ### `install_if_modified`
163
+
164
+ **Definition**
165
+
166
+ install_if_modified(from, to, options={}, &block)
167
+
168
+ **Module**
169
+
170
+ Capistrano::Configuration::Actions::FileTransferExt
171
+
172
+ The `install_if_modified` action is `install`'s senior brother.
173
+ Install files on remote servers only if they are different.
174
+
175
+ #### Arguments
176
+
177
+ **from**
178
+
179
+ Source file on remote server.
180
+
181
+ **to**
182
+
183
+ Destination file on remote server.
184
+
185
+ **options**
186
+
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
+ * `: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`.
191
+ * `:digest` This is a symbol indicating which algorithm should be used to calculate the checksum of files. `:md5` is default.
192
+ * `:digest_cmd` The command to calculate checksum of files. `md5sum` is default.
193
+
194
+
94
195
  ## Contributing
95
196
 
96
197
  1. Fork it
@@ -16,4 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.version = Capistrano::Configuration::Actions::FileTransferExt::VERSION
17
17
 
18
18
  gem.add_dependency("capistrano")
19
+ gem.add_development_dependency("net-scp", "~> 1.0.4")
20
+ gem.add_development_dependency("net-ssh", "~> 2.2.2")
21
+ gem.add_development_dependency("vagrant", "~> 1.0.6")
19
22
  end
@@ -64,7 +64,7 @@ module Capistrano
64
64
  # * :digest_cmd - the digest command. the default is "#{digest}sum".
65
65
  #
66
66
  def transfer_if_modified(direction, from, to, options={}, &block)
67
- digest_method = options.fetch(:digest, "md5")
67
+ digest_method = options.fetch(:digest, :md5).to_s
68
68
  digest_cmd = options.fetch(:digest_cmd, "#{digest_method.downcase}sum")
69
69
  require "digest/#{digest_method.downcase}"
70
70
  target = direction == :up ? from : to
@@ -75,7 +75,7 @@ module Capistrano
75
75
  target.pos = pos
76
76
  else
77
77
  begin
78
- digest = Digest::const_get(digest_method.upcase).hexdigest(File.read(target))
78
+ digest = Digest.const_get(digest_method.upcase).hexdigest(File.read(target))
79
79
  rescue SystemCallError
80
80
  digest = nil
81
81
  end
@@ -102,18 +102,63 @@ module Capistrano
102
102
  # The +options+ hash may include any of the following keys:
103
103
  #
104
104
  # * :mode - permission of the file.
105
- # * :sudo - use sudo if set true. the default is false.
105
+ # * :via - :run by default.
106
106
  #
107
107
  def install(from, to, options={}, &block)
108
- mode = options.delete(:mode)
109
- try_sudo = options.delete(:sudo) ? sudo : ""
108
+ via = options.delete(:via)
109
+ if via == :sudo or options.delete(:sudo) # check :sudo for backward compatibility
110
+ # ignore {:via => :sudo} since `sudo()` cannot handle multiple commands properly.
111
+ try_sudo = sudo
112
+ else
113
+ try_sudo = ""
114
+ options[:via] = via
115
+ 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
127
+ end
128
+ 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
137
+ end
138
+ 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
147
+ end
148
+ end
110
149
  execute = []
111
- execute << "( test -d #{File.dirname(to).dump} || #{try_sudo} mkdir -p #{File.dirname(to).dump} )"
112
- execute << "#{try_sudo} mv -f #{from.dump} #{to.dump}"
150
+ if block_given?
151
+ execute << yield(from, to)
152
+ 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}"
155
+ end
113
156
  if mode
114
157
  mode = mode.is_a?(Numeric) ? mode.to_s(8) : mode.to_s
115
- execute << "#{try_sudo} chmod #{mode} #{to.dump}"
158
+ execute << "#{try_sudo} chmod #{mode.dump} #{to.dump}"
116
159
  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
117
162
  invoke_command(execute.join(" && "), options)
118
163
  end
119
164
  alias place install
@@ -123,37 +168,32 @@ module Capistrano
123
168
  # The +options+ hash may include any of the following keys:
124
169
  #
125
170
  # * :mode - permission of the file.
126
- # * :sudo - use sudo if set true. the default is false.
171
+ # * :via - :run by default.
127
172
  # * :digest - digest algorithm. the default is "md5".
128
173
  # * :digest_cmd - the digest command. the default is "#{digest}sum".
129
174
  #
130
175
  def install_if_modified(from, to, options={}, &block)
131
- mode = options.delete(:mode)
132
- try_sudo = options.delete(:sudo) ? sudo : ""
133
- digest_method = options.fetch(:digest, "md5")
176
+ digest_method = options.fetch(:digest, :md5).to_s
134
177
  digest_cmd = options.fetch(:digest_cmd, "#{digest_method.downcase}sum")
135
- execute = []
136
- execute << %{( test -d #{File.dirname(to).dump} || #{try_sudo} mkdir -p #{File.dirname(to).dump} )}
137
- # calculate hexdigest of `from'
138
- execute << %{from=$(#{digest_cmd} #{from.dump} 2>/dev/null | #{DIGEST_FILTER_CMD} || true)}
139
- execute << %{echo %s} % ["#{digest_method.upcase}(#{from}) = ${from}".dump]
140
- # calculate hexdigest of `to'
141
- execute << %{to=$(#{digest_cmd} #{to.dump} 2>/dev/null | #{DIGEST_FILTER_CMD} || true)}
142
- execute << %{echo %s} % ["#{digest_method.upcase}(#{to}) = ${to}".dump]
143
- # check the hexdigests
144
- execute << (<<-EOS).gsub(/\s+/, " ").strip
145
- if [ -n "${from}" -a "${to}" ] && [ "${from}" = "${to}" ]; then
146
- echo "skip placing since no changes.";
147
- else
148
- #{try_sudo} mv -f #{from.dump} #{to.dump};
149
- fi
150
- EOS
151
-
152
- if mode
153
- mode = mode.is_a?(Numeric) ? mode.to_s(8) : mode.to_s
154
- execute << "#{try_sudo} chmod #{mode} #{to.dump}"
178
+ install(from, to, options) do |from, to|
179
+ execute = []
180
+ execute << %{( test -d #{File.dirname(to).dump} || #{try_sudo} mkdir -p #{File.dirname(to).dump} )}
181
+ # calculate hexdigest of `from'
182
+ execute << %{from=$(#{digest_cmd} #{from.dump} 2>/dev/null | #{DIGEST_FILTER_CMD} || true)}
183
+ execute << %{echo %s} % ["#{digest_method.upcase}(#{from}) = ${from}".dump]
184
+ # calculate hexdigest of `to'
185
+ execute << %{to=$(#{digest_cmd} #{to.dump} 2>/dev/null | #{DIGEST_FILTER_CMD} || true)}
186
+ execute << %{echo %s} % ["#{digest_method.upcase}(#{to}) = ${to}".dump]
187
+ # check the hexdigests
188
+ execute << (<<-EOS).gsub(/\s+/, " ").strip
189
+ if [ -n "${from}" -a "${to}" ] && [ "${from}" = "${to}" ]; then
190
+ echo "skip installing since no changes.";
191
+ else
192
+ #{try_sudo} mv -f #{from.dump} #{to.dump};
193
+ fi
194
+ EOS
195
+ execute.join(" && ")
155
196
  end
156
- invoke_command(execute.join(" && "), options)
157
197
  end
158
198
  alias place_if_modified install_if_modified
159
199
  end
@@ -2,7 +2,7 @@ module Capistrano
2
2
  class Configuration
3
3
  module Actions
4
4
  module FileTransferExt
5
- VERSION = "0.0.4"
5
+ VERSION = "0.1.0"
6
6
  end
7
7
  end
8
8
  end
@@ -0,0 +1,5 @@
1
+ /.bundle
2
+ /.vagrant
3
+ /known_hosts
4
+ /tmp
5
+ /vendor
@@ -0,0 +1,2 @@
1
+ load "deploy"
2
+ load "../config/deploy"
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec(:path => "../..")
@@ -0,0 +1,99 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ Vagrant::Config.run do |config|
5
+ # All Vagrant configuration is done here. The most common configuration
6
+ # options are documented and commented below. For a complete reference,
7
+ # please see the online documentation at vagrantup.com.
8
+
9
+ # Every Vagrant virtual environment requires a box to build off of.
10
+ config.vm.box = "centos6-64"
11
+
12
+ # The url from where the 'config.vm.box' box will be fetched if it
13
+ # doesn't already exist on the user's system.
14
+ config.vm.box_url = "http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.3-x86_64-v20130101.box"
15
+
16
+ # Boot with a GUI so you can see the screen. (Default is headless)
17
+ # config.vm.boot_mode = :gui
18
+
19
+ # Assign this VM to a host-only network IP, allowing you to access it
20
+ # via the IP. Host-only networks can talk to the host machine as well as
21
+ # any other machines on the same network, but cannot be accessed (through this
22
+ # network interface) by any external networks.
23
+ config.vm.network :hostonly, "192.168.33.10"
24
+
25
+ # Assign this VM to a bridged network, allowing you to connect directly to a
26
+ # network using the host's network device. This makes the VM appear as another
27
+ # physical device on your network.
28
+ # config.vm.network :bridged
29
+
30
+ # Forward a port from the guest to the host, which allows for outside
31
+ # computers to access the VM, whereas host only networking does not.
32
+ # config.vm.forward_port 80, 8080
33
+
34
+ # Share an additional folder to the guest VM. The first argument is
35
+ # an identifier, the second is the path on the guest to mount the
36
+ # folder, and the third is the path on the host to the actual folder.
37
+ # config.vm.share_folder "v-data", "/vagrant_data", "../data"
38
+
39
+ # Enable provisioning with Puppet stand alone. Puppet manifests
40
+ # are contained in a directory path relative to this Vagrantfile.
41
+ # You will need to create the manifests directory and a manifest in
42
+ # the file precise-amd64.pp in the manifests_path directory.
43
+ #
44
+ # An example Puppet manifest to provision the message of the day:
45
+ #
46
+ # # group { "puppet":
47
+ # # ensure => "present",
48
+ # # }
49
+ # #
50
+ # # File { owner => 0, group => 0, mode => 0644 }
51
+ # #
52
+ # # file { '/etc/motd':
53
+ # # content => "Welcome to your Vagrant-built virtual machine!
54
+ # # Managed by Puppet.\n"
55
+ # # }
56
+ #
57
+ # config.vm.provision :puppet do |puppet|
58
+ # puppet.manifests_path = "manifests"
59
+ # puppet.manifest_file = "precise-amd64.pp"
60
+ # end
61
+
62
+ # Enable provisioning with chef solo, specifying a cookbooks path, roles
63
+ # path, and data_bags path (all relative to this Vagrantfile), and adding
64
+ # some recipes and/or roles.
65
+ #
66
+ # config.vm.provision :chef_solo do |chef|
67
+ # chef.cookbooks_path = "../my-recipes/cookbooks"
68
+ # chef.roles_path = "../my-recipes/roles"
69
+ # chef.data_bags_path = "../my-recipes/data_bags"
70
+ # chef.add_recipe "mysql"
71
+ # chef.add_role "web"
72
+ #
73
+ # # You may also specify custom JSON attributes:
74
+ # chef.json = { :mysql_password => "foo" }
75
+ # end
76
+
77
+ # Enable provisioning with chef server, specifying the chef server URL,
78
+ # and the path to the validation key (relative to this Vagrantfile).
79
+ #
80
+ # The Opscode Platform uses HTTPS. Substitute your organization for
81
+ # ORGNAME in the URL and validation key.
82
+ #
83
+ # If you have your own Chef Server, use the appropriate URL, which may be
84
+ # HTTP instead of HTTPS depending on your configuration. Also change the
85
+ # validation key to validation.pem.
86
+ #
87
+ # config.vm.provision :chef_client do |chef|
88
+ # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
89
+ # chef.validation_key_path = "ORGNAME-validator.pem"
90
+ # end
91
+ #
92
+ # If you're using the Opscode platform, your validator client is
93
+ # ORGNAME-validator, replacing ORGNAME with your organization name.
94
+ #
95
+ # IF you have your own Chef Server, the default validation client name is
96
+ # chef-validator, unless you changed the configuration.
97
+ #
98
+ # chef.validation_client_name = "ORGNAME-validator"
99
+ end
@@ -0,0 +1,7 @@
1
+ #!/bin/sh -e
2
+
3
+ bundle exec vagrant up
4
+ bundle exec cap test_all
5
+ bundle exec vagrant destroy -f
6
+
7
+ # vim:set ft=sh :
@@ -0,0 +1,476 @@
1
+ set :application, "capistrano-file-transfer-ext"
2
+ set :repository, "."
3
+ set :deploy_to do
4
+ File.join("/home", user, application)
5
+ end
6
+ set :deploy_via, :copy
7
+ set :scm, :none
8
+ set :use_sudo, false
9
+ set :user, "vagrant"
10
+ set :password, "vagrant"
11
+ set :ssh_options, {:user_known_hosts_file => "/dev/null"}
12
+
13
+ role :web, "192.168.33.10"
14
+ role :app, "192.168.33.10"
15
+ role :db, "192.168.33.10", :primary => true
16
+
17
+ $LOAD_PATH.push(File.expand_path("../../lib", File.dirname(__FILE__)))
18
+ require "capistrano/configuration/actions/file_transfer_ext"
19
+ require "stringio"
20
+
21
+ task(:test_all) {
22
+ find_and_execute_task("test_default")
23
+ find_and_execute_task("test_transfer_if_modified")
24
+ find_and_execute_task("test_install")
25
+ find_and_execute_task("test_install_if_modified")
26
+ }
27
+
28
+ def _invoke_command(cmdline, options={})
29
+ via = options.delete(:via)
30
+ if via == :run_locally
31
+ run_locally(cmdline)
32
+ else
33
+ invoke_command(cmdline, options.merge(:via => via))
34
+ end
35
+ end
36
+
37
+ def assert_timestamp_equals(x, y, options={})
38
+ begin
39
+ _invoke_command("test \! #{x.dump} -nt #{y.dump} -a \! #{x.dump} -ot #{y.dump}", options)
40
+ rescue
41
+ logger.debug("assert_timestamp_equals(#{x}, #{y}) failed.")
42
+ _invoke_command("ls -l #{x.dump} #{y.dump}", options)
43
+ raise
44
+ end
45
+ end
46
+
47
+ def assert_timestamp_not_equals(x, y, options={})
48
+ begin
49
+ _invoke_command("test #{x.dump} -nt #{y.dump} -o #{x.dump} -ot #{y.dump}", options)
50
+ rescue
51
+ logger.debug("assert_timestamp_not_equals(#{x}, #{y}) failed.")
52
+ _invoke_command("ls -l #{x.dump} #{y.dump}", options)
53
+ raise
54
+ end
55
+ end
56
+
57
+ def assert_file_equals(x, y, options={})
58
+ begin
59
+ _invoke_command("cmp #{x.dump} #{y.dump}", options)
60
+ rescue
61
+ logger.debug("assert_file_equals(#{x}, #{y}) failed.")
62
+ _invoke_command("ls -l #{x.dump} #{y.dump}", options)
63
+ raise
64
+ end
65
+ end
66
+
67
+ def assert_file_mode_equals(x, y, options={})
68
+ begin
69
+ _invoke_command("test $(ls -l #{x.dump} | cut -d ' ' -f 1) = $(ls -l #{y.dump} | cut -d ' ' -f 1)", options)
70
+ rescue
71
+ logger.debug("assert_file_mode_equals(#{x}, #{y}) failed.")
72
+ _invoke_command("ls -l #{x.dump} #{y.dump}", options)
73
+ raise
74
+ end
75
+ end
76
+
77
+ def assert_file_mode(mode, file, options={})
78
+ mode = mode.to_i(8) if mode.is_a?(String)
79
+ smode = "%c%c%c%c%c%c%c%c%c%c" % [
80
+ ?-,
81
+ mode & 0400 == 0 ? ?- : ?r, mode & 0200 == 0 ? ?- : ?w, mode & 0100 == 0 ? ?- : ?x,
82
+ mode & 0040 == 0 ? ?- : ?r, mode & 0020 == 0 ? ?- : ?w, mode & 0010 == 0 ? ?- : ?x,
83
+ mode & 0004 == 0 ? ?- : ?r, mode & 0002 == 0 ? ?- : ?w, mode & 0001 == 0 ? ?- : ?x,
84
+ ]
85
+ begin
86
+ _invoke_command("test #{smode.dump} = $(ls -l #{file.dump} | cut -d ' ' -f 1)", options)
87
+ rescue
88
+ logger.debug("assert_file_mode(#{mode.to_s(8)}, #{file}) failed.")
89
+ _invoke_command("ls -l #{file.dump}", options)
90
+ raise
91
+ end
92
+ end
93
+
94
+ def assert_file_owner(uid, file, options={})
95
+ uid = uid.to_i
96
+ # `stat -c` => GNU, `stat -f` => BSD
97
+ begin
98
+ _invoke_command("test #{uid} -eq $( stat -c '%u' #{file.dump} || stat -f '%u' #{file.dump} )", options)
99
+ rescue
100
+ logger.debug("assert_file_owner(#{uid}, #{file}) failed.")
101
+ _invoke_command("ls -l #{file.dump}", options)
102
+ raise
103
+ end
104
+ end
105
+
106
+ def assert_file_group(gid, file, options={})
107
+ gid = gid.to_i
108
+ # `stat -c` => GNU, `stat -f` => BSD
109
+ begin
110
+ _invoke_command("test #{gid} -eq $( stat -c '%g' #{file.dump} || stat -f '%g' #{file.dump} )", options)
111
+ rescue
112
+ logger.debug("assert_file_group(#{gid}, #{file}) failed.")
113
+ _invoke_command("ls -l #{file.dump}", options)
114
+ raise
115
+ end
116
+ end
117
+
118
+ namespace(:test_default) {
119
+ task(:default) {
120
+ methods.grep(/^test_/).each do |m|
121
+ send(m)
122
+ end
123
+ }
124
+ before "test_default", "test_default:setup"
125
+ after "test_default", "test_default:teardown"
126
+
127
+ task(:setup) {
128
+ run_locally("mkdir -p tmp")
129
+ run("mkdir -p tmp")
130
+ }
131
+
132
+ task(:teardown) {
133
+ run_locally("rm -rf tmp")
134
+ sudo("rm -rf tmp")
135
+ }
136
+
137
+ task(:test_safe_upload) {
138
+ body = "foo"
139
+ from = "tmp/foo"
140
+ to = "tmp/rfoo"
141
+ run_locally("rm -f #{from.dump}; echo #{body.dump} > #{from.dump}")
142
+ run("rm -f #{to.dump}")
143
+ safe_upload(from, to)
144
+ run("test -f #{to.dump}")
145
+ run("test #{body.dump} = $(cat #{to.dump})")
146
+ }
147
+
148
+ task(:test_safe_put) {
149
+ body = "bar"
150
+ to = "tmp/rbar"
151
+ run("rm -f #{to.dump}")
152
+ safe_put(body, to)
153
+ run("test -f #{to.dump}")
154
+ run("test #{body.dump} = $(cat #{to.dump})")
155
+ }
156
+ }
157
+
158
+ namespace(:test_transfer_if_modified) {
159
+ task(:default) {
160
+ methods.grep(/^test_/).each do |m|
161
+ send(m)
162
+ end
163
+ }
164
+ before "test_transfer_if_modified", "test_transfer_if_modified:setup"
165
+ after "test_transfer_if_modified", "test_transfer_if_modified:teardown"
166
+
167
+ task(:setup) {
168
+ run_locally("mkdir -p tmp")
169
+ run("mkdir -p tmp")
170
+ }
171
+
172
+ task(:teardown) {
173
+ run_locally("rm -rf tmp")
174
+ sudo("rm -rf tmp")
175
+ }
176
+
177
+ def _test_transfer_if_modified_up(from, to, options={})
178
+ run("rm -f #{to.dump}")
179
+ transfer_if_modified(:up, from, to, options)
180
+ if from.respond_to?(:read)
181
+ pos = from.pos
182
+ body = from.dup.read
183
+ from.pos = pos
184
+ else
185
+ body = File.read(from)
186
+ end
187
+ tempbody = capture("mktemp tmp/body.XXXXXXXXXX").strip
188
+ run("rm -f #{tempbody.dump}")
189
+ put(body, tempbody)
190
+ assert_file_equals(tempbody, to)
191
+
192
+ ## should not transfer without changes
193
+ 2.times do
194
+ sleep(1)
195
+ tempto = capture("mktemp tmp/to.XXXXXXXXXX").strip
196
+ run("rm -f #{tempto.dump}; cp -p #{to.dump} #{tempto.dump}")
197
+ transfer_if_modified(:up, from, to, options) # re-transfer
198
+ assert_file_equals(tempto, to)
199
+ assert_timestamp_equals(tempto, to)
200
+ end
201
+
202
+ ## should transfer if `from' is changed
203
+ tempfrom = run_locally("mktemp tmp/from.XXXXXXXXXX").strip
204
+ tempto = capture("mktemp tmp/to.XXXXXXXXXX").strip
205
+ File.write(tempfrom, body * 4)
206
+ run("rm -f #{tempto.dump}; cp -p #{to.dump} #{tempto.dump}")
207
+ transfer_if_modified(:up, tempfrom, to, options)
208
+ run("test #{tempto.dump} -ot #{to.dump}") # check if `to' is overwritten
209
+ end
210
+
211
+ def _test_transfer_if_modified_down(from, to, options={})
212
+ to = to.gsub(/\$CAPISTRANO:HOST\$/, "192.168.33.10")
213
+ run_locally("rm -f #{to.dump}")
214
+ transfer_if_modified(:down, from, to, options)
215
+ tempbody = capture("mktemp tmp/body.XXXXXXXXXX").strip
216
+ run_locally("rm -f #{tempbody.dump}")
217
+ download(from, tempbody)
218
+ assert_file_equals(tempbody, to, :via => :run_locally)
219
+
220
+ ## should not transfer without changes
221
+ 2.times do
222
+ sleep(1)
223
+ tempto = capture("mktemp tmp/to.XXXXXXXXXX").strip
224
+ run_locally("rm -f #{tempto.dump}; cp -p #{to.dump} #{tempto.dump}")
225
+ transfer_if_modified(:down, from, to, options) # re-transfer
226
+ assert_file_equals(tempto, to, :via => :run_locally)
227
+ assert_timestamp_equals(tempto, to, :via => :run_locally)
228
+ end
229
+
230
+ ## should transfer if `from' is changed
231
+ tempfrom = capture("mktemp tmp/from.XXXXXXXXXX").strip
232
+ tempto = run_locally("mktemp tmp/to.XXXXXXXXXX").strip
233
+ run("for i in 0 1 2 3; do cat #{from.dump} >> #{tempfrom.dump}; done")
234
+ run_locally("rm -f #{tempto.dump}; cp -p #{to.dump} #{tempto.dump}")
235
+ transfer_if_modified(:down, tempfrom, to, options)
236
+ run_locally("test #{tempto.dump} -ot #{to.dump}") # check if `to' is overwritten
237
+ end
238
+
239
+ task(:test_transfer_if_modified_up) {
240
+ run_locally("rm -f tmp/foo; echo foo > tmp/foo")
241
+ _test_transfer_if_modified_up("tmp/foo", "tmp/rfoo")
242
+ }
243
+
244
+ task(:test_transfer_if_modified_up_with_stringio) {
245
+ _test_transfer_if_modified_up(StringIO.new("bar"), "tmp/rbar", :digest => :sha1)
246
+ }
247
+
248
+ task(:test_transfer_if_modified_down) {
249
+ run("rm -f tmp/foo; echo foo > tmp/foo")
250
+ _test_transfer_if_modified_down("tmp/foo", "tmp/lfoo", :digest => :sha1)
251
+ }
252
+
253
+ task(:test_transfer_if_modified_down_with_capistrano_host) {
254
+ run("rm -f tmp/bar; echo bar > tmp/bar")
255
+ _test_transfer_if_modified_down("tmp/bar", "tmp/lbar_$CAPISTRANO:HOST$")
256
+ }
257
+ }
258
+
259
+ def _test_install(frombody, tobody, options={})
260
+ from = capture("mktemp tmp/from.XXXXXXXXXX").strip
261
+ run("rm -f #{from.dump}")
262
+ put(frombody, from)
263
+
264
+ sleep(1)
265
+ to = capture("mktemp tmp/to.XXXXXXXXXX").strip
266
+ run("rm -f #{to.dump}")
267
+ if tobody
268
+ put(tobody, to)
269
+ orig_mode = options.delete(:orig_mode)
270
+ run("chmod #{orig_mode.to_s(8)} #{to.dump}") if orig_mode
271
+ orig_owner = options.delete(:orig_owner)
272
+ sudo("chown #{orig_owner} #{to.dump}") if orig_owner
273
+ orig_group = options.delete(:orig_group)
274
+ sudo("chgrp #{orig_group} #{to.dump}") if orig_group
275
+ end
276
+
277
+ tempfrom = capture("mktemp tmp/from2.XXXXXXXXXX").strip
278
+ run("rm -f #{tempfrom.dump}; #{sudo} cp -p #{from.dump} #{tempfrom.dump}")
279
+
280
+ tempto = capture("mktemp tmp/to2.XXXXXXXXXX").strip
281
+ if tobody
282
+ run("rm -f #{tempto.dump}; #{sudo} cp -p #{to.dump} #{tempto.dump}")
283
+ else
284
+ run("rm -f #{tempto.dump}")
285
+ end
286
+
287
+ method = ( options.delete(:install) || :always )
288
+ send(method == :if_modified ? :install_if_modified : :install, from, to, options)
289
+ yield(from, to, tempfrom, tempto)
290
+ end
291
+
292
+ namespace(:test_install) {
293
+ task(:default) {
294
+ methods.grep(/^test_/).each do |m|
295
+ send(m)
296
+ end
297
+ }
298
+ before "test_install", "test_install:setup"
299
+ after "test_install", "test_install:teardown"
300
+
301
+ task(:setup) {
302
+ run_locally("mkdir -p tmp")
303
+ run("mkdir -p tmp")
304
+ }
305
+
306
+ task(:teardown) {
307
+ run_locally("rm -rf tmp")
308
+ sudo("rm -rf tmp")
309
+ }
310
+
311
+ task(:test_if_not_modified) {
312
+ _test_install("foo", "foo") do |from, to, tempfrom, tempto|
313
+ assert_file_equals(tempto, to)
314
+ assert_timestamp_equals(tempfrom, to)
315
+ assert_timestamp_not_equals(tempto, to)
316
+ assert_file_mode_equals(tempto, to)
317
+ end
318
+ }
319
+
320
+ task(:test_if_modified) {
321
+ _test_install("foo", "bar", :digest => :sha1) do |from, to, tempfrom, tempto|
322
+ assert_file_equals(tempfrom, to)
323
+ assert_timestamp_equals(tempfrom, to)
324
+ assert_timestamp_not_equals(tempto, to)
325
+ assert_file_mode_equals(tempto, to)
326
+ end
327
+ }
328
+
329
+ task(:test_if_missing) {
330
+ _test_install("baz", nil) do |from, to, tempfrom, tempto|
331
+ assert_file_equals(tempfrom, to)
332
+ assert_timestamp_equals(tempfrom, to)
333
+ end
334
+ }
335
+
336
+ task(:test_with_mode) {
337
+ _test_install("foo", "bar", :mode => 0755) do |from, to, tempfrom, tempto|
338
+ assert_file_mode(0755, to)
339
+ end
340
+ }
341
+
342
+ task(:test_via_sudo) {
343
+ _test_install("bar", "baz", :orig_owner => 0, :via => :sudo) do |from, to, tempfrom, tempto|
344
+ assert_file_owner(0, to)
345
+ end
346
+ }
347
+
348
+ task(:test_with_mode_via_sudo) {
349
+ _test_install("bar", "baz", :mode => 0644, :orig_owner => 0, :via => :sudo) do |from, to, tempfrom, tempto|
350
+ assert_file_mode(0644, to)
351
+ assert_file_owner(0, to)
352
+ end
353
+ }
354
+
355
+ task(:test_preserve_original_mode) {
356
+ _test_install("foo", "bar", :orig_mode => 0600) do |from, to, tempfrom, tempto|
357
+ assert_file_mode(0600, to)
358
+ end
359
+ }
360
+
361
+ task(:test_ignore_original_mode) {
362
+ _test_install("foo", "bar", :orig_mode => 0400, :mode => 0700) do |from, to, tempfrom, tempto|
363
+ assert_file_mode(0700, to)
364
+ end
365
+ }
366
+
367
+ task(:test_preserve_original_owner_group_mode) {
368
+ _test_install("foo", "bar", :orig_owner => 0, :orig_group => 0, :orig_mode => 0640, :via => :sudo) do |from, to, tempfrom, tempto|
369
+ assert_file_mode(0640, to)
370
+ assert_file_owner(0, to)
371
+ assert_file_group(0, to)
372
+ end
373
+ }
374
+
375
+ task(:test_ignore_original_owner_group_mode) {
376
+ _test_install("bar", "baz", :owner => 0, :group => 0, :mode => 0640, :via => :sudo) do |from, to, tempfrom, tempto|
377
+ assert_file_mode(0640, to)
378
+ assert_file_owner(0, to)
379
+ assert_file_group(0, to)
380
+ end
381
+ }
382
+ }
383
+
384
+ namespace(:test_install_if_modified) {
385
+ task(:default) {
386
+ methods.grep(/^test_/).each do |m|
387
+ send(m)
388
+ end
389
+ }
390
+ before "test_install_if_modified", "test_install_if_modified:setup"
391
+ after "test_install_if_modified", "test_install_if_modified:teardown"
392
+
393
+ task(:setup) {
394
+ run_locally("mkdir -p tmp")
395
+ run("mkdir -p tmp")
396
+ }
397
+
398
+ task(:teardown) {
399
+ run_locally("rm -rf tmp")
400
+ sudo("rm -rf tmp")
401
+ }
402
+
403
+ task(:test_if_not_modified) {
404
+ _test_install("foo", "foo", :install => :if_modified) do |from, to, tempfrom, tempto|
405
+ assert_file_equals(tempto, to)
406
+ assert_timestamp_equals(tempto, to)
407
+ assert_timestamp_not_equals(tempfrom, to)
408
+ assert_file_mode_equals(tempto, to)
409
+ end
410
+ }
411
+
412
+ task(:test_if_modified) {
413
+ _test_install("foo", "bar", :install => :if_modified, :digest => :sha1) do |from, to, tempfrom, tempto|
414
+ assert_file_equals(tempfrom, to)
415
+ assert_timestamp_equals(tempfrom, to)
416
+ assert_timestamp_not_equals(tempto, to)
417
+ assert_file_mode_equals(tempto, to)
418
+ end
419
+ }
420
+
421
+ task(:test_if_missing) {
422
+ _test_install("baz", nil, :install => :if_modified) do |from, to, tempfrom, tempto|
423
+ assert_file_equals(tempfrom, to)
424
+ assert_timestamp_equals(tempfrom, to)
425
+ end
426
+ }
427
+
428
+ task(:test_with_mode) {
429
+ _test_install("foo", "bar", :install => :if_modified, :mode => 0755) do |from, to, tempfrom, tempto|
430
+ assert_file_mode(0755, to)
431
+ end
432
+ }
433
+
434
+ task(:test_via_sudo) {
435
+ _test_install("bar", "baz", :install => :if_modified, :orig_owner => 0, :via => :sudo) do |from, to, tempfrom, tempto|
436
+ assert_file_owner(0, to)
437
+ end
438
+ }
439
+
440
+ task(:test_with_mode_via_sudo) {
441
+ _test_install("bar", "baz", :install => :if_modified, :orig_owner => 0, :mode => 0644, :via => :sudo) do |from, to, tempfrom, tempto|
442
+ assert_file_mode(0644, to)
443
+ assert_file_owner(0, to)
444
+ end
445
+ }
446
+
447
+ task(:test_preserve_original_mode) {
448
+ _test_install("foo", "bar", :install => :if_modified, :orig_mode => 0600) do |from, to, tempfrom, tempto|
449
+ assert_file_mode(0600, to)
450
+ end
451
+ }
452
+
453
+ task(:test_ignore_original_mode) {
454
+ _test_install("foo", "bar", :install => :if_modified, :orig_mode => 0400, :mode => 0700) do |from, to, tempfrom, tempto|
455
+ assert_file_mode(0700, to)
456
+ end
457
+ }
458
+
459
+ task(:test_preserve_original_owner_group_mode) {
460
+ _test_install("foo", "bar", :install => :if_modified, :orig_owner => 0, :orig_group => 0, :orig_mode => 0640, :via => :sudo) do |from, to, tempfrom, tempto|
461
+ assert_file_mode(0640, to)
462
+ assert_file_owner(0, to)
463
+ assert_file_group(0, to)
464
+ end
465
+ }
466
+
467
+ task(:test_ignore_original_owner_group_mode) {
468
+ _test_install("bar", "baz", :install => :if_modified, :owner => 0, :group => 0, :mode => 0640, :via => :sudo) do |from, to, tempfrom, tempto|
469
+ assert_file_mode(0640, to)
470
+ assert_file_owner(0, to)
471
+ assert_file_group(0, to)
472
+ end
473
+ }
474
+ }
475
+
476
+ # vim:set ft=ruby sw=2 ts=2 :
@@ -0,0 +1,5 @@
1
+ /.bundle
2
+ /.vagrant
3
+ /known_hosts
4
+ /tmp
5
+ /vendor
@@ -0,0 +1,2 @@
1
+ load "deploy"
2
+ load "../config/deploy"
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec(:path => "../..")
@@ -0,0 +1,99 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ Vagrant::Config.run do |config|
5
+ # All Vagrant configuration is done here. The most common configuration
6
+ # options are documented and commented below. For a complete reference,
7
+ # please see the online documentation at vagrantup.com.
8
+
9
+ # Every Vagrant virtual environment requires a box to build off of.
10
+ config.vm.box = "precise64"
11
+
12
+ # The url from where the 'config.vm.box' box will be fetched if it
13
+ # doesn't already exist on the user's system.
14
+ config.vm.box_url = "http://files.vagrantup.com/precise64.box"
15
+
16
+ # Boot with a GUI so you can see the screen. (Default is headless)
17
+ # config.vm.boot_mode = :gui
18
+
19
+ # Assign this VM to a host-only network IP, allowing you to access it
20
+ # via the IP. Host-only networks can talk to the host machine as well as
21
+ # any other machines on the same network, but cannot be accessed (through this
22
+ # network interface) by any external networks.
23
+ config.vm.network :hostonly, "192.168.33.10"
24
+
25
+ # Assign this VM to a bridged network, allowing you to connect directly to a
26
+ # network using the host's network device. This makes the VM appear as another
27
+ # physical device on your network.
28
+ # config.vm.network :bridged
29
+
30
+ # Forward a port from the guest to the host, which allows for outside
31
+ # computers to access the VM, whereas host only networking does not.
32
+ # config.vm.forward_port 80, 8080
33
+
34
+ # Share an additional folder to the guest VM. The first argument is
35
+ # an identifier, the second is the path on the guest to mount the
36
+ # folder, and the third is the path on the host to the actual folder.
37
+ # config.vm.share_folder "v-data", "/vagrant_data", "../data"
38
+
39
+ # Enable provisioning with Puppet stand alone. Puppet manifests
40
+ # are contained in a directory path relative to this Vagrantfile.
41
+ # You will need to create the manifests directory and a manifest in
42
+ # the file precise-amd64.pp in the manifests_path directory.
43
+ #
44
+ # An example Puppet manifest to provision the message of the day:
45
+ #
46
+ # # group { "puppet":
47
+ # # ensure => "present",
48
+ # # }
49
+ # #
50
+ # # File { owner => 0, group => 0, mode => 0644 }
51
+ # #
52
+ # # file { '/etc/motd':
53
+ # # content => "Welcome to your Vagrant-built virtual machine!
54
+ # # Managed by Puppet.\n"
55
+ # # }
56
+ #
57
+ # config.vm.provision :puppet do |puppet|
58
+ # puppet.manifests_path = "manifests"
59
+ # puppet.manifest_file = "precise-amd64.pp"
60
+ # end
61
+
62
+ # Enable provisioning with chef solo, specifying a cookbooks path, roles
63
+ # path, and data_bags path (all relative to this Vagrantfile), and adding
64
+ # some recipes and/or roles.
65
+ #
66
+ # config.vm.provision :chef_solo do |chef|
67
+ # chef.cookbooks_path = "../my-recipes/cookbooks"
68
+ # chef.roles_path = "../my-recipes/roles"
69
+ # chef.data_bags_path = "../my-recipes/data_bags"
70
+ # chef.add_recipe "mysql"
71
+ # chef.add_role "web"
72
+ #
73
+ # # You may also specify custom JSON attributes:
74
+ # chef.json = { :mysql_password => "foo" }
75
+ # end
76
+
77
+ # Enable provisioning with chef server, specifying the chef server URL,
78
+ # and the path to the validation key (relative to this Vagrantfile).
79
+ #
80
+ # The Opscode Platform uses HTTPS. Substitute your organization for
81
+ # ORGNAME in the URL and validation key.
82
+ #
83
+ # If you have your own Chef Server, use the appropriate URL, which may be
84
+ # HTTP instead of HTTPS depending on your configuration. Also change the
85
+ # validation key to validation.pem.
86
+ #
87
+ # config.vm.provision :chef_client do |chef|
88
+ # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
89
+ # chef.validation_key_path = "ORGNAME-validator.pem"
90
+ # end
91
+ #
92
+ # If you're using the Opscode platform, your validator client is
93
+ # ORGNAME-validator, replacing ORGNAME with your organization name.
94
+ #
95
+ # IF you have your own Chef Server, the default validation client name is
96
+ # chef-validator, unless you changed the configuration.
97
+ #
98
+ # chef.validation_client_name = "ORGNAME-validator"
99
+ end
@@ -0,0 +1,7 @@
1
+ #!/bin/sh -e
2
+
3
+ bundle exec vagrant up
4
+ bundle exec cap test_all
5
+ bundle exec vagrant destroy -f
6
+
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.0.4
4
+ version: 0.1.0
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-07 00:00:00.000000000 Z
12
+ date: 2013-03-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: capistrano
@@ -27,6 +27,54 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: net-scp
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.0.4
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.4
46
+ - !ruby/object:Gem::Dependency
47
+ name: net-ssh
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.2.2
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.2.2
62
+ - !ruby/object:Gem::Dependency
63
+ name: vagrant
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.0.6
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.0.6
30
78
  description: A sort of utilities which helps you transferring files with Capistrano.
31
79
  email:
32
80
  - yamashita@geishatokyo.com
@@ -42,6 +90,17 @@ files:
42
90
  - capistrano-file-transfer-ext.gemspec
43
91
  - lib/capistrano/configuration/actions/file_transfer_ext.rb
44
92
  - lib/capistrano/configuration/actions/file_transfer_ext/version.rb
93
+ - test/centos6-64/.gitignore
94
+ - test/centos6-64/Capfile
95
+ - test/centos6-64/Gemfile
96
+ - test/centos6-64/Vagrantfile
97
+ - test/centos6-64/run.sh
98
+ - test/config/deploy.rb
99
+ - test/precise64/.gitignore
100
+ - test/precise64/Capfile
101
+ - test/precise64/Gemfile
102
+ - test/precise64/Vagrantfile
103
+ - test/precise64/run.sh
45
104
  homepage: https://github.com/yyuu/capistrano-file-transfer-ext
46
105
  licenses: []
47
106
  post_install_message:
@@ -66,4 +125,15 @@ rubygems_version: 1.8.23
66
125
  signing_key:
67
126
  specification_version: 3
68
127
  summary: A sort of utilities which helps you transferring files with Capistrano.
69
- test_files: []
128
+ test_files:
129
+ - test/centos6-64/.gitignore
130
+ - test/centos6-64/Capfile
131
+ - test/centos6-64/Gemfile
132
+ - test/centos6-64/Vagrantfile
133
+ - test/centos6-64/run.sh
134
+ - test/config/deploy.rb
135
+ - test/precise64/.gitignore
136
+ - test/precise64/Capfile
137
+ - test/precise64/Gemfile
138
+ - test/precise64/Vagrantfile
139
+ - test/precise64/run.sh