capistrano-file-transfer-ext 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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