s3cp 0.2.3 → 0.2.4

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/History.txt CHANGED
@@ -1,4 +1,31 @@
1
- === 0.2.4 / (Pending)
1
+ === 0.2.5 / (Pending)
2
+
3
+ === 0.2.4 / 2012-02-27
4
+
5
+ * Fixed: Handling of relative paths in s3cp.
6
+
7
+ % s3cp -r bucket:path/to/files /local/path
8
+
9
+ will now produce local files:
10
+
11
+ /local/path/files/A
12
+ /local/path/files/B
13
+ /local/path/files/C
14
+ /local/path/files2/D
15
+ /local/path/files2/E
16
+ ...
17
+
18
+ whereas,
19
+
20
+ % s3cp -r bucket:path/to/files/ /local/path
21
+
22
+
23
+ /local/path/A
24
+ /local/path/B
25
+ /local/path/C
26
+ ...
27
+
28
+ (note the trailing slash on "bucket:path/to/files")
2
29
 
3
30
  === 0.2.3 / (2012-02-24)
4
31
 
data/lib/s3cp/s3cp.rb CHANGED
@@ -198,8 +198,11 @@ def no_slash(path)
198
198
  path.match(/^\//) ? path[1..-1] : path
199
199
  end
200
200
 
201
+ # relative("path/to/", "path/to/file") => "file"
202
+ # relative("path/to", "path/to/file") => "to/file"
201
203
  def relative(base, path)
202
- no_slash(path[base.length..-1])
204
+ dir = base.rindex("/") ? base[0..base.rindex("/")] : ""
205
+ no_slash(path[dir.length..-1])
203
206
  end
204
207
 
205
208
  def log(msg)
data/lib/s3cp/version.rb CHANGED
@@ -16,5 +16,5 @@
16
16
  # the License.
17
17
 
18
18
  module S3CP
19
- VERSION = "0.2.3"
19
+ VERSION = "0.2.4"
20
20
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: s3cp
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 3
10
- version: 0.2.3
9
+ - 4
10
+ version: 0.2.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Alex Boisvert
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-02-24 00:00:00 Z
18
+ date: 2012-02-28 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  prerelease: false
@@ -154,7 +154,6 @@ files:
154
154
  - lib/s3cp/utils.rb
155
155
  - lib/s3cp/s3mod.rb
156
156
  - lib/s3cp/s3cat.rb
157
- - lib/s3cp/#Untitled-7#
158
157
  - lib/s3cp/completion.rb
159
158
  - History.txt
160
159
  - README.md
@@ -1,288 +0,0 @@
1
- diff --git a/History.txt b/History.txt
2
- index 8de2725..37eaec8 100644
3
- --- a/History.txt
4
- +++ b/History.txt
5
- @@ -1,5 +1,9 @@
6
- === 0.2.3 / (Pending)
7
-
8
- +* Added: --include and --exclude REGEX support for s3cp
9
- +
10
- +* Added: --sync mode for s3cp
11
- +
12
- === 0.2.2 / (2012-02-23)
13
-
14
- * Added: Progress bars during upload/download if $stdout.isatty
15
- diff --git a/lib/s3cp/s3cp.rb b/lib/s3cp/s3cp.rb
16
- index 0fabcdd..a1423f5 100644
17
- --- a/lib/s3cp/s3cp.rb
18
- +++ b/lib/s3cp/s3cp.rb
19
- @@ -35,6 +35,9 @@ options[:overwrite] = ENV["S3CP_RETRIES"] ? (ENV["S3CP_OVERWRITE"] =~ /y|y
20
- options[:checksum] = ENV["S3CP_CHECKSUM"] ? (ENV["S3CP_CHECKSUM"] =~ /y|yes|true|1|^\s*$/i ? true : false) : true
21
- options[:retries] = ENV["S3CP_RETRIES"] ? ENV["S3CP_RETRIES"].to_i : 5
22
- options[:retry_delay] = ENV["S3CP_RETRY_DELAY"] ? ENV["S3CP_RETRY_DELAY"].to_i : 1
23
- +options[:include_regex] = []
24
- +options[:exclude_regex] = []
25
- +options[:sync] = false
26
-
27
- op = OptionParser.new do |opts|
28
- opts.banner = <<-BANNER
29
- @@ -67,6 +70,10 @@ op = OptionParser.new do |opts|
30
- options[:overwrite] = false
31
- end
32
-
33
- + opts.on("--sync", "Sync mode: use checksum to determine if files need copying.") do
34
- + options[:sync] = true
35
- + end
36
- +
37
- opts.on("--max-attempts N", "Number of attempts to upload/download until checksum matches (default #{options[:retries]})") do |attempts|
38
- options[:retries] = attempts.to_i
39
- end
40
- @@ -90,6 +97,14 @@ op = OptionParser.new do |opts|
41
- opts.separator " AMZ headers: \'x-amz-acl: public-read\'"
42
- opts.separator ""
43
-
44
- + opts.on("-i REGEX", "--include REGEX", "Copy only files matching the following regular expression.") do |regex|
45
- + options[:include_regex] << regex
46
- + end
47
- +
48
- + opts.on("-x REGEX", "--exclude REGEX", "Do not copy files matching provided regular expression.") do |regex|
49
- + options[:exclude_regex] << regex
50
- + end
51
- +
52
- opts.on("--verbose", "Verbose mode") do
53
- options[:verbose] = true
54
- end
55
- @@ -110,6 +125,16 @@ if ARGV.size < 2
56
- exit
57
- end
58
-
59
- +if options[:include_regex].any? && !options[:recursive]
60
- + puts "-i (--include regex) option requires -r (recursive) option."
61
- + exit(1)
62
- +end
63
- +
64
- +if options[:exclude_regex].any? && !options[:recursive]
65
- + puts "-x (--exclude regex) option requires -r (recursive) option."
66
- + exit(1)
67
- +end
68
- +
69
- destination = ARGV.last
70
- sources = ARGV[0..-2]
71
-
72
- @@ -144,6 +169,16 @@ end
73
- @bucket = $1
74
- @prefix = $2
75
-
76
- +@includes = options[:include_regex].map { |s| Regexp.new(s) }
77
- +@excludes = options[:exclude_regex].map { |s| Regexp.new(s) }
78
- +
79
- +def match(path)
80
- + matching = true
81
- + return false if @includes.any? && !@includes.any? { |regex| regex.match(path) }
82
- + return false if @excludes.any? && @excludes.any? { |regex| regex.match(path) }
83
- + true
84
- +end
85
- +
86
- @s3 = S3CP.connect()
87
-
88
- def direction(from, to)
89
- @@ -217,68 +252,90 @@ end
90
-
91
- def local_to_s3(bucket_to, key, file, options = {})
92
- log(with_headers("Copy #{file} to s3://#{bucket_to}/#{key}"))
93
- - if options[:checksum]
94
- - expected_md5 = md5(file)
95
- +
96
- + expected_md5 = if options[:checksum] || options[:sync]
97
- + md5(file)
98
- end
99
- - retries = 0
100
- - begin
101
- - if retries == options[:retries]
102
- - fail "Unable to upload to s3://#{bucket_to}/#{key} after #{retries} attempts."
103
- - end
104
- - if retries > 0
105
- - STDERR.puts "Warning: failed checksum for s3://#{bucket_to}/#{bucket_to}. Retrying #{options[:retries] - retries} more time(s)."
106
- - sleep options[:retry_delay]
107
- +
108
- + actual_md5 = if options[:sync]
109
- + md5 = s3_checksum(bucket_to, key)
110
- + case md5
111
- + when :not_found
112
- + nil
113
- + when :invalid
114
- + STDERR.puts "Warning: invalid MD5 checksum in metadata; file will be force-copied."
115
- + nil
116
- + else
117
- + md5
118
- end
119
- + end
120
-
121
- - f = File.open(file)
122
- + if actual_md5.nil? || (options[:sync] && expected_md5 != actual_md5)
123
- + retries = 0
124
- begin
125
- - if $stdout.isatty
126
- - f = Proxy.new(f)
127
- - progress_bar = ProgressBar.new(File.basename(file), File.size(file)).tap do |p|
128
- - p.file_transfer_mode
129
- - end
130
- - class << f
131
- - attr_accessor :progress_bar
132
- - def read(length, buffer=nil)
133
- - begin
134
- - result = @target.read(length, buffer)
135
- - @progress_bar.inc result.length if result
136
- - result
137
- - rescue => e
138
- - puts e
139
- - raise e
140
- + if retries == options[:retries]
141
- + fail "Unable to upload to s3://#{bucket_to}/#{key} after #{retries} attempts."
142
- + end
143
- + if retries > 0
144
- + STDERR.puts "Warning: failed checksum for s3://#{bucket_to}/#{bucket_to}. Retrying #{options[:retries] - retries} more time(s)."
145
- + sleep options[:retry_delay]
146
- + end
147
- +
148
- + f = File.open(file)
149
- + begin
150
- + if $stdout.isatty
151
- + f = Proxy.new(f)
152
- + progress_bar = ProgressBar.new(File.basename(file), File.size(file)).tap do |p|
153
- + p.file_transfer_mode
154
- + end
155
- + class << f
156
- + attr_accessor :progress_bar
157
- + def read(length, buffer=nil)
158
- + begin
159
- + result = @target.read(length, buffer)
160
- + @progress_bar.inc result.length if result
161
- + result
162
- + rescue => e
163
- + puts e
164
- + raise e
165
- + end
166
- end
167
- end
168
- + f.progress_bar = progress_bar
169
- + else
170
- + progress_bar = nil
171
- end
172
- - f.progress_bar = progress_bar
173
- - else
174
- - progress_bar = nil
175
- - end
176
-
177
- - meta = @s3.interface.put(bucket_to, key, f, @headers)
178
- - progress_bar.finish if progress_bar
179
- + meta = @s3.interface.put(bucket_to, key, f, @headers)
180
- + progress_bar.finish if progress_bar
181
-
182
- - if options[:checksum]
183
- - metadata = @s3.interface.head(bucket_to, key)
184
- - actual_md5 = metadata["etag"] or fail "Unable to get etag/md5 for #{bucket_to}:#{key}"
185
- - actual_md5 = actual_md5.sub(/^"/, "").sub(/"$/, "") # strip beginning and trailing quotes
186
- - if actual_md5 =~ /-/
187
- - STDERR.puts "Warning: invalid MD5 checksum in metadata; skipped checksum verification."
188
- - actual_md5 = nil
189
- + if options[:checksum]
190
- + actual_md5 = s3_checksum(bucket_to, key)
191
- + unless actual_md5.is_a? String
192
- + STDERR.puts "Warning: invalid MD5 checksum in metadata; skipped checksum verification."
193
- + actual_md5 = nil
194
- + end
195
- end
196
- + rescue => e
197
- + raise e unless options[:checksum]
198
- + STDERR.puts e
199
- + ensure
200
- + f.close()
201
- end
202
- - rescue => e
203
- - raise e unless options[:checksum]
204
- - STDERR.puts e
205
- - ensure
206
- - f.close()
207
- - end
208
- - retries += 1
209
- + retries += 1
210
- end until options[:checksum] == false || actual_md5.nil? || expected_md5 == actual_md5
211
- + else
212
- + log "Already synchronized."
213
- + end
214
- end
215
-
216
- def s3_to_local(bucket_from, key_from, dest, options = {})
217
- log("Copy s3://#{bucket_from}/#{key_from} to #{dest}")
218
- +
219
- + actual_md5 = if options[:sync] && File.exist?(dest)
220
- + md5(dest)
221
- + end
222
- +
223
- retries = 0
224
- begin
225
- if retries == options[:retries]
226
- @@ -292,34 +349,38 @@ def s3_to_local(bucket_from, key_from, dest, options = {})
227
-
228
- f = File.new(dest, "wb")
229
- begin
230
- - size = nil
231
- - if options[:checksum]
232
- - metadata = @s3.interface.head(bucket_from, key_from)
233
- - expected_md5 = metadata["etag"] or fail "Unable to get etag/md5 for #{bucket_from}:#{key_from}"
234
- - expected_md5 = expected_md5.sub(/^"/, "").sub(/"$/, "") # strip beginning and trailing quotes
235
- - if expected_md5 =~ /-/
236
- + expected_md5 = if options[:checksum] || options[:sync]
237
- + md5 = s3_checksum(bucket_from, key_from)
238
- + if options[:sync] && !md5.is_a?(String)
239
- + STDERR.puts "Warning: invalid MD5 checksum in metadata; file will be force-copied."
240
- + nil
241
- + elsif !md5.is_a? String
242
- STDERR.puts "Warning: invalid MD5 checksum in metadata; skipped checksum verification."
243
- - expected_md5 = nil
244
- + nil
245
- + else
246
- + md5
247
- end
248
- - size = metadata["content-length"].to_i
249
- - elsif $stdout.isatty
250
- - metadata = @s3.interface.head(bucket_from, key_from)
251
- - size = metadata["content-length"].to_i
252
- end
253
- - begin
254
- - progress_bar = if size
255
- - ProgressBar.new(File.basename(key_from), size).tap do |p|
256
- - p.file_transfer_mode
257
- +
258
- + if (expected_md5 == nil) || (options[:sync] && expected_md5 != actual_md5)
259
- + begin
260
- + progress_bar = if $stdout.isatty
261
- + size = s3_size(bucket_from, key_from)
262
- + ProgressBar.new(File.basename(key_from), size).tap do |p|
263
- + p.file_transfer_mode
264
- + end
265
- end
266
- + @s3.interface.get(bucket_from, key_from) do |chunk|
267
- + f.write(chunk)
268
- + progress_bar.inc chunk.size if progress_bar
269
- + end
270
- + progress_bar.finish
271
- + rescue => e
272
- + progress_bar.halt if progress_bar
273
- + raise e
274
- end
275
- - @s3.interface.get(bucket_from, key_from) do |chunk|
276
- - f.write(chunk)
277
- - progress_bar.inc chunk.size if progress_bar
278
- - end
279
- - progress_bar.finish
280
- - rescue => e
281
- - progress_bar.halt if progress_bar
282
- - raise e
283
- + else
284
- + log("Already synchronized")
285
- end
286
- rescue => e
287
- raise e unless options[:checksum]
288
-