bitferry 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3cf2cfbd34a736bdab0ca41d188a732b5e848be7a746ae9d351424e305ac59d
4
- data.tar.gz: 42c91f3371a2c61afdc813870550d19341b2b2e0e8096b5ec2b41adb05fd0670
3
+ metadata.gz: 17d40f40cac1390daae5cb6500acddf6f47bc83cc99b089530b7babf76adfb67
4
+ data.tar.gz: 4a5652c86fc09e7b6a23fe1bf53b8ffd891ca544782fe73df9eeb4472cdd4495
5
5
  SHA512:
6
- metadata.gz: 3c4f17e69351732f951a411bfc2f62f5052838abfa81d79655e9f6e5b9ee7826dc98a0b40005437d613155071fb71d028dcdddfb7bdcf8e29bd3e6a0ce022bcc
7
- data.tar.gz: 64e48fa1d13e51f28c598ff57022d9d300b901a9db2b98af0b446bcfe540d6dd218e8da8d1b08a4ec7b2700af63b6e1d693d340685d49674f69b4004a30fd6d6
6
+ metadata.gz: 95f3e41305bd871b1ca3ce8b8d5172c54006e8b3ca4125ac60dd36a1d4f9b816853f2f30454c7269ef084c596c520c491c0f9202169ef9e2cf34f82a06f3d33f
7
+ data.tar.gz: 0cc18872c913a6583d72d487191c26bac65f35f09fa051005b6fdc39293eb8d428a151e9643486bd9bb241dbd2cf705081345638c274ce49a2f13ebddd26daed
data/CHANGES.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.0.4
2
+
3
+ - Include/exclude path filters
4
+ - Include user's home in the Bitferry volume lookup list
5
+
1
6
  ## 0.0.3
2
7
 
3
8
  - Windows bundle
data/README.md CHANGED
@@ -6,7 +6,7 @@ The [Bitferry](https://github.com/okhlybov/bitferry) is aimed at establishing th
6
6
 
7
7
  The intended usage ranges from maintaining simple directory copy to another location (disk, mount point) to complex many-to-many (online/offline) data replication/backup solution employing portable media as additional data storage and a means of data propagation between the offsites.
8
8
 
9
- The core idea that drives Bitferry is the conversion of full (absolute) endpoint's paths into the volume-relative ones, where the volume is a data file which is put along the endpoint's data and denotes the root of the directory hierarchy. This leads to the important location independence property meaning that Bitferry is then able to restore the tasks' source-destination endpoint connections in spite of the volume location changes, which is a likely scenario in case of portable storage (different UNIX mount points, Windows drives etc.).
9
+ The core idea that drives Bitferry is the conversion of full (absolute) endpoints' paths into the volume-relative ones, where the volume is a data file which is put along the endpoint's data and denotes the root of the directory hierarchy. This makes data position-independent which means that Bitferry is then able to restore the tasks' source-destination endpoint connections in spite of the volume location changes, which is a likely scenario in case of portable storage (different UNIX mount points, Windows drives etc.).
10
10
 
11
11
  Bitferry is effectively a frontend to the [Rclone](https://rclone.org) and [Restic](https://restic.net) utilities.
12
12
 
data/lib/bitferry/cli.rb CHANGED
@@ -22,6 +22,31 @@ Encryption = %{
22
22
  }
23
23
 
24
24
 
25
+ $process = nil
26
+ $encryption = nil
27
+ $include = []
28
+ $exclude = []
29
+
30
+
31
+ def ext_globs(exts) = exts.split(',').collect { |ext| "*.#{ext}" }
32
+
33
+
34
+ def setup_task(x, include: true)
35
+ x.option ['-i'], 'EXTS', 'Include file extensions (comma-separated list)', multivalued: true, attribute_name: :include_exts do |exts|
36
+ $include << ext_globs(exts)
37
+ end if include
38
+ x.option ['-x'], 'EXTS', 'Exclude file extensions (comma-separated list)', multivalued: true, attribute_name: :exclude_exts do |exts|
39
+ $exclude << ext_globs(exts)
40
+ end
41
+ x.option ['--include'], 'GLOBS', 'Include path specifications (comma-separated list)', multivalued: true, attribute_name: :include do |globs|
42
+ $include << globs.split(',')
43
+ end if include
44
+ x.option ['--exclude'], 'GLOBS', 'Exclude path specifications (comma-separated list)', multivalued: true, attribute_name: :exclude do |globs|
45
+ $exclude << globs.split(',')
46
+ end
47
+ end
48
+
49
+
25
50
  def setup_rclone_task(x)
26
51
  x.parameter 'SOURCE', 'Source endpoint specifier'
27
52
  x.parameter 'DESTINATION', 'Destination endpoint specifier'
@@ -33,7 +58,7 @@ def setup_rclone_task(x)
33
58
  $encryption = Bitferry::Rclone::Decrypt
34
59
  $profile = :default
35
60
  end
36
- x.option '-x', :flag, 'Use extended encryption profile options (applies to -e, -d)', attribute_name: :x do
61
+ x.option '-u', :flag, 'Apply extended (unicode) encryption profile options (alias for -E extended / -D extended)', attribute_name: :u do
37
62
  $extended = true
38
63
  end
39
64
  x.option ['--process', '-X'], 'OPTIONS', 'Extra task processing profile/options' do |opts|
@@ -47,6 +72,7 @@ def setup_rclone_task(x)
47
72
  $encryption = Bitferry::Rclone::Decrypt
48
73
  $profile = opts
49
74
  end
75
+ setup_task(x)
50
76
  end
51
77
 
52
78
 
@@ -54,6 +80,7 @@ def create_rclone_task(task, *args, **opts)
54
80
  task.new(*args,
55
81
  process: $process,
56
82
  encryption: $encryption&.new(obtain_password, process: $extended ? :extended : $profile),
83
+ include: $include.flatten.uniq, exclude: $exclude.flatten.uniq,
57
84
  **opts
58
85
  )
59
86
  end
@@ -251,13 +278,14 @@ Clamp do
251
278
  }
252
279
  option '--force', :flag, 'Force overwriting existing repository' do $format = true end
253
280
  option ['--attach', '-a'], :flag, 'Attach to existing repository' do $format = false end
254
- option '-f', :flag, 'Rig for application of the snapshot retention policy (alias for -F default)', attribute_name: :f do $forget = :default end
255
- option '-c', :flag, 'Rig for repository checking (alias for -C default)', attribute_name: :c do $check = :default end
281
+ option '-f', :flag, 'Apply default snapshot retention policy options (alias for -F default)', attribute_name: :f do $forget = :default end
282
+ option '-c', :flag, 'Apply default repository checking options (alias for -C default)', attribute_name: :c do $check = :default end
256
283
  option ['--process', '-X'], 'OPTIONS', 'Extra task processing profile/options' do |opts| $process = opts end
257
- option ['--forget', '-F'], 'OPTIONS', 'Rig for snapshot retention policy with profile/options' do |opts| $forget = opts end
258
- option ['--check', '-C'], 'OPTIONS', 'Rig for repository checking with profile/options' do |opts| $check = opts end
284
+ option ['--forget', '-F'], 'OPTIONS', 'Snapshot retention policy with profile/options' do |opts| $forget = opts end
285
+ option ['--check', '-C'], 'OPTIONS', 'Repository checking with profile/options' do |opts| $check = opts end
259
286
  parameter 'SOURCE', 'Source endpoint specifier'
260
287
  parameter 'REPOSITORY', 'Destination repository endpoint specifier'
288
+ setup_task(self, include: false)
261
289
  def execute
262
290
  bitferry {
263
291
  Bitferry::Restic::Backup.new(
@@ -265,7 +293,8 @@ Clamp do
265
293
  format: $format,
266
294
  process: $process,
267
295
  check: $check,
268
- forget: $forget
296
+ forget: $forget,
297
+ exclude: $exclude.flatten.uniq
269
298
  )
270
299
  }
271
300
  end
@@ -280,11 +309,13 @@ Clamp do
280
309
  option ['--process', '-X'], 'OPTIONS', 'Extra task processing profile/options' do |opts| $process = opts end
281
310
  parameter 'REPOSITORY', 'Source repository endpoint specifier'
282
311
  parameter 'DESTINATION', 'Destination endpoint specifier'
312
+ setup_task(self)
283
313
  def execute
284
314
  bitferry {
285
315
  Bitferry::Restic::Restore.new(
286
316
  destination, repository, obtain_password,
287
317
  process: $process,
318
+ include: $include.flatten.uniq, exclude: $exclude.flatten.uniq
288
319
  )
289
320
  }
290
321
  end
data/lib/bitferry.rb CHANGED
@@ -12,7 +12,7 @@ require 'shellwords'
12
12
  module Bitferry
13
13
 
14
14
 
15
- VERSION = '0.0.3'
15
+ VERSION = '0.0.4'
16
16
 
17
17
 
18
18
  module Logging
@@ -39,7 +39,7 @@ module Bitferry
39
39
  reset
40
40
  log.info('restoring volumes')
41
41
  result = true
42
- roots = (environment_mounts + system_mounts).uniq
42
+ roots = (environment_mounts + system_mounts + [Dir.home]).uniq
43
43
  log.info("distilled volume search path: #{roots.join(', ')}")
44
44
  roots.each do |root|
45
45
  if File.exist?(File.join(root, Volume::STORAGE))
@@ -498,6 +498,9 @@ module Bitferry
498
498
  attr_reader :modified
499
499
 
500
500
 
501
+ attr_reader :include, :exclude
502
+
503
+
501
504
  def process_options = @process_options.nil? ? [] : @process_options # As a mandatory option it should never be nil
502
505
 
503
506
 
@@ -530,10 +533,12 @@ module Bitferry
530
533
  end
531
534
 
532
535
 
533
- def initialize(tag: Bitferry.tag, modified: DateTime.now)
536
+ def initialize(tag: Bitferry.tag, modified: DateTime.now, include: [], exclude: [])
534
537
  @tag = tag
535
538
  @generation = 0
536
- @modified = modified
539
+ @include = include
540
+ @exclude = exclude
541
+ @modified = modified.is_a?(DateTime) ? modified : DateTime.parse(modified)
537
542
  # FIXME handle process_options at this level
538
543
  end
539
544
 
@@ -546,6 +551,8 @@ module Bitferry
546
551
 
547
552
 
548
553
  def restore(hash)
554
+ @include = hash.fetch(:include, [])
555
+ @exclude = hash.fetch(:exclude, [])
549
556
  @state = :intact
550
557
  log.info("restored task #{tag}")
551
558
  end
@@ -558,7 +565,9 @@ module Bitferry
558
565
  def externalize
559
566
  {
560
567
  task: tag,
561
- modified: @modified
568
+ modified: modified,
569
+ include: include.empty? ? nil : include,
570
+ exclude: exclude.empty? ? nil : exclude
562
571
  }.compact
563
572
  end
564
573
 
@@ -584,6 +593,14 @@ module Bitferry
584
593
  end
585
594
 
586
595
 
596
+ def show_filters
597
+ xs = []
598
+ xs << 'include: ' + include.join(',') unless include.empty?
599
+ xs << 'exclude: ' + exclude.join(',') unless exclude.empty?
600
+ xs.join(' ').to_s
601
+ end
602
+
603
+
587
604
  def self.[](tag) = @@registry[tag]
588
605
 
589
606
 
@@ -609,8 +626,16 @@ module Bitferry
609
626
  def self.reset = @@registry = {}
610
627
 
611
628
 
612
- def self.register(task) = @@registry[task.tag] = task # TODO settle on task with the latest timestamp
613
-
629
+ def self.register(task)
630
+ # Task with newer timestamp replaces already registered task, if any
631
+ if (xtag = @@registry[task.tag]).nil?
632
+ @@registry[task.tag] = task
633
+ elsif xtag.modified < task.modified
634
+ @@registry[task.tag] = task
635
+ else
636
+ xtag
637
+ end
638
+ end
614
639
 
615
640
  def self.intact = live.filter { |task| task.intact? }
616
641
 
@@ -795,7 +820,7 @@ module Bitferry
795
820
  end
796
821
 
797
822
 
798
- def show_status = "#{show_operation} #{source.show_status} #{show_direction} #{destination.show_status}"
823
+ def show_status = "#{show_operation} #{source.show_status} #{show_direction} #{destination.show_status} #{show_filters}"
799
824
 
800
825
 
801
826
  def show_operation = encryption.nil? ? '' : encryption.show_operation
@@ -833,8 +858,14 @@ module Bitferry
833
858
  end
834
859
 
835
860
 
861
+ def include_filters = include.collect { |x| ['--filter', "+ #{x}"]}.flatten
862
+
863
+
864
+ def exclude_filters = ([Volume::STORAGE, Volume::STORAGE_] + exclude).collect { |x| ['--filter', "- #{x}"]}.flatten
865
+
866
+
836
867
  def process_arguments
837
- ['--filter', "- #{Volume::STORAGE}", '--filter', "- #{Volume::STORAGE_}"] + common_options + process_options + (
868
+ include_filters + exclude_filters + common_options + process_options + (
838
869
  encryption.nil? ? [source.root.to_s, destination.root.to_s] : encryption.arguments(self)
839
870
  )
840
871
  end
@@ -846,7 +877,7 @@ module Bitferry
846
877
  puts cms if Bitferry.verbosity == :verbose
847
878
  log.info(cms)
848
879
  status = Open3.pipeline(cmd).first
849
- raise "rclone exit code #{status.exitstatus}" unless status.success?
880
+ raise RuntimeError, "rclone exit code #{status.exitstatus}" unless status.success?
850
881
  status.success?
851
882
  end
852
883
 
@@ -1011,6 +1042,9 @@ module Bitferry
1011
1042
  def format = nil
1012
1043
 
1013
1044
 
1045
+ def include_filters = include.collect { |x| ['--include', x]}.flatten
1046
+
1047
+
1014
1048
  def common_options
1015
1049
  [
1016
1050
  case Bitferry.verbosity
@@ -1037,7 +1071,8 @@ module Bitferry
1037
1071
  begin
1038
1072
  Dir.chdir(chdir) unless chdir.nil?
1039
1073
  status = Open3.pipeline(cmd).first
1040
- raise "restic exit code #{status.exitstatus}" unless status.success?
1074
+ raise RuntimeError, "restic exit code #{status.exitstatus}" unless status.success?
1075
+ status.success?
1041
1076
  ensure
1042
1077
  Dir.chdir(wd) unless chdir.nil?
1043
1078
  end
@@ -1077,7 +1112,7 @@ module Bitferry
1077
1112
 
1078
1113
 
1079
1114
  FORGET = {
1080
- default: ['--prune', '--keep-within-hourly', '24h', '--keep-within-daily', '7d', '--keep-within-weekly', '30d', '--keep-within-monthly', '1y', '--keep-within-yearly', '100y']
1115
+ default: ['--prune', '--no-cache', '--keep-within-hourly', '24h', '--keep-within-daily', '7d', '--keep-within-weekly', '30d', '--keep-within-monthly', '1y', '--keep-within-yearly', '100y']
1081
1116
  }
1082
1117
  FORGET[nil] = nil # Skip processing retention policy by default
1083
1118
 
@@ -1102,7 +1137,10 @@ module Bitferry
1102
1137
  end
1103
1138
 
1104
1139
 
1105
- def show_status = "#{show_operation} #{directory.show_status} #{show_direction} #{repository.show_status}"
1140
+ def exclude_filters = ([Volume::STORAGE, Volume::STORAGE_] + exclude).collect { |x| ['--exclude', x]}.flatten
1141
+
1142
+
1143
+ def show_status = "#{show_operation} #{directory.show_status} #{show_direction} #{repository.show_status} #{show_filters}"
1106
1144
 
1107
1145
 
1108
1146
  def show_operation = 'encrypt+backup'
@@ -1114,7 +1152,7 @@ module Bitferry
1114
1152
  def process
1115
1153
  begin
1116
1154
  log.info("processing task #{tag}")
1117
- execute('backup', '.', '--tag', "bitferry,#{tag}", '--exclude', Volume::STORAGE, '--exclude', Volume::STORAGE_, *process_options, *common_options_simulate, chdir: directory.root)
1155
+ execute('backup', '.', '--tag', "bitferry,#{tag}", *exclude_filters, *process_options, *common_options_simulate, chdir: directory.root)
1118
1156
  unless check_options.nil?
1119
1157
  log.info("checking repository in #{repository.root}")
1120
1158
  execute('check', *check_options, *common_options)
@@ -1199,7 +1237,10 @@ module Bitferry
1199
1237
  end
1200
1238
 
1201
1239
 
1202
- def show_status = "#{show_operation} #{repository.show_status} #{show_direction} #{directory.show_status}"
1240
+ def exclude_filters = exclude.collect { |x| ['--exclude', x]}.flatten
1241
+
1242
+
1243
+ def show_status = "#{show_operation} #{repository.show_status} #{show_direction} #{directory.show_status} #{show_filters}"
1203
1244
 
1204
1245
 
1205
1246
  def show_operation = 'decrypt+restore'
@@ -1230,7 +1271,7 @@ module Bitferry
1230
1271
  log.info("processing task #{tag}")
1231
1272
  begin
1232
1273
  # FIXME restore specifically tagged latest snapshot
1233
- execute('restore', 'latest', '--target', '.', *process_options, *common_options, simulate: Bitferry.simulate?, chdir: directory.root)
1274
+ execute('restore', 'latest', '--target', directory.root.to_s, *include_filters, *exclude_filters, *process_options, *common_options, simulate: Bitferry.simulate?)
1234
1275
  true
1235
1276
  rescue
1236
1277
  false
@@ -1326,7 +1367,7 @@ module Bitferry
1326
1367
 
1327
1368
  def restore(hash)
1328
1369
  @volume_tag = hash.fetch(:volume)
1329
- @path = Pathname.new(hash.fetch(:path))
1370
+ @path = Pathname.new(hash.fetch(:path, ''))
1330
1371
  end
1331
1372
 
1332
1373
 
@@ -1334,8 +1375,8 @@ module Bitferry
1334
1375
  {
1335
1376
  endpoint: :bitferry,
1336
1377
  volume: volume_tag,
1337
- path: path
1338
- }
1378
+ path: path.to_s.empty? ? nil : path
1379
+ }.compact
1339
1380
  end
1340
1381
 
1341
1382
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitferry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleg A. Khlybov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-07 00:00:00.000000000 Z
11
+ date: 2024-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake