bitferry 0.0.6 → 0.0.8
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/CHANGES.md +39 -28
- data/README.md +231 -231
- data/bin/bitferry +2 -2
- data/bin/bitferryfx +2 -2
- data/lib/bitferry/cli.rb +376 -375
- data/lib/bitferry/fx.rb +131 -63
- data/lib/bitferry.rb +1502 -1424
- metadata +38 -14
data/lib/bitferry/cli.rb
CHANGED
@@ -1,375 +1,376 @@
|
|
1
|
-
require 'clamp'
|
2
|
-
require 'bitferry'
|
3
|
-
require 'io/console'
|
4
|
-
|
5
|
-
|
6
|
-
Endpoint = %{
|
7
|
-
The endpoint may be one of:
|
8
|
-
* directory -- absolute or relative local directory (/data, ../source, c:\\data)
|
9
|
-
* local:directory, :directory -- absolute local directory (:/data, local:c:\\data)
|
10
|
-
* :tag:directory -- path relative to the intact volume matched by (partial) tag (:fa2c:source/data)
|
11
|
-
|
12
|
-
The former case resolves specified directory againt an intact volume to make it volume-relative.
|
13
|
-
It is an error if there is no intact volume that encompasses specified directory.
|
14
|
-
The local: directory is left as is (not resolved against volumes).
|
15
|
-
The :tag: directory is bound to the specified volume.
|
16
|
-
}
|
17
|
-
|
18
|
-
|
19
|
-
Encryption = %{
|
20
|
-
The encryption mode is controlled by --encrypt or --decrypt options.
|
21
|
-
The mandatory password will be read from the standard input channel (pipe or keyboard).
|
22
|
-
}
|
23
|
-
|
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
|
-
|
50
|
-
def setup_rclone_task(x)
|
51
|
-
x.parameter 'SOURCE', 'Source endpoint specifier'
|
52
|
-
x.parameter 'DESTINATION', 'Destination endpoint specifier'
|
53
|
-
x.option '-e', :flag, 'Encrypt files in destination using default profile (alias for -E default)', attribute_name: :e do
|
54
|
-
$encryption = Bitferry::Rclone::Encrypt
|
55
|
-
$profile = :default
|
56
|
-
end
|
57
|
-
x.option '-d', :flag, 'Decrypt source files using default profile (alias for -D default)', attribute_name: :d do
|
58
|
-
$encryption = Bitferry::Rclone::Decrypt
|
59
|
-
$profile = :default
|
60
|
-
end
|
61
|
-
x.option '-u', :flag, 'Apply extended (unicode) encryption profile options (alias for -E extended / -D extended)', attribute_name: :u do
|
62
|
-
$extended = true
|
63
|
-
end
|
64
|
-
x.option ['--process', '-X'], 'OPTIONS', 'Extra task processing profile/options' do |opts|
|
65
|
-
$process = opts
|
66
|
-
end
|
67
|
-
x.option ['--encrypt', '-E'], 'OPTIONS', 'Encrypt files in destination using specified profile/options' do |opts|
|
68
|
-
$encryption = Bitferry::Rclone::Encrypt
|
69
|
-
$profile = opts
|
70
|
-
end
|
71
|
-
x.option ['--decrypt', '-D'], 'OPTIONS', 'Decrypt source files using specified profile/options' do |opts|
|
72
|
-
$encryption = Bitferry::Rclone::Decrypt
|
73
|
-
$profile = opts
|
74
|
-
end
|
75
|
-
setup_task(x)
|
76
|
-
end
|
77
|
-
|
78
|
-
|
79
|
-
def create_rclone_task(task, *args, **opts)
|
80
|
-
task.new(*args,
|
81
|
-
process: $process,
|
82
|
-
encryption: $encryption&.new(obtain_password, process: $extended ? :extended : $profile),
|
83
|
-
include: $include.flatten.uniq, exclude: $exclude.flatten.uniq,
|
84
|
-
**opts
|
85
|
-
)
|
86
|
-
end
|
87
|
-
|
88
|
-
|
89
|
-
def bitferry(&code)
|
90
|
-
begin
|
91
|
-
Bitferry.restore
|
92
|
-
result = yield
|
93
|
-
exit(Bitferry.commit && result ? 0 : 1)
|
94
|
-
rescue => e
|
95
|
-
Bitferry.log.fatal(e.message)
|
96
|
-
exit(1)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
|
101
|
-
def obtain_password
|
102
|
-
if $stdin.tty?
|
103
|
-
p1 = IO.console.getpass 'Enter password:'
|
104
|
-
p2 = IO.console.getpass 'Repeat password:'
|
105
|
-
raise 'passwords do not match' unless p1 == p2
|
106
|
-
p1
|
107
|
-
else
|
108
|
-
$stdin.readline.strip!
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
|
113
|
-
Bitferry.
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
puts
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
puts
|
156
|
-
puts
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
puts
|
164
|
-
puts
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
option
|
281
|
-
option '-
|
282
|
-
option '-
|
283
|
-
option
|
284
|
-
option ['--
|
285
|
-
option ['--
|
286
|
-
|
287
|
-
parameter '
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
parameter '
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
1
|
+
require 'clamp'
|
2
|
+
require 'bitferry'
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
|
6
|
+
Endpoint = %{
|
7
|
+
The endpoint may be one of:
|
8
|
+
* directory -- absolute or relative local directory (/data, ../source, c:\\data)
|
9
|
+
* local:directory, :directory -- absolute local directory (:/data, local:c:\\data)
|
10
|
+
* :tag:directory -- path relative to the intact volume matched by (partial) tag (:fa2c:source/data)
|
11
|
+
|
12
|
+
The former case resolves specified directory againt an intact volume to make it volume-relative.
|
13
|
+
It is an error if there is no intact volume that encompasses specified directory.
|
14
|
+
The local: directory is left as is (not resolved against volumes).
|
15
|
+
The :tag: directory is bound to the specified volume.
|
16
|
+
}
|
17
|
+
|
18
|
+
|
19
|
+
Encryption = %{
|
20
|
+
The encryption mode is controlled by --encrypt or --decrypt options.
|
21
|
+
The mandatory password will be read from the standard input channel (pipe or keyboard).
|
22
|
+
}
|
23
|
+
|
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
|
+
|
50
|
+
def setup_rclone_task(x)
|
51
|
+
x.parameter 'SOURCE', 'Source endpoint specifier'
|
52
|
+
x.parameter 'DESTINATION', 'Destination endpoint specifier'
|
53
|
+
x.option '-e', :flag, 'Encrypt files in destination using default profile (alias for -E default)', attribute_name: :e do
|
54
|
+
$encryption = Bitferry::Rclone::Encrypt
|
55
|
+
$profile = :default
|
56
|
+
end
|
57
|
+
x.option '-d', :flag, 'Decrypt source files using default profile (alias for -D default)', attribute_name: :d do
|
58
|
+
$encryption = Bitferry::Rclone::Decrypt
|
59
|
+
$profile = :default
|
60
|
+
end
|
61
|
+
x.option '-u', :flag, 'Apply extended (unicode) encryption profile options (alias for -E extended / -D extended)', attribute_name: :u do
|
62
|
+
$extended = true
|
63
|
+
end
|
64
|
+
x.option ['--process', '-X'], 'OPTIONS', 'Extra task processing profile/options' do |opts|
|
65
|
+
$process = opts
|
66
|
+
end
|
67
|
+
x.option ['--encrypt', '-E'], 'OPTIONS', 'Encrypt files in destination using specified profile/options' do |opts|
|
68
|
+
$encryption = Bitferry::Rclone::Encrypt
|
69
|
+
$profile = opts
|
70
|
+
end
|
71
|
+
x.option ['--decrypt', '-D'], 'OPTIONS', 'Decrypt source files using specified profile/options' do |opts|
|
72
|
+
$encryption = Bitferry::Rclone::Decrypt
|
73
|
+
$profile = opts
|
74
|
+
end
|
75
|
+
setup_task(x)
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def create_rclone_task(task, *args, **opts)
|
80
|
+
task.new(*args,
|
81
|
+
process: $process,
|
82
|
+
encryption: $encryption&.new(obtain_password, process: $extended ? :extended : $profile),
|
83
|
+
include: $include.flatten.uniq, exclude: $exclude.flatten.uniq,
|
84
|
+
**opts
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def bitferry(&code)
|
90
|
+
begin
|
91
|
+
Bitferry.restore
|
92
|
+
result = yield
|
93
|
+
exit(Bitferry.commit && result ? 0 : 1)
|
94
|
+
rescue => e
|
95
|
+
Bitferry.log.fatal(e.message)
|
96
|
+
exit(1)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
def obtain_password
|
102
|
+
if $stdin.tty?
|
103
|
+
p1 = IO.console.getpass 'Enter password:'
|
104
|
+
p2 = IO.console.getpass 'Repeat password:'
|
105
|
+
raise 'passwords do not match' unless p1 == p2
|
106
|
+
p1
|
107
|
+
else
|
108
|
+
$stdin.readline.strip!
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
Bitferry.ui = :cli
|
114
|
+
Bitferry.log.level = Logger::DEBUG if $DEBUG
|
115
|
+
|
116
|
+
|
117
|
+
Clamp do
|
118
|
+
|
119
|
+
|
120
|
+
self.default_subcommand = 'show'
|
121
|
+
|
122
|
+
|
123
|
+
option '--version', :flag, 'Print version' do
|
124
|
+
$stdout.puts Bitferry::VERSION
|
125
|
+
exit
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
option ['--verbose', '-v'], :flag, 'Extensive logging' do
|
130
|
+
Bitferry.verbosity = :verbose
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
option ['--quiet', '-q'], :flag, 'Disable logging' do
|
135
|
+
Bitferry.verbosity = :quiet
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
option ['--dry-run', '-n'], :flag, 'Simulation mode (make no on-disk changes)' do
|
140
|
+
Bitferry.simulate = true
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
subcommand ['show', 'info', 'i'], 'Print state' do
|
145
|
+
def execute
|
146
|
+
Bitferry.restore
|
147
|
+
unless (xs = Bitferry::Volume.intact).empty?
|
148
|
+
$stdout.puts '# Intact volumes'
|
149
|
+
$stdout.puts
|
150
|
+
xs.each do |volume|
|
151
|
+
$stdout.puts " #{volume.tag} #{volume.root}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
unless (xs = Bitferry::Task.intact).empty?
|
155
|
+
$stdout.puts
|
156
|
+
$stdout.puts '# Intact tasks'
|
157
|
+
$stdout.puts
|
158
|
+
xs.each do |task|
|
159
|
+
$stdout.puts " #{task.tag} #{task.show_status}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
if !(xs = Bitferry::Task.stale).empty? && Bitferry.verbosity == :verbose
|
163
|
+
$stdout.puts
|
164
|
+
$stdout.puts '# Stale tasks'
|
165
|
+
$stdout.puts
|
166
|
+
xs.each do |task|
|
167
|
+
$stdout.puts " #{task.tag} #{task.show_status}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
subcommand ['create', 'c'], 'Create entity' do
|
175
|
+
|
176
|
+
|
177
|
+
subcommand ['volume', 'v'], 'Create volume' do
|
178
|
+
banner %{
|
179
|
+
Create new volume in specified directory. Create directory if it does not exist.
|
180
|
+
Refuse to overwrite existing volume storage unless --force is specified.
|
181
|
+
}
|
182
|
+
option '--force', :flag, 'Overwrite existing volume storage in target directory'
|
183
|
+
parameter 'DIRECTORY', 'Target volume directory'
|
184
|
+
def execute
|
185
|
+
bitferry { Bitferry::Volume.new(directory, overwrite: force?) }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
subcommand ['task', 't'], 'Create task' do
|
191
|
+
|
192
|
+
|
193
|
+
subcommand ['copy', 'c'], 'Create copy task' do
|
194
|
+
banner %{
|
195
|
+
Create source --> destination file copy task.
|
196
|
+
|
197
|
+
The task operates recursively on two specified endpoints.
|
198
|
+
This task unconditionally copies all source files overwriting existing files in destination.
|
199
|
+
|
200
|
+
#{Endpoint}
|
201
|
+
|
202
|
+
#{Encryption}
|
203
|
+
|
204
|
+
This task employs the Rclone worker.
|
205
|
+
}
|
206
|
+
setup_rclone_task(self)
|
207
|
+
def execute
|
208
|
+
bitferry { create_rclone_task(Bitferry::Rclone::Copy, source, destination) }
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
subcommand ['update', 'u'], 'Create update task' do
|
214
|
+
banner %{
|
215
|
+
Create source --> destination file update (freshen) task.
|
216
|
+
|
217
|
+
The task operates recursively on two specified endpoints.
|
218
|
+
This task copies newer source files while skipping unchanged files in destination.
|
219
|
+
|
220
|
+
#{Endpoint}
|
221
|
+
|
222
|
+
#{Encryption}
|
223
|
+
|
224
|
+
This task employs the Rclone worker.
|
225
|
+
}
|
226
|
+
setup_rclone_task(self)
|
227
|
+
def execute
|
228
|
+
bitferry { create_rclone_task(Bitferry::Rclone::Update, source, destination) }
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
subcommand ['synchronize', 'sync', 's'], 'Create one way sync task' do
|
234
|
+
banner %{
|
235
|
+
Create source --> destination one way file synchronization task.
|
236
|
+
|
237
|
+
The task operates recursively on two specified endpoints.
|
238
|
+
This task copies newer source files while skipping unchanged files in destination.
|
239
|
+
Also, it deletes destination files which are non-existent in source.
|
240
|
+
|
241
|
+
#{Endpoint}
|
242
|
+
|
243
|
+
#{Encryption}
|
244
|
+
|
245
|
+
This task employs the Rclone worker.
|
246
|
+
}
|
247
|
+
setup_rclone_task(self)
|
248
|
+
def execute
|
249
|
+
bitferry { create_rclone_task(Bitferry::Rclone::Synchronize, source, destination) }
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
subcommand ['equalize', 'bisync', 'e'], 'Create two way sync task' do
|
255
|
+
banner %{
|
256
|
+
Create source <-> destination two way file synchronization task.
|
257
|
+
|
258
|
+
The task operates recursively on two specified endpoints.
|
259
|
+
This task retains only the most recent versions of files on both endpoints.
|
260
|
+
Opon execution both endpoints are left identical.
|
261
|
+
|
262
|
+
#{Endpoint}
|
263
|
+
|
264
|
+
#{Encryption}
|
265
|
+
|
266
|
+
This task employs the Rclone worker.
|
267
|
+
}
|
268
|
+
setup_rclone_task(self)
|
269
|
+
def execute
|
270
|
+
bitferry { create_rclone_task(Bitferry::Rclone::Equalize, source, destination) }
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
subcommand ['backup', 'b'], 'Create backup task' do
|
276
|
+
banner %{
|
277
|
+
Create source --> repository incremental backup task.
|
278
|
+
This task employs the Restic worker.
|
279
|
+
}
|
280
|
+
option '--force', :flag, 'Force overwriting existing repository' do $format = true end
|
281
|
+
option ['--attach', '-a'], :flag, 'Attach to existing repository' do $format = false end
|
282
|
+
option '-f', :flag, 'Apply default snapshot retention policy options (alias for -F default)', attribute_name: :f do $forget = :default end
|
283
|
+
option '-c', :flag, 'Apply default repository checking options (alias for -C default)', attribute_name: :c do $check = :default end
|
284
|
+
option ['--process', '-X'], 'OPTIONS', 'Extra task processing profile/options' do |opts| $process = opts end
|
285
|
+
option ['--forget', '-F'], 'OPTIONS', 'Snapshot retention policy with profile/options' do |opts| $forget = opts end
|
286
|
+
option ['--check', '-C'], 'OPTIONS', 'Repository checking with profile/options' do |opts| $check = opts end
|
287
|
+
parameter 'SOURCE', 'Source endpoint specifier'
|
288
|
+
parameter 'REPOSITORY', 'Destination repository endpoint specifier'
|
289
|
+
setup_task(self, include: false)
|
290
|
+
def execute
|
291
|
+
bitferry {
|
292
|
+
Bitferry::Restic::Backup.new(
|
293
|
+
source, repository, obtain_password,
|
294
|
+
format: $format,
|
295
|
+
process: $process,
|
296
|
+
check: $check,
|
297
|
+
forget: $forget,
|
298
|
+
exclude: $exclude.flatten.uniq
|
299
|
+
)
|
300
|
+
}
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
|
305
|
+
subcommand ['restore', 'r'], 'Create restore task' do
|
306
|
+
banner %{
|
307
|
+
Create repository --> destination restore task.
|
308
|
+
This task employs the Restic worker.
|
309
|
+
}
|
310
|
+
option ['--process', '-X'], 'OPTIONS', 'Extra task processing profile/options' do |opts| $process = opts end
|
311
|
+
parameter 'REPOSITORY', 'Source repository endpoint specifier'
|
312
|
+
parameter 'DESTINATION', 'Destination endpoint specifier'
|
313
|
+
setup_task(self)
|
314
|
+
def execute
|
315
|
+
bitferry {
|
316
|
+
Bitferry::Restic::Restore.new(
|
317
|
+
destination, repository, obtain_password,
|
318
|
+
process: $process,
|
319
|
+
include: $include.flatten.uniq, exclude: $exclude.flatten.uniq
|
320
|
+
)
|
321
|
+
}
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
|
332
|
+
subcommand ['delete', 'd'], 'Delete entity' do
|
333
|
+
|
334
|
+
|
335
|
+
subcommand ['volume', 'v'], 'Delete volume' do
|
336
|
+
banner %{
|
337
|
+
Delete volumes matched by specified (partial) tags.
|
338
|
+
There may be multiple tags but each tag must match at most one volume.
|
339
|
+
This command deletes the volume storage file only with the rest of data left intact.
|
340
|
+
}
|
341
|
+
option '--wipe', :flag, 'Wipe target directory upon deletion'
|
342
|
+
parameter 'TAG ...', 'Volume tags', attribute_name: :tags
|
343
|
+
def execute
|
344
|
+
bitferry { Bitferry::Volume.delete(*tags, wipe: wipe?) }
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
|
349
|
+
subcommand ['task', 't'], 'Delete task' do
|
350
|
+
banner %{
|
351
|
+
Delete tasks matched by specified (partial) tags.
|
352
|
+
There may be multiple tags but each tag must match at most one task.
|
353
|
+
}
|
354
|
+
parameter 'TAG ...', 'Task tags', attribute_name: :tags
|
355
|
+
def execute
|
356
|
+
bitferry { Bitferry::Task.delete(*tags) }
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
|
361
|
+
end
|
362
|
+
|
363
|
+
|
364
|
+
subcommand ['process', 'x'], 'Process tasks' do
|
365
|
+
banner %{
|
366
|
+
Process tasks matched by specified (partial) tags.
|
367
|
+
If no tags are given, process all intact tasks.
|
368
|
+
}
|
369
|
+
parameter '[TAG] ...', 'Task tags', attribute_name: :tags
|
370
|
+
def execute
|
371
|
+
bitferry { Bitferry.process(*tags) }
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
|
376
|
+
end
|