s3cp 0.2.3 → 0.2.4

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