riserva 0.1.2 → 0.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: db30408186529f3b54b2351fcfda5f20474f78a3
4
- data.tar.gz: da8cc2d981e0ea05801ad07a9fa42eae79a50e4b
2
+ SHA256:
3
+ metadata.gz: 9d9c1baf7002404750e398f00ffd3c65f66aac5f9405de22a4976bfdc8d6d44e
4
+ data.tar.gz: b93e96cebf3e341dc1b3f40e47efeadc0ec81931ab30774d3da3ade4bed4c20c
5
5
  SHA512:
6
- metadata.gz: 818540fa6bb016d026122b544387c60d2060e52f598d5488735fb2fe88a75d2726fbd5aabf6141635323c1d9251727eee1a21335f7a16d0b153a567321b68288
7
- data.tar.gz: de7b3add993e1329095c3b39eadb3c220c0f632d4d945a6981fe82449fb8a91e916642080958bbad5440fc902f63b24b60aa1a65a5f72718231958872e07c9d2
6
+ metadata.gz: a1def65971f0715dde4fc92b4cede1e995a59631f555889093e62e303d7c26accc7735b05ffc9c4d018113478da61e9ebdf09981fd23dccc045982bde2a68de7
7
+ data.tar.gz: 894fc37b635f4deb25bad08b50db64420c6fb470b76c80d5e9225832c05d2f04fa73371f92a47fc4dd468d4a6d1589b9ae95dfab823d377b65a6be6556b7f492
data/.gitignore CHANGED
File without changes
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
+ --require spec_helper
1
2
  --format documentation
2
3
  --color
File without changes
File without changes
data/Gemfile CHANGED
File without changes
File without changes
data/README.md CHANGED
File without changes
data/Rakefile CHANGED
File without changes
data/TODO.md CHANGED
@@ -1,5 +1,6 @@
1
1
  ## TODO
2
2
 
3
- * delete old files
3
+ * encrypt backups
4
4
  * restore from backup
5
+ * implement deduplication
5
6
  * create archives in `/tmp` folder if location is not specified
@@ -10,8 +10,10 @@ class RiservaExecutable
10
10
 
11
11
  def call
12
12
  case Choice[:perform]
13
- when 'backup' then
13
+ when 'backup'
14
14
  Riserva::Commands::Backup.new.call
15
+ when 'clean'
16
+ Riserva::Commands::Clean.new.call
15
17
  end
16
18
  end
17
19
  end
@@ -8,6 +8,8 @@ folders:
8
8
  storage:
9
9
  google_drive:
10
10
  secrets: 'config/google_drive_secrets.json'
11
+ days_to_keep: 30
11
12
 
12
13
  dropbox:
13
14
  secrets: 'config/dropbox_secrets.json'
15
+ days_to_keep: 30
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'riserva/version'
4
4
  require 'active_support/core_ext/string/inflections'
5
+ require 'active_support/time'
5
6
  require 'pathname'
7
+ require 'parallel'
6
8
 
7
9
  module Riserva
8
10
  autoload :Config, 'riserva/config'
@@ -11,6 +13,7 @@ module Riserva
11
13
  module Commands
12
14
  autoload :ApplicationCommand, 'riserva/commands/application_command'
13
15
  autoload :Backup, 'riserva/commands/backup'
16
+ autoload :Clean, 'riserva/commands/clean'
14
17
  autoload :CreateArchive, 'riserva/commands/create_archive'
15
18
  autoload :UploadFile, 'riserva/commands/upload_file'
16
19
  end
@@ -18,6 +21,7 @@ module Riserva
18
21
  module Listeners
19
22
  autoload :ApplicationListener, 'riserva/listeners/application_listener'
20
23
  autoload :Backup, 'riserva/listeners/backup'
24
+ autoload :Clean, 'riserva/listeners/clean'
21
25
  autoload :CreateArchive, 'riserva/listeners/create_archive'
22
26
  autoload :UploadFile, 'riserva/listeners/upload_file'
23
27
  end
@@ -31,4 +35,8 @@ module Riserva
31
35
  def self.logger
32
36
  Riserva::Log.new.logger
33
37
  end
38
+
39
+ def self.version
40
+ "Riserva v#{Riserva::VERSION}"
41
+ end
34
42
  end
@@ -4,8 +4,8 @@ Choice.options do
4
4
  option :perform do
5
5
  short '-p'
6
6
  long '--perform=OPERATION'
7
- desc 'Operation to perform, valid options: backup (default)'
8
- valid %w[backup]
7
+ desc 'Operation to perform, valid options: backup (default), clean'
8
+ valid %w[backup clean]
9
9
  default 'backup'
10
10
  end
11
11
 
@@ -31,7 +31,7 @@ Choice.options do
31
31
  long '--version'
32
32
  desc 'Show version'
33
33
  action do
34
- puts "Riserva backup tool v#{Riserva::VERSION}"
34
+ puts Riserva.version
35
35
  exit
36
36
  end
37
37
  end
@@ -1,46 +1,44 @@
1
1
  module Riserva::Commands
2
2
  class Backup < ApplicationCommand
3
- attr_reader :uploaders
3
+ MAX_THREADS = 5
4
4
 
5
5
  def initialize
6
6
  super
7
- @uploaders = {}
8
7
  end
9
8
 
10
9
  def call
11
- create_archives
12
- push_to_cloud
10
+ perform
13
11
 
14
- success? ? broadcast(:ok) : broadcast(:failed)
12
+ broadcast(:ok)
13
+ rescue StandardError => e
14
+ broadcast(:failed)
15
+ raise e
15
16
  end
16
17
 
17
18
  private
18
19
 
19
- def success?
20
- @uploaders.values.map { |uploader| uploader.files == archivator.files }.all?
21
- end
22
-
23
- def create_archives
24
- Riserva::Config.folders { |folder| archivator.call(folder) }
20
+ def perform
21
+ Riserva::Config.folders.each do |folder|
22
+ push_to_cloud archivator.call(folder).files
23
+ end
25
24
  end
26
25
 
27
26
  def archivator
28
27
  @archivator ||= Riserva::Commands::CreateArchive.new
29
28
  end
30
29
 
31
- def push_to_cloud
32
- Riserva::Config.storages do |storage|
33
- archivator.files.each { |file| upload_file(storage, file) }
30
+ def push_to_cloud(files)
31
+ Parallel.map(Riserva::Config.storages, in_threads: MAX_THREADS) do |storage|
32
+ files.map { |file| upload_file(storage, file) }
34
33
  end
35
34
  end
36
35
 
37
36
  def upload_file(storage, file)
38
- Riserva.logger.info("Uploading file #{file} to #{storage.title}...")
39
-
40
37
  uploader(storage).call(file)
41
38
  end
42
39
 
43
40
  def uploader(storage)
41
+ @uploaders ||= {}
44
42
  @uploaders[storage.title] ||= Riserva::Commands::UploadFile.new(storage)
45
43
  end
46
44
  end
@@ -0,0 +1,21 @@
1
+ module Riserva::Commands
2
+ class Clean < ApplicationCommand
3
+ def call
4
+ storages.each do |storage|
5
+ broadcast(:start_cleaning, storage)
6
+
7
+ if storage.clean
8
+ broadcast(:ok)
9
+ else
10
+ broadcast(:failed, storage)
11
+ end
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def storages
18
+ Riserva::Config.storages
19
+ end
20
+ end
21
+ end
@@ -1,7 +1,8 @@
1
1
  module Riserva::Commands
2
2
  class CreateArchive < ApplicationCommand
3
3
  def call(path)
4
- return broadcast(:invalid) unless super
4
+ broadcast(:start, "folder: #{path}")
5
+ return broadcast(:invalid, "`#{@path}` is not a directory") unless super
5
6
 
6
7
  file = archive_name
7
8
  create_archive(file) ? broadcast(:ok, file) : broadcast(:failed)
@@ -6,9 +6,10 @@ module Riserva::Commands
6
6
  end
7
7
 
8
8
  def call(path)
9
+ broadcast(:start, "file: #{path} to #{@storage.title}...")
9
10
  return broadcast(:invalid) unless super
10
11
 
11
- success? ? broadcast(:ok, @path) : broadcast(:failed, @path)
12
+ success? ? broadcast(:ok, @path, @storage.title) : broadcast(:failed, @path)
12
13
  end
13
14
 
14
15
  private
@@ -1,14 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'safe_yaml'
3
+ require 'yaml'
4
4
 
5
5
  module Riserva
6
6
  class Config
7
7
  DEFAULT_PATH = 'config/riserva.yml'
8
8
 
9
9
  def initialize
10
- SafeYAML::OPTIONS[:default_mode] = :safe
11
-
12
10
  @path = ENV['RISERVA_CONFIG'] || DEFAULT_PATH
13
11
  end
14
12
 
@@ -22,14 +20,14 @@ module Riserva
22
20
  end
23
21
 
24
22
  def self.folders
25
- read('folders').each { |folder| yield Pathname.new(folder) }
23
+ read('folders').map { |folder| Pathname.new(folder) }
26
24
  end
27
25
 
28
26
  def self.storages
29
- read('storage').keys.each do |storage|
27
+ read('storage').keys.map do |storage|
30
28
  klass = "Riserva::Storage::#{storage.camelize}"
31
- yield(klass.safe_constantize.new) unless klass.nil?
32
- end
29
+ klass.safe_constantize&.new
30
+ end.compact
33
31
  end
34
32
 
35
33
  private
@@ -8,20 +8,31 @@ module Riserva::Listeners
8
8
  @files = Set.new
9
9
  end
10
10
 
11
- def ok(file)
11
+ def start(*args)
12
+ Riserva.logger.info(progname) { ['Starting', *args].join(': ') }
13
+ end
14
+
15
+ def ok(file, *args)
12
16
  @files << Pathname.new(file)
13
17
 
14
- Riserva.logger.info(progname) { "OK: #{file}" }
18
+ Riserva.logger.info(progname) { ["OK: #{file}", *args].join(': ') }
15
19
  end
16
20
 
17
- def failed
18
- Riserva.logger.error(progname) { 'Failed' }
21
+ def invalid(message = nil)
22
+ Riserva.logger.debug(progname) { ['Invalid', message].compact.join(': ') }
23
+ end
24
+
25
+ def failed(file = nil)
26
+ Riserva.logger.error(progname) { ['Failed', file].compact.join(': ') }
19
27
  end
20
28
 
21
29
  protected
22
30
 
23
31
  def progname
24
- self.class.name.split('::').last
32
+ command = self.class.name.split('::').last
33
+ worker = Parallel.worker_number.presence
34
+
35
+ [command, worker].compact.join(' ')
25
36
  end
26
37
  end
27
38
  end
@@ -6,7 +6,7 @@ module Riserva::Listeners
6
6
  class Backup < ApplicationListener
7
7
  def initialize
8
8
  notify('Starting backup...')
9
- Riserva.logger.info('Starting backup...')
9
+ Riserva.logger.info(Riserva.version) { 'Starting backup...' }
10
10
  end
11
11
 
12
12
  def ok
@@ -0,0 +1,19 @@
1
+ module Riserva::Listeners
2
+ class Clean < ApplicationListener
3
+ def initialize
4
+ Riserva.logger.info('Starting cleanup...')
5
+ end
6
+
7
+ def ok
8
+ Riserva.logger.info('OK')
9
+ end
10
+
11
+ def failed(storage)
12
+ Riserva.logger.info("Failed: #{storage.title}")
13
+ end
14
+
15
+ def start_cleaning(storage)
16
+ Riserva.logger.info("Deleting old files on #{storage.title}...")
17
+ end
18
+ end
19
+ end
File without changes
File without changes
File without changes
@@ -17,5 +17,12 @@ module Riserva::Storage
17
17
  def config_secrets
18
18
  Riserva::Config.read([:storage, title, :secrets].join('.'))
19
19
  end
20
+
21
+ def time_to_keep
22
+ value = Riserva::Config.read([:storage, title, :days_to_keep].join('.'))
23
+ return unless value
24
+
25
+ value.days
26
+ end
20
27
  end
21
28
  end
@@ -26,6 +26,16 @@ module Riserva::Storage
26
26
  data.content_hash == checksum
27
27
  end
28
28
 
29
+ def clean
30
+ return unless time_to_keep
31
+
32
+ session.list_folder('').entries.each do |file|
33
+ next unless file.client_modified < time_to_keep.ago
34
+
35
+ session.delete file.path_lower
36
+ end
37
+ end
38
+
29
39
  private
30
40
 
31
41
  def session
@@ -20,6 +20,14 @@ module Riserva::Storage
20
20
  file.md5_checksum == checksum
21
21
  end
22
22
 
23
+ def clean
24
+ return unless time_to_keep
25
+
26
+ session.files(q: ['createdTime < ?', time_to_keep.ago]).each do |file|
27
+ file.delete(true)
28
+ end
29
+ end
30
+
23
31
  private
24
32
 
25
33
  def session
@@ -1,3 +1,3 @@
1
1
  module Riserva
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.7'
3
3
  end
@@ -21,10 +21,10 @@ Gem::Specification.new do |spec|
21
21
  spec.executables << 'riserva'
22
22
  spec.require_paths = ['lib']
23
23
 
24
- spec.add_development_dependency 'bundler', '~> 1.14'
25
- spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'bundler'
25
+ spec.add_development_dependency 'pry-byebug'
26
+ spec.add_development_dependency 'rake', '>= 12.3.3'
26
27
  spec.add_development_dependency 'rspec', '~> 3.0'
27
- spec.add_development_dependency 'guard-rspec', '~> 4.7', '>= 4.7.3'
28
28
  spec.add_development_dependency 'wisper-rspec', '~> 0.0.3'
29
29
 
30
30
  spec.add_runtime_dependency 'activesupport', '~> 4.0'
@@ -33,6 +33,6 @@ Gem::Specification.new do |spec|
33
33
  spec.add_runtime_dependency 'dropbox_content_hasher', '~> 0.1.0'
34
34
  spec.add_runtime_dependency 'google_drive', '~> 2.1', '>= 2.1.5'
35
35
  spec.add_runtime_dependency 'notifier', '~> 0.5.2'
36
- spec.add_runtime_dependency 'safe_yaml', '~> 1.0', '>= 1.0.4'
36
+ spec.add_runtime_dependency 'parallel', '~> 1.19', '>= 1.19.2'
37
37
  spec.add_runtime_dependency 'wisper', '~> 2.0'
38
38
  end
metadata CHANGED
@@ -1,77 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riserva
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Malinovskiy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-12 00:00:00.000000000 Z
11
+ date: 2020-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.14'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.14'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: pry-byebug
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
47
+ version: 12.3.3
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '3.0'
54
+ version: 12.3.3
55
55
  - !ruby/object:Gem::Dependency
56
- name: guard-rspec
56
+ name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '4.7'
62
- - - ">="
63
- - !ruby/object:Gem::Version
64
- version: 4.7.3
61
+ version: '3.0'
65
62
  type: :development
66
63
  prerelease: false
67
64
  version_requirements: !ruby/object:Gem::Requirement
68
65
  requirements:
69
66
  - - "~>"
70
67
  - !ruby/object:Gem::Version
71
- version: '4.7'
72
- - - ">="
73
- - !ruby/object:Gem::Version
74
- version: 4.7.3
68
+ version: '3.0'
75
69
  - !ruby/object:Gem::Dependency
76
70
  name: wisper-rspec
77
71
  requirement: !ruby/object:Gem::Requirement
@@ -177,25 +171,25 @@ dependencies:
177
171
  - !ruby/object:Gem::Version
178
172
  version: 0.5.2
179
173
  - !ruby/object:Gem::Dependency
180
- name: safe_yaml
174
+ name: parallel
181
175
  requirement: !ruby/object:Gem::Requirement
182
176
  requirements:
183
177
  - - "~>"
184
178
  - !ruby/object:Gem::Version
185
- version: '1.0'
179
+ version: '1.19'
186
180
  - - ">="
187
181
  - !ruby/object:Gem::Version
188
- version: 1.0.4
182
+ version: 1.19.2
189
183
  type: :runtime
190
184
  prerelease: false
191
185
  version_requirements: !ruby/object:Gem::Requirement
192
186
  requirements:
193
187
  - - "~>"
194
188
  - !ruby/object:Gem::Version
195
- version: '1.0'
189
+ version: '1.19'
196
190
  - - ">="
197
191
  - !ruby/object:Gem::Version
198
- version: 1.0.4
192
+ version: 1.19.2
199
193
  - !ruby/object:Gem::Dependency
200
194
  name: wisper
201
195
  requirement: !ruby/object:Gem::Requirement
@@ -223,7 +217,6 @@ files:
223
217
  - ".rubocop.yml"
224
218
  - ".travis.yml"
225
219
  - Gemfile
226
- - Guardfile
227
220
  - LICENSE.txt
228
221
  - README.md
229
222
  - Rakefile
@@ -238,11 +231,13 @@ files:
238
231
  - lib/riserva/command_line.rb
239
232
  - lib/riserva/commands/application_command.rb
240
233
  - lib/riserva/commands/backup.rb
234
+ - lib/riserva/commands/clean.rb
241
235
  - lib/riserva/commands/create_archive.rb
242
236
  - lib/riserva/commands/upload_file.rb
243
237
  - lib/riserva/config.rb
244
238
  - lib/riserva/listeners/application_listener.rb
245
239
  - lib/riserva/listeners/backup.rb
240
+ - lib/riserva/listeners/clean.rb
246
241
  - lib/riserva/listeners/create_archive.rb
247
242
  - lib/riserva/listeners/upload_file.rb
248
243
  - lib/riserva/log.rb
@@ -271,7 +266,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
271
266
  version: '0'
272
267
  requirements: []
273
268
  rubyforge_project:
274
- rubygems_version: 2.6.14.1
269
+ rubygems_version: 2.7.6.2
275
270
  signing_key:
276
271
  specification_version: 4
277
272
  summary: Backup files to cloud drive
data/Guardfile DELETED
@@ -1,70 +0,0 @@
1
- # A sample Guardfile
2
- # More info at https://github.com/guard/guard#readme
3
-
4
- ## Uncomment and set this to only include directories you want to watch
5
- # directories %w(app lib config test spec features) \
6
- # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
-
8
- ## Note: if you are using the `directories` clause above and you are not
9
- ## watching the project directory ('.'), then you will want to move
10
- ## the Guardfile to a watched dir and symlink it back, e.g.
11
- #
12
- # $ mkdir config
13
- # $ mv Guardfile config/
14
- # $ ln -s config/Guardfile .
15
- #
16
- # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
-
18
- # Note: The cmd option is now required due to the increasing number of ways
19
- # rspec may be run, below are examples of the most common uses.
20
- # * bundler: 'bundle exec rspec'
21
- # * bundler binstubs: 'bin/rspec'
22
- # * spring: 'bin/rspec' (This will use spring if running and you have
23
- # installed the spring binstubs per the docs)
24
- # * zeus: 'zeus rspec' (requires the server to be started separately)
25
- # * 'just' rspec: 'rspec'
26
-
27
- guard :rspec, cmd: "bundle exec rspec" do
28
- require "guard/rspec/dsl"
29
- dsl = Guard::RSpec::Dsl.new(self)
30
-
31
- # Feel free to open issues for suggestions and improvements
32
-
33
- # RSpec files
34
- rspec = dsl.rspec
35
- watch(rspec.spec_helper) { rspec.spec_dir }
36
- watch(rspec.spec_support) { rspec.spec_dir }
37
- watch(rspec.spec_files)
38
-
39
- # Ruby files
40
- ruby = dsl.ruby
41
- dsl.watch_spec_files_for(ruby.lib_files)
42
-
43
- # Rails files
44
- rails = dsl.rails(view_extensions: %w(erb haml slim))
45
- dsl.watch_spec_files_for(rails.app_files)
46
- dsl.watch_spec_files_for(rails.views)
47
-
48
- watch(rails.controllers) do |m|
49
- [
50
- rspec.spec.call("routing/#{m[1]}_routing"),
51
- rspec.spec.call("controllers/#{m[1]}_controller"),
52
- rspec.spec.call("acceptance/#{m[1]}")
53
- ]
54
- end
55
-
56
- # Rails config changes
57
- watch(rails.spec_helper) { rspec.spec_dir }
58
- watch(rails.routes) { "#{rspec.spec_dir}/routing" }
59
- watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
60
-
61
- # Capybara features specs
62
- watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
63
- watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
64
-
65
- # Turnip features and steps
66
- watch(%r{^spec/acceptance/(.+)\.feature$})
67
- watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
68
- Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
69
- end
70
- end