yobi 0.1.0 → 0.2.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/CHANGELOG.md +16 -0
- data/README.md +93 -124
- data/lib/yobi/argv_builder.rb +5 -0
- data/lib/yobi/errors.rb +6 -10
- data/lib/yobi/fancy_hash.rb +31 -0
- data/lib/yobi/io_handle.rb +9 -7
- data/lib/yobi/{mount.rb → mount_handle.rb} +11 -10
- data/lib/yobi/repository/backup.rb +75 -85
- data/lib/yobi/repository/cat.rb +10 -0
- data/lib/yobi/repository/check.rb +61 -45
- data/lib/yobi/repository/diff.rb +130 -47
- data/lib/yobi/repository/dump.rb +1 -1
- data/lib/yobi/repository/find.rb +11 -33
- data/lib/yobi/repository/forget.rb +6 -48
- data/lib/yobi/repository/init.rb +70 -0
- data/lib/yobi/repository/key.rb +59 -52
- data/lib/yobi/repository/ls.rb +48 -37
- data/lib/yobi/repository/mount.rb +16 -18
- data/lib/yobi/repository/restore.rb +42 -40
- data/lib/yobi/repository/snapshots.rb +2 -29
- data/lib/yobi/repository/stats.rb +47 -4
- data/lib/yobi/repository/tag.rb +50 -35
- data/lib/yobi/repository.rb +8 -43
- data/lib/yobi/restic.rb +73 -32
- data/lib/yobi/restic_output.rb +169 -12
- data/lib/yobi/snapshot.rb +81 -2
- data/lib/yobi/version.rb +2 -2
- data/lib/yobi.rb +2 -1
- data/sig/yobi.rbs +252 -139
- metadata +5 -7
- data/Rakefile +0 -12
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yobi
|
|
4
|
+
# Base class for every wrapper around one raw Restic JSON object.
|
|
5
|
+
class FancyHash < Hash
|
|
6
|
+
# @private
|
|
7
|
+
def initialize(raw)
|
|
8
|
+
super()
|
|
9
|
+
replace(raw)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @return [String]
|
|
13
|
+
def inspect
|
|
14
|
+
"#<#{self.class} #{super}>"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @return [void]
|
|
18
|
+
def pretty_print(q)
|
|
19
|
+
q.object_group(self) do
|
|
20
|
+
each do |key, value|
|
|
21
|
+
next if key == "message_type"
|
|
22
|
+
|
|
23
|
+
value = public_send(key) if self.class.method_defined?(key, false)
|
|
24
|
+
q.breakable
|
|
25
|
+
q.text "#{key}="
|
|
26
|
+
q.pp(value)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/yobi/io_handle.rb
CHANGED
|
@@ -22,14 +22,11 @@ module Yobi
|
|
|
22
22
|
# @return [Integer] the Restic process's pid
|
|
23
23
|
attr_reader :pid
|
|
24
24
|
|
|
25
|
-
# @
|
|
26
|
-
|
|
27
|
-
# @param output_file [File]
|
|
28
|
-
# @param argv [Array<String>]
|
|
29
|
-
def initialize(io, pid:, output_file:, argv:)
|
|
25
|
+
# @private
|
|
26
|
+
def initialize(io, pid:, output:, argv:)
|
|
30
27
|
@io = io
|
|
31
28
|
@pid = pid
|
|
32
|
-
@
|
|
29
|
+
@output = output
|
|
33
30
|
@argv = argv
|
|
34
31
|
@closed = false
|
|
35
32
|
end
|
|
@@ -39,6 +36,11 @@ module Yobi
|
|
|
39
36
|
@closed
|
|
40
37
|
end
|
|
41
38
|
|
|
39
|
+
# @return [String]
|
|
40
|
+
def inspect
|
|
41
|
+
"#<#{self.class} pid=#{pid} closed=#{closed?}>"
|
|
42
|
+
end
|
|
43
|
+
|
|
42
44
|
# Reaps the Restic process and raises based on its exit code. Safe to
|
|
43
45
|
# call more than once.
|
|
44
46
|
#
|
|
@@ -49,7 +51,7 @@ module Yobi
|
|
|
49
51
|
@closed = true
|
|
50
52
|
@io.close unless @io.closed?
|
|
51
53
|
_, status = Process.wait2(@pid)
|
|
52
|
-
Restic.dispatch(exit_code: status.exitstatus, output:
|
|
54
|
+
Restic.dispatch(exit_code: status.exitstatus, output: @output, argv: @argv)
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
# Yields binary-safe chunks of {#io} until EOF, then calls {#close}.
|
|
@@ -5,20 +5,16 @@ module Yobi
|
|
|
5
5
|
# Restic has reported itself ready. The mounted filesystem itself is
|
|
6
6
|
# browsed with ordinary file I/O at {#mountpoint}; this object only
|
|
7
7
|
# manages the Restic process's lifetime.
|
|
8
|
-
class
|
|
8
|
+
class MountHandle
|
|
9
9
|
# @return [String] the path the repository is mounted at
|
|
10
10
|
attr_reader :mountpoint
|
|
11
11
|
|
|
12
|
-
# @
|
|
13
|
-
|
|
14
|
-
# @param output [IO]
|
|
15
|
-
# @param output_file [File]
|
|
16
|
-
# @param argv [Array<String>]
|
|
17
|
-
def initialize(wait_thr:, mountpoint:, output:, output_file:, argv:)
|
|
12
|
+
# @private
|
|
13
|
+
def initialize(wait_thr:, mountpoint:, pipe:, output:, argv:)
|
|
18
14
|
@wait_thr = wait_thr
|
|
19
15
|
@mountpoint = mountpoint
|
|
16
|
+
@pipe = pipe
|
|
20
17
|
@output = output
|
|
21
|
-
@output_file = output_file
|
|
22
18
|
@argv = argv
|
|
23
19
|
@stopped = false
|
|
24
20
|
end
|
|
@@ -33,6 +29,11 @@ module Yobi
|
|
|
33
29
|
@stopped
|
|
34
30
|
end
|
|
35
31
|
|
|
32
|
+
# @return [String]
|
|
33
|
+
def inspect
|
|
34
|
+
"#<#{self.class} pid=#{pid} mountpoint=#{mountpoint.inspect} stopped=#{stopped?}>"
|
|
35
|
+
end
|
|
36
|
+
|
|
36
37
|
# Sends the Restic process SIGINT, waits for it to unmount and exit,
|
|
37
38
|
# then raises based on its exit code. Safe to call more than once, and
|
|
38
39
|
# safe to call after the mount has already been stopped externally
|
|
@@ -52,13 +53,13 @@ module Yobi
|
|
|
52
53
|
|
|
53
54
|
drain_remaining_output
|
|
54
55
|
status = @wait_thr.value
|
|
55
|
-
Restic.dispatch(exit_code: status.exitstatus, output:
|
|
56
|
+
Restic.dispatch(exit_code: status.exitstatus, output: @output, argv: @argv)
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
private
|
|
59
60
|
|
|
60
61
|
def drain_remaining_output
|
|
61
|
-
@
|
|
62
|
+
@pipe.each_line { |line| @output.file.write(line) }
|
|
62
63
|
rescue IOError, Errno::EBADF
|
|
63
64
|
nil
|
|
64
65
|
end
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "json"
|
|
4
3
|
require "time"
|
|
5
|
-
require "delegate"
|
|
6
4
|
require "shellwords"
|
|
7
5
|
|
|
8
6
|
module Yobi
|
|
@@ -40,7 +38,7 @@ module Yobi
|
|
|
40
38
|
# @param time [String, nil] timestamp to record instead of now
|
|
41
39
|
# @param verbose [Boolean] stream a {Yobi::BackupVerboseStatus} per file to the block
|
|
42
40
|
# @param with_atime [Boolean] also store files' access times
|
|
43
|
-
# @yieldparam message [Yobi::BackupStatus, Yobi::BackupError, Yobi::BackupVerboseStatus]
|
|
41
|
+
# @yieldparam message [Yobi::BackupStatus, Yobi::BackupError, Yobi::BackupVerboseStatus, Yobi::BackupSummary]
|
|
44
42
|
# @return [Yobi::BackupOutcome]
|
|
45
43
|
def backup(source:, excludes: [], exclude_files: [], exclude_if_present: [], exclude_larger_than: nil,
|
|
46
44
|
files_from: [], files_from_raw: [], files_from_verbatim: [], iexcludes: [], iexclude_files: [],
|
|
@@ -88,27 +86,13 @@ module Yobi
|
|
|
88
86
|
end
|
|
89
87
|
end
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
run_restic(argv)
|
|
95
|
-
end
|
|
96
|
-
BackupOutcome.new(execution[:exit_code], execution[:output])
|
|
89
|
+
output = Yobi::ResticOutput.new(transform: Yobi::BackupMessageWrapper)
|
|
90
|
+
execution = run_restic(argv, output: output, &block)
|
|
91
|
+
BackupOutcome.new(execution)
|
|
97
92
|
end
|
|
98
93
|
|
|
99
94
|
private
|
|
100
95
|
|
|
101
|
-
def dispatch_backup_message(raw)
|
|
102
|
-
case raw["message_type"]
|
|
103
|
-
when "status"
|
|
104
|
-
yield Yobi::BackupStatus.new(raw)
|
|
105
|
-
when "error"
|
|
106
|
-
yield Yobi::BackupError.new(raw)
|
|
107
|
-
when "verbose_status"
|
|
108
|
-
yield Yobi::BackupVerboseStatus.new(raw)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
96
|
def tokenize(command)
|
|
113
97
|
case command
|
|
114
98
|
in String => value
|
|
@@ -119,75 +103,102 @@ module Yobi
|
|
|
119
103
|
end
|
|
120
104
|
end
|
|
121
105
|
|
|
106
|
+
# Classifies a raw backup message Hash by its own message_type, wrapping
|
|
107
|
+
# it in the matching typed class. Used both by {Yobi::Repository#backup}
|
|
108
|
+
# (as the {Yobi::ResticOutput} `transform:` for a live streaming run) and
|
|
109
|
+
# by {Yobi::BackupOutcome}'s own post-hoc accessors.
|
|
110
|
+
#
|
|
111
|
+
# @private
|
|
112
|
+
module BackupMessageWrapper
|
|
113
|
+
# @param raw [Hash]
|
|
114
|
+
# @return [Yobi::BackupStatus, Yobi::BackupError, Yobi::BackupVerboseStatus, Yobi::BackupSummary, Hash]
|
|
115
|
+
# the raw Hash itself for a message_type this version of Yobi doesn't recognize
|
|
116
|
+
def self.call(raw)
|
|
117
|
+
case raw["message_type"]
|
|
118
|
+
when "status" then BackupStatus.new(raw)
|
|
119
|
+
when "error" then BackupError.new(raw)
|
|
120
|
+
when "verbose_status" then BackupVerboseStatus.new(raw)
|
|
121
|
+
when "summary" then BackupSummary.new(raw)
|
|
122
|
+
else raw
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
122
127
|
# The outcome of one {Yobi::Repository#backup} call.
|
|
123
|
-
class BackupOutcome
|
|
124
|
-
#
|
|
125
|
-
# `source: [:stdin_from_command, ...]` command's own subprocess.
|
|
128
|
+
class BackupOutcome
|
|
129
|
+
# @private
|
|
126
130
|
COMMAND_OUTPUT_LINE_PATTERN = /\Asubprocess [^:]+: (.*)/
|
|
127
131
|
|
|
128
|
-
# @return [
|
|
129
|
-
|
|
130
|
-
exit_code.zero?
|
|
131
|
-
end
|
|
132
|
+
# @return [Yobi::ResticOutput]
|
|
133
|
+
attr_reader :output
|
|
132
134
|
|
|
133
|
-
# @
|
|
134
|
-
def
|
|
135
|
-
|
|
135
|
+
# @private
|
|
136
|
+
def initialize(execution)
|
|
137
|
+
@output = execution[:output]
|
|
136
138
|
end
|
|
137
139
|
|
|
138
140
|
# @return [Enumerable<Yobi::BackupError>]
|
|
139
141
|
def errors
|
|
140
|
-
@errors ||=
|
|
142
|
+
@errors ||= output.messages("error")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Every message from the run, in file order, each wrapped in its own
|
|
146
|
+
# {Yobi::BackupStatus}/{Yobi::BackupError}/{Yobi::BackupVerboseStatus}/{Yobi::BackupSummary}.
|
|
147
|
+
#
|
|
148
|
+
# @return [Enumerable<Yobi::BackupStatus, Yobi::BackupError, Yobi::BackupVerboseStatus, Yobi::BackupSummary>]
|
|
149
|
+
def messages
|
|
150
|
+
@messages ||= output.messages
|
|
141
151
|
end
|
|
142
152
|
|
|
143
153
|
# The `source: [:stdin_from_command, ...]` subprocess's own stderr, if any, de-prefixed.
|
|
144
154
|
#
|
|
145
|
-
# @
|
|
155
|
+
# @yieldparam line [String]
|
|
156
|
+
# @return [Enumerator] if no block is given
|
|
146
157
|
def command_output
|
|
147
|
-
|
|
158
|
+
return enum_for(:command_output) unless block_given?
|
|
159
|
+
|
|
160
|
+
output.stderr_lines.each do |line|
|
|
148
161
|
match = COMMAND_OUTPUT_LINE_PATTERN.match(line)
|
|
149
|
-
match[1] if match
|
|
162
|
+
yield match[1] if match
|
|
150
163
|
end
|
|
151
164
|
end
|
|
152
165
|
|
|
153
|
-
# @return [
|
|
154
|
-
def
|
|
155
|
-
@
|
|
156
|
-
hash = summary_hash
|
|
157
|
-
hash.merge(
|
|
158
|
-
"backup_start" => (Time.parse(hash["backup_start"]) if hash["backup_start"]),
|
|
159
|
-
"backup_end" => (Time.parse(hash["backup_end"]) if hash["backup_end"])
|
|
160
|
-
)
|
|
161
|
-
end
|
|
166
|
+
# @return [Yobi::BackupSummary] Restic's own `"summary"` fields
|
|
167
|
+
def summary
|
|
168
|
+
@summary ||= output.messages("summary").first || BackupSummary.new({})
|
|
162
169
|
end
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
find_summary || {}
|
|
170
|
+
alias_method :report, :summary
|
|
171
|
+
|
|
172
|
+
# @return [void]
|
|
173
|
+
def pretty_print(q)
|
|
174
|
+
q.object_group(self) do
|
|
175
|
+
q.breakable
|
|
176
|
+
q.text("(has errors)") if errors.any?
|
|
177
|
+
q.breakable
|
|
178
|
+
q.pp summary
|
|
173
179
|
end
|
|
174
180
|
end
|
|
181
|
+
end
|
|
175
182
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
# The `"summary"` message from a backup run, the final result once the
|
|
184
|
+
# command finishes. Dispatched to {Yobi::Repository#backup}'s block, and
|
|
185
|
+
# also what {Yobi::BackupOutcome#summary} returns.
|
|
186
|
+
# https://restic.readthedocs.io/en/stable/075_scripting.html#summary
|
|
187
|
+
class BackupSummary < Yobi::FancyHash
|
|
188
|
+
# @return [Time, nil]
|
|
189
|
+
def backup_start
|
|
190
|
+
@backup_start ||= Time.parse(self["backup_start"]) if self["backup_start"]
|
|
191
|
+
end
|
|
183
192
|
|
|
184
|
-
|
|
193
|
+
# @return [Time, nil]
|
|
194
|
+
def backup_end
|
|
195
|
+
@backup_end ||= Time.parse(self["backup_end"]) if self["backup_end"]
|
|
185
196
|
end
|
|
186
197
|
end
|
|
187
198
|
|
|
188
199
|
# One `"status"` message from a live backup run.
|
|
189
200
|
# https://restic.readthedocs.io/en/stable/075_scripting.html#status
|
|
190
|
-
class BackupStatus <
|
|
201
|
+
class BackupStatus < Yobi::FancyHash
|
|
191
202
|
# @return [Float, nil]
|
|
192
203
|
def percent_done
|
|
193
204
|
self["percent_done"]
|
|
@@ -226,7 +237,7 @@ module Yobi
|
|
|
226
237
|
|
|
227
238
|
# One `"error"` message from a backup run.
|
|
228
239
|
# https://restic.readthedocs.io/en/stable/075_scripting.html#error
|
|
229
|
-
class BackupError <
|
|
240
|
+
class BackupError < Yobi::FancyHash
|
|
230
241
|
# @return [String, nil]
|
|
231
242
|
def message
|
|
232
243
|
dig("error", "message")
|
|
@@ -243,30 +254,9 @@ module Yobi
|
|
|
243
254
|
end
|
|
244
255
|
end
|
|
245
256
|
|
|
246
|
-
# Enumerable over every {Yobi::BackupError} in a run.
|
|
247
|
-
class BackupErrors
|
|
248
|
-
include Enumerable
|
|
249
|
-
|
|
250
|
-
# @param output [Yobi::ResticOutput]
|
|
251
|
-
def initialize(output)
|
|
252
|
-
@output = output
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
# @yieldparam error [Yobi::BackupError]
|
|
256
|
-
# @return [Enumerator] if no block is given
|
|
257
|
-
def each
|
|
258
|
-
return enum_for(:each) unless block_given?
|
|
259
|
-
|
|
260
|
-
@output.index["error"].each do |offset|
|
|
261
|
-
line = @output.read_line_at(offset)
|
|
262
|
-
yield BackupError.new(JSON.parse(line))
|
|
263
|
-
end
|
|
264
|
-
end
|
|
265
|
-
end
|
|
266
|
-
|
|
267
257
|
# One `"verbose_status"` message from a backup run, one per file. Only
|
|
268
258
|
# emitted when `verbose: true` is passed to {Yobi::Repository#backup}.
|
|
269
|
-
class BackupVerboseStatus <
|
|
259
|
+
class BackupVerboseStatus < Yobi::FancyHash
|
|
270
260
|
# @return [String]
|
|
271
261
|
def action
|
|
272
262
|
self["action"]
|
data/lib/yobi/repository/cat.rb
CHANGED
|
@@ -4,6 +4,16 @@ require "json"
|
|
|
4
4
|
|
|
5
5
|
module Yobi
|
|
6
6
|
class Repository
|
|
7
|
+
# `restic cat config`: the repository's own config document.
|
|
8
|
+
#
|
|
9
|
+
# @return [Hash]
|
|
10
|
+
# @raise [Yobi::RepositoryNotFound, Yobi::AuthenticationFailed]
|
|
11
|
+
def cat_config
|
|
12
|
+
execution = run_restic(build_argv("cat", "config"))
|
|
13
|
+
JSON.parse(execution[:output].to_s)
|
|
14
|
+
end
|
|
15
|
+
alias_method :config, :cat_config
|
|
16
|
+
|
|
7
17
|
# `restic cat snapshot ID`: one snapshot's own raw stored record.
|
|
8
18
|
#
|
|
9
19
|
# @param id [String, Yobi::Snapshot]
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "json"
|
|
4
|
-
require "delegate"
|
|
5
|
-
|
|
6
3
|
module Yobi
|
|
7
4
|
class Repository
|
|
8
5
|
# `restic check`: tests the repository for errors.
|
|
@@ -23,74 +20,93 @@ module Yobi
|
|
|
23
20
|
a.repeat_flag(:tag, tags)
|
|
24
21
|
a.flag(:with_cache) if with_cache
|
|
25
22
|
end
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
output = Yobi::ResticOutput.new(transform: Yobi::CheckMessageWrapper)
|
|
24
|
+
execution = run_restic(argv, output: output)
|
|
25
|
+
CheckOutcome.new(execution)
|
|
28
26
|
end
|
|
29
27
|
end
|
|
30
28
|
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
# Classifies a raw check message Hash by its own message_type, wrapping
|
|
30
|
+
# it in the matching typed class. Used both as the {Yobi::ResticOutput}
|
|
31
|
+
# `transform:` for {Yobi::Repository#check} and by {Yobi::CheckOutcome}'s
|
|
32
|
+
# own post-hoc accessors.
|
|
33
|
+
#
|
|
34
|
+
# @private
|
|
35
|
+
module CheckMessageWrapper
|
|
36
|
+
# @param raw [Hash]
|
|
37
|
+
# @return [Yobi::CheckError, Yobi::CheckSummary, Hash]
|
|
38
|
+
# the raw Hash itself for a message_type this version of Yobi doesn't recognize
|
|
39
|
+
def self.call(raw)
|
|
40
|
+
case raw["message_type"]
|
|
41
|
+
when "error" then CheckError.new(raw)
|
|
42
|
+
when "summary" then CheckSummary.new(raw)
|
|
43
|
+
else raw
|
|
44
|
+
end
|
|
36
45
|
end
|
|
46
|
+
end
|
|
37
47
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
# The outcome of one {Yobi::Repository#check} call.
|
|
49
|
+
class CheckOutcome
|
|
50
|
+
# @return [Yobi::ResticOutput]
|
|
51
|
+
attr_reader :output
|
|
42
52
|
|
|
43
|
-
private
|
|
53
|
+
# @private
|
|
54
|
+
def initialize(execution)
|
|
55
|
+
@output = execution[:output]
|
|
56
|
+
end
|
|
44
57
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if hash && hash["message_type"] == "summary"
|
|
49
|
-
hash
|
|
50
|
-
else
|
|
51
|
-
find_summary || {}
|
|
52
|
-
end
|
|
58
|
+
# @return [Yobi::CheckSummary] Restic's own `"summary"` fields
|
|
59
|
+
def summary
|
|
60
|
+
@summary ||= output.messages("summary").first || CheckSummary.new({})
|
|
53
61
|
end
|
|
62
|
+
alias_method :report, :summary
|
|
54
63
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
# @return [Enumerable<Yobi::CheckError>]
|
|
65
|
+
def errors
|
|
66
|
+
@errors ||= output.messages("error")
|
|
67
|
+
end
|
|
58
68
|
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
# @return [void]
|
|
70
|
+
def pretty_print(q)
|
|
71
|
+
q.object_group(self) do
|
|
72
|
+
q.breakable
|
|
73
|
+
q.text("(has errors)") if errors.any?
|
|
74
|
+
q.breakable
|
|
75
|
+
q.pp summary
|
|
61
76
|
end
|
|
62
|
-
|
|
63
|
-
nil
|
|
64
77
|
end
|
|
65
78
|
end
|
|
66
79
|
|
|
67
80
|
# One `"error"` message from a check run.
|
|
68
81
|
# https://restic.readthedocs.io/en/stable/075_scripting.html#error
|
|
69
|
-
class CheckError <
|
|
82
|
+
class CheckError < Yobi::FancyHash
|
|
70
83
|
# @return [String]
|
|
71
84
|
def message
|
|
72
85
|
self["message"]
|
|
73
86
|
end
|
|
74
87
|
end
|
|
75
88
|
|
|
76
|
-
#
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
# The `"summary"` message from a check run, the final result once the
|
|
90
|
+
# command finishes. Dispatched to {Yobi::CheckOutcome#summary}.
|
|
91
|
+
class CheckSummary < Yobi::FancyHash
|
|
92
|
+
# @return [Integer]
|
|
93
|
+
def num_errors
|
|
94
|
+
self["num_errors"] || 0
|
|
95
|
+
end
|
|
79
96
|
|
|
80
|
-
# @
|
|
81
|
-
def
|
|
82
|
-
|
|
97
|
+
# @return [Array<String>] pack IDs needing `repair_packs`/`repair_snapshots`
|
|
98
|
+
def broken_packs
|
|
99
|
+
self["broken_packs"] || []
|
|
83
100
|
end
|
|
84
101
|
|
|
85
|
-
# @
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
102
|
+
# @return [Boolean] whether to run {Yobi::Repository#repair_index}
|
|
103
|
+
def suggest_repair_index?
|
|
104
|
+
self["suggest_repair_index"]
|
|
105
|
+
end
|
|
89
106
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
end
|
|
107
|
+
# @return [Boolean] whether to run {Yobi::Repository#prune}
|
|
108
|
+
def suggest_prune?
|
|
109
|
+
self["suggest_prune"]
|
|
94
110
|
end
|
|
95
111
|
end
|
|
96
112
|
end
|