s3_zipper 1.0.5 → 2.0.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.
- checksums.yaml +4 -4
- data/.env.test +0 -1
- data/.rubocop.yml +174 -0
- data/.rubocop_todo.yml +1569 -0
- data/Gemfile.lock +32 -19
- data/README.md +20 -1
- data/bin/console +1 -0
- data/exe/s3_zipper +13 -12
- data/lib/s3_zipper.rb +44 -26
- data/lib/s3_zipper/client.rb +13 -3
- data/lib/s3_zipper/progress.rb +38 -12
- data/lib/s3_zipper/spinner.rb +58 -0
- data/lib/s3_zipper/version.rb +3 -1
- data/s3_zipper.gemspec +13 -8
- metadata +56 -12
data/Gemfile.lock
CHANGED
@@ -1,34 +1,38 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
s3_zipper (1.0.
|
5
|
-
aws-sdk
|
4
|
+
s3_zipper (1.0.9)
|
5
|
+
aws-sdk (~> 2)
|
6
|
+
concurrent-ruby (~> 1.1)
|
7
|
+
multiblock (~> 0.2.1)
|
6
8
|
ruby-progressbar (~> 1)
|
7
9
|
rubyzip (>= 1.0.0)
|
8
10
|
|
9
11
|
GEM
|
10
12
|
remote: https://rubygems.org/
|
11
13
|
specs:
|
12
|
-
|
13
|
-
aws-
|
14
|
-
aws-sdk
|
15
|
-
aws-
|
16
|
-
|
17
|
-
aws-sigv4 (~> 1.1)
|
18
|
-
jmespath (~> 1.0)
|
19
|
-
aws-sdk-kms (1.18.0)
|
20
|
-
aws-sdk-core (~> 3, >= 3.48.2)
|
21
|
-
aws-sigv4 (~> 1.1)
|
22
|
-
aws-sdk-s3 (1.36.1)
|
23
|
-
aws-sdk-core (~> 3, >= 3.48.2)
|
24
|
-
aws-sdk-kms (~> 1)
|
14
|
+
ast (2.4.0)
|
15
|
+
aws-eventstream (1.1.0)
|
16
|
+
aws-sdk (2.11.632)
|
17
|
+
aws-sdk-resources (= 2.11.632)
|
18
|
+
aws-sdk-core (2.11.632)
|
25
19
|
aws-sigv4 (~> 1.0)
|
26
|
-
|
27
|
-
|
20
|
+
jmespath (~> 1.0)
|
21
|
+
aws-sdk-resources (2.11.632)
|
22
|
+
aws-sdk-core (= 2.11.632)
|
23
|
+
aws-sigv4 (1.2.2)
|
24
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
25
|
+
concurrent-ruby (1.1.5)
|
28
26
|
diff-lcs (1.3)
|
29
27
|
docile (1.3.1)
|
28
|
+
jaro_winkler (1.5.2)
|
30
29
|
jmespath (1.4.0)
|
31
30
|
json (2.2.0)
|
31
|
+
multiblock (0.2.1)
|
32
|
+
parallel (1.17.0)
|
33
|
+
parser (2.6.3.0)
|
34
|
+
ast (~> 2.4.0)
|
35
|
+
rainbow (3.0.0)
|
32
36
|
rake (10.5.0)
|
33
37
|
rake-compiler (1.0.7)
|
34
38
|
rake
|
@@ -45,14 +49,22 @@ GEM
|
|
45
49
|
diff-lcs (>= 1.2.0, < 2.0)
|
46
50
|
rspec-support (~> 3.8.0)
|
47
51
|
rspec-support (3.8.0)
|
52
|
+
rubocop (0.70.0)
|
53
|
+
jaro_winkler (~> 1.5.1)
|
54
|
+
parallel (~> 1.10)
|
55
|
+
parser (>= 2.6)
|
56
|
+
rainbow (>= 2.2.2, < 4.0)
|
57
|
+
ruby-progressbar (~> 1.7)
|
58
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
48
59
|
ruby-progressbar (1.10.0)
|
49
|
-
rubyzip (1.2.
|
60
|
+
rubyzip (1.2.3)
|
50
61
|
simplecov (0.16.1)
|
51
62
|
docile (~> 1.1)
|
52
63
|
json (>= 1.8, < 3)
|
53
64
|
simplecov-html (~> 0.10.0)
|
54
65
|
simplecov-html (0.10.2)
|
55
66
|
thor (0.20.3)
|
67
|
+
unicode-display_width (1.6.0)
|
56
68
|
|
57
69
|
PLATFORMS
|
58
70
|
ruby
|
@@ -62,9 +74,10 @@ DEPENDENCIES
|
|
62
74
|
rake (~> 10.0)
|
63
75
|
rake-compiler
|
64
76
|
rspec (~> 3.0)
|
77
|
+
rubocop (~> 0.70.0)
|
65
78
|
s3_zipper!
|
66
79
|
simplecov
|
67
80
|
thor
|
68
81
|
|
69
82
|
BUNDLED WITH
|
70
|
-
2.
|
83
|
+
2.2.12
|
data/README.md
CHANGED
@@ -46,6 +46,25 @@ zipper.zip_to_s3(keys)
|
|
46
46
|
# }
|
47
47
|
```
|
48
48
|
|
49
|
+
### Event Handling
|
50
|
+
```ruby
|
51
|
+
keys = ["documents/files/790/306/985/original/background-10.jpg", "documents/files/790/307/076/original/background-10.jpg"]
|
52
|
+
zipper.zip_to_s3 keys do
|
53
|
+
on.start { puts 'Starting to zip' }
|
54
|
+
on.progress { |progress| puts progress.percentage }
|
55
|
+
on.finish { puts 'Finished zipping' }
|
56
|
+
on.upload { puts 'Upload complete' }
|
57
|
+
end
|
58
|
+
# {
|
59
|
+
# :key=>"3dc29e9ba0a069eb5d0783f07b12e1b3.zip",
|
60
|
+
# :url => "https://bucket_name.s3.us-west-2.amazonaws.com/35b6f0e2ee91aa0e3c0640c7a4b2b7db.zip"
|
61
|
+
# :zipped=>["documents/files/790/306/985/original/background-10.jpg", "documents/files/790/307/076/original/background-10.jpg"],
|
62
|
+
# :failed=>[]
|
63
|
+
# }
|
64
|
+
```
|
65
|
+
|
66
|
+
|
67
|
+
|
49
68
|
## Development
|
50
69
|
|
51
70
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -54,7 +73,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
54
73
|
|
55
74
|
## Contributing
|
56
75
|
|
57
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
76
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/capshareinc/s3_zipper. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
58
77
|
|
59
78
|
## License
|
60
79
|
|
data/bin/console
CHANGED
data/exe/s3_zipper
CHANGED
@@ -1,24 +1,25 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require "s3_zipper"
|
4
|
-
require
|
5
|
-
require
|
5
|
+
require "thor"
|
6
|
+
require "awesome_print"
|
6
7
|
class CLI < Thor
|
7
|
-
desc
|
8
|
-
method_option :filename, type: :string, aliases:
|
9
|
-
method_option :bucket, type: :string, default: ENV[
|
8
|
+
desc "local_file FILES", "zip files from s3 to a local file"
|
9
|
+
method_option :filename, type: :string, aliases: "f", desc: "Name of the created zip archive"
|
10
|
+
method_option :bucket, type: :string, default: ENV["AWS_BUCKET"], required: !ENV["AWS_BUCKET"].nil?, aliases: "b", desc: "Name of the bucket the files are in"
|
10
11
|
|
11
|
-
def local_file
|
12
|
+
def local_file *files
|
12
13
|
S3Zipper.new(options[:bucket]).zip_to_local_file(files, file: options[:filename] || SecureRandom.hex)
|
13
14
|
end
|
14
15
|
|
15
|
-
desc
|
16
|
-
method_option :filename, type: :string, aliases:
|
17
|
-
method_option :path, type: :string, aliases:
|
18
|
-
method_option :bucket, type: :string, default: ENV[
|
16
|
+
desc "s3 FILES", "zip files from s3 to s3"
|
17
|
+
method_option :filename, type: :string, aliases: "f", desc: "Name of the created zip archive"
|
18
|
+
method_option :path, type: :string, aliases: "p", desc: "Path to the file in s3"
|
19
|
+
method_option :bucket, type: :string, default: ENV["AWS_BUCKET"], required: !ENV["AWS_BUCKET"].nil?, aliases: "b", desc: "Name of the bucket the files are in"
|
19
20
|
|
20
|
-
def s3
|
21
|
+
def s3 *files
|
21
22
|
S3Zipper.new(options[:bucket]).zip_to_s3(files, filename: options[:filename] || SecureRandom.hex, path: options[:path])
|
22
23
|
end
|
23
24
|
end
|
24
|
-
CLI.start(ARGV)
|
25
|
+
CLI.start(ARGV)
|
data/lib/s3_zipper.rb
CHANGED
@@ -1,37 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "s3_zipper/version"
|
2
|
-
require
|
4
|
+
require "s3_zipper/progress"
|
5
|
+
require "s3_zipper/spinner"
|
3
6
|
require "s3_zipper/client"
|
4
7
|
require "zip"
|
8
|
+
require "multiblock"
|
5
9
|
|
6
10
|
class S3Zipper
|
7
|
-
attr_accessor :client, :options, :progress
|
11
|
+
attr_accessor :client, :options, :progress, :zip_client, :wrapper
|
8
12
|
|
9
13
|
# @param [String] bucket - bucket that files exist in
|
10
14
|
# @param [Hash] options - options for zipper
|
11
15
|
# @option options [Boolean] :progress - toggles progress tracking
|
12
16
|
# @return [S3Zipper]
|
13
17
|
def initialize bucket, options = {}
|
14
|
-
@options
|
15
|
-
@
|
16
|
-
@progress
|
18
|
+
@options = options
|
19
|
+
@wrapper = Multiblock.wrapper
|
20
|
+
@progress = Progress.new(enabled: options[:progress], format: "%e %c/%C %t", total: nil, length: 80, autofinish: false)
|
21
|
+
@client = Client.new(bucket, options)
|
22
|
+
@zip_client = Client.new(options[:zip_bucket], options) if options[:zip_bucket]
|
23
|
+
@zip_client ||= @client
|
17
24
|
end
|
18
25
|
|
19
26
|
# Zips files from s3 to a local zip
|
20
27
|
# @param [Array] keys - Array of s3 keys to zip
|
21
28
|
# @param [String, File] file - Filename or file object for the zip, defaults to a random string
|
22
29
|
# @return [Hash]
|
23
|
-
def zip_to_local_file
|
24
|
-
|
25
|
-
|
30
|
+
def zip_to_local_file keys, file: SecureRandom.hex
|
31
|
+
yield(wrapper) if block_given?
|
32
|
+
file = file.is_a?(File) ? file : File.open("#{file}.zip", "w")
|
33
|
+
zip(keys, file.path)
|
26
34
|
end
|
27
35
|
|
28
36
|
# Zips files from s3 to a temporary zip
|
29
37
|
# @param [Array] keys - Array of s3 keys to zip
|
30
38
|
# @param [String, File] filename - Name of file, defaults to a random string
|
31
39
|
# @return [Hash]
|
32
|
-
def zip_to_tempfile
|
33
|
-
|
34
|
-
|
40
|
+
def zip_to_tempfile keys, filename: SecureRandom.hex, cleanup: false
|
41
|
+
yield(wrapper) if block_given?
|
42
|
+
zipfile = Tempfile.new([filename, ".zip"])
|
43
|
+
result = zip(keys, zipfile.path)
|
35
44
|
zipfile.unlink if cleanup
|
36
45
|
result
|
37
46
|
end
|
@@ -41,36 +50,45 @@ class S3Zipper
|
|
41
50
|
# @param [String, File] filename - Name of file, defaults to a random string
|
42
51
|
# @param [String] path - path for file in s3
|
43
52
|
# @return [Hash]
|
44
|
-
def zip_to_s3 keys, filename: SecureRandom.hex, path: nil, s3_options: {}
|
45
|
-
|
53
|
+
def zip_to_s3 keys, filename: SecureRandom.hex, path: nil, s3_options: {}
|
54
|
+
yield(wrapper) if block_given?
|
46
55
|
filename = "#{path ? "#{path}/" : ''}#{filename}.zip"
|
47
|
-
result = zip_to_tempfile(keys, filename: filename, cleanup: false
|
48
|
-
|
49
|
-
client.upload(result.delete(:filename), filename, options: s3_options)
|
50
|
-
progress.finish(title: "Uploaded zip to #{filename}")
|
56
|
+
result = zip_to_tempfile(keys, filename: filename, cleanup: false)
|
57
|
+
zip_client.upload(result.delete(:filename), filename, options: s3_options)
|
51
58
|
result[:key] = filename
|
52
|
-
result[:url] =
|
59
|
+
result[:url] = zip_client.get_url(result[:key])
|
60
|
+
wrapper.call(:upload, result)
|
53
61
|
result
|
54
62
|
end
|
55
63
|
|
56
64
|
private
|
57
65
|
|
66
|
+
def add_to_zip zipfile, filename, file, n = 0
|
67
|
+
existing_file = zipfile.find_entry(filename)
|
68
|
+
if existing_file
|
69
|
+
filename = "#{File.basename(filename, ".*").split('(').first}(#{n})#{File.extname(filename)}"
|
70
|
+
add_to_zip(zipfile, filename, file, n + 1)
|
71
|
+
else
|
72
|
+
zipfile.add(filename, file.path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
58
76
|
# @param [Array] keys - Array of s3 keys to zip
|
59
77
|
# @param [String] path - path to zip
|
60
78
|
# @yield [progress]
|
61
79
|
# @return [Hash]
|
62
|
-
def zip
|
63
|
-
|
64
|
-
total += keys.size
|
65
|
-
progress.update_attrs total: total, title: "Zipping Keys to #{path}"
|
80
|
+
def zip keys, path
|
81
|
+
progress.reset total: keys.size, title: "Zipping Keys to #{path}"
|
66
82
|
Zip::File.open(path, Zip::File::CREATE) do |zipfile|
|
83
|
+
wrapper.call(:start, zipfile)
|
67
84
|
@failed, @successful = client.download_keys keys do |file, key|
|
68
|
-
progress.increment
|
69
|
-
|
70
|
-
yield(zipfile, progress) if block_given?
|
85
|
+
progress.increment title: "Zipping #{key} to #{path}"
|
86
|
+
wrapper.call(:progress, progress)
|
71
87
|
next if file.nil?
|
72
|
-
zipfile
|
88
|
+
add_to_zip(zipfile, File.basename(key), file)
|
73
89
|
end
|
90
|
+
progress.finish(title: "Zipped keys to #{path}")
|
91
|
+
wrapper.call(:finish, zipfile)
|
74
92
|
end
|
75
93
|
@successful.each { |_, temp| temp.unlink }
|
76
94
|
{
|
data/lib/s3_zipper/client.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aws-sdk"
|
2
4
|
|
3
5
|
class S3Zipper
|
4
6
|
class Client
|
@@ -42,18 +44,26 @@ class S3Zipper
|
|
42
44
|
temp.binmode
|
43
45
|
temp = download_to_file key, temp
|
44
46
|
return if temp.nil?
|
47
|
+
|
45
48
|
yield(temp) if block_given?
|
46
49
|
temp
|
47
50
|
ensure
|
48
51
|
temp&.unlink if cleanup
|
49
52
|
end
|
50
53
|
|
51
|
-
def get_url
|
54
|
+
def get_url key
|
52
55
|
resource.bucket(bucket_name).object(key).public_url
|
53
56
|
end
|
54
57
|
|
55
58
|
def upload local_path, repo_path, options: {}
|
56
|
-
|
59
|
+
spinner = Spinner.new(
|
60
|
+
enabled: options[:progress],
|
61
|
+
title: "Uploading zip to #{bucket_name}/#{repo_path}",
|
62
|
+
)
|
63
|
+
spinner.start
|
64
|
+
object = client.put_object(options.merge!(bucket: bucket_name, key: repo_path, body: File.open(local_path).read))
|
65
|
+
spinner.finish title: "Uploaded zip to #{bucket_name}/#{repo_path}"
|
66
|
+
object
|
57
67
|
end
|
58
68
|
end
|
59
69
|
end
|
data/lib/s3_zipper/progress.rb
CHANGED
@@ -1,38 +1,61 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ruby-progressbar"
|
2
4
|
class Progress
|
5
|
+
|
3
6
|
def initialize options = {}
|
4
7
|
return unless options[:enabled] || true
|
8
|
+
|
5
9
|
@options = options
|
6
10
|
@format = options[:format]
|
7
11
|
@progress_bar = ProgressBar.create(@options)
|
8
12
|
end
|
9
13
|
|
10
|
-
def reset
|
14
|
+
def reset title: nil, total: nil, format: nil
|
15
|
+
return unless @progress_bar
|
16
|
+
|
11
17
|
@progress_bar.progress = 0
|
12
|
-
@progress_bar.title = title
|
13
|
-
@progress_bar.total = total
|
14
|
-
@progress_bar.format = format
|
18
|
+
@progress_bar.title = title if title
|
19
|
+
@progress_bar.total = total if total
|
20
|
+
@progress_bar.format = format if format
|
15
21
|
refresh
|
16
22
|
end
|
17
23
|
|
24
|
+
def spin
|
25
|
+
until @progress_bar.finished?
|
26
|
+
increment
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
18
30
|
def total
|
19
|
-
@progress_bar
|
31
|
+
return unless @progress_bar
|
32
|
+
|
33
|
+
@progress_bar.total
|
20
34
|
end
|
21
35
|
|
22
36
|
def percentage
|
23
|
-
@progress_bar
|
37
|
+
return unless @progress_bar
|
38
|
+
|
39
|
+
@progress_bar.to_h["percentage"]
|
24
40
|
end
|
25
41
|
|
26
42
|
def refresh
|
27
|
-
@progress_bar
|
43
|
+
return unless @progress_bar
|
44
|
+
|
45
|
+
@progress_bar.refresh
|
28
46
|
end
|
29
47
|
|
30
48
|
def progress
|
31
|
-
@progress_bar
|
49
|
+
return unless @progress_bar
|
50
|
+
|
51
|
+
@progress_bar.progress
|
32
52
|
end
|
33
53
|
|
34
|
-
def increment
|
35
|
-
@progress_bar
|
54
|
+
def increment attrs = {}
|
55
|
+
return unless @progress_bar
|
56
|
+
|
57
|
+
@progress_bar.increment
|
58
|
+
update_attrs(attrs) unless attrs.empty?
|
36
59
|
end
|
37
60
|
|
38
61
|
def update_attrs attrs
|
@@ -41,11 +64,13 @@ class Progress
|
|
41
64
|
|
42
65
|
def update attr, value
|
43
66
|
return unless @progress_bar
|
67
|
+
|
44
68
|
@progress_bar.send("#{attr}=", value)
|
45
69
|
end
|
46
70
|
|
47
71
|
def finish title: nil, format: nil
|
48
72
|
return unless @progress_bar
|
73
|
+
|
49
74
|
@progress_bar.title = title if title
|
50
75
|
@progress_bar.format = format if format
|
51
76
|
@progress_bar.finish
|
@@ -57,6 +82,7 @@ class Progress
|
|
57
82
|
|
58
83
|
def get_attr attr
|
59
84
|
return unless @progress_bar
|
85
|
+
|
60
86
|
@progress_bar.send(attr)
|
61
87
|
end
|
62
|
-
end
|
88
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ruby-progressbar"
|
4
|
+
require "concurrent-ruby"
|
5
|
+
class S3Zipper
|
6
|
+
class Spinner
|
7
|
+
include Concurrent::Async
|
8
|
+
|
9
|
+
def initialize title: "", enabled: true, steps: %w[▸▹▹▹▹▹ ▹▸▹▹▹▹ ▹▹▸▹▹▹ ▹▹▹▸▹▹ ▹▹▹▹▸▹ ▹▹▹▹▹▸ ▹▹▹▹▹]
|
10
|
+
return unless enabled || true
|
11
|
+
|
12
|
+
@progress_bar = ProgressBar.create(
|
13
|
+
format: "[%B] %t",
|
14
|
+
total: nil,
|
15
|
+
length: 100,
|
16
|
+
title: title,
|
17
|
+
autofinish: false,
|
18
|
+
unknown_progress_animation_steps: steps,
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def reset title: nil, total: nil, format: nil
|
23
|
+
return unless @progress_bar
|
24
|
+
|
25
|
+
@progress_bar.progress = 0
|
26
|
+
@progress_bar.title = title if title
|
27
|
+
@progress_bar.total = total if total
|
28
|
+
@progress_bar.format = format if format
|
29
|
+
refresh
|
30
|
+
end
|
31
|
+
|
32
|
+
def start
|
33
|
+
async.spin
|
34
|
+
end
|
35
|
+
|
36
|
+
def spin
|
37
|
+
return unless @progress_bar
|
38
|
+
until @progress_bar.finished?
|
39
|
+
increment
|
40
|
+
sleep(2)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def increment
|
45
|
+
return unless @progress_bar
|
46
|
+
|
47
|
+
@progress_bar.increment
|
48
|
+
end
|
49
|
+
|
50
|
+
def finish title: nil
|
51
|
+
return unless @progress_bar
|
52
|
+
|
53
|
+
@progress_bar.title = title if title
|
54
|
+
@progress_bar.format = "[✔] %t"
|
55
|
+
@progress_bar.finish
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|