yobi 0.3.0 → 1.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.
@@ -2,20 +2,18 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic copy`: replicates snapshots from another repository into
6
- # this one. Already-copied snapshots are skipped automatically.
5
+ # +restic copy+: replicates snapshots from another repository into this
6
+ # one. Already-copied snapshots are skipped automatically.
7
7
  #
8
- # @param from_repo [String, Yobi::Repository, Array] the source repository's
9
- # URL; a `Repository` instance (its own `#url`/`#password` are used
10
- # automatically); or a `[:file, "..."]` tuple reading the URL from a file
11
- # @param from_password [String, Array, Symbol, #call, nil] the source
12
- # repository's password, same shape as {#initialize}'s `password:`
13
- # @param snapshot_ids [Array<String>, String] snapshots to copy; all of them if empty
14
- # @param from_key_hint [String, nil]
15
- # @param hosts [Array<String>, String] filter by hostname(s)
16
- # @param paths [Array<String>, String] filter by originally backed-up path(s)
17
- # @param tags [Array<String>, String] filter by tag(s)
18
- # @return [true]
8
+ # +from_repo:+ is the source repository's URL, a Yobi::Repository (its
9
+ # own #url/#password are used automatically), or a +[:file, "..."]+
10
+ # tuple reading the URL from a file. +from_password:+ takes the same
11
+ # shape as #initialize's +password:+, and defaults to the source
12
+ # Repository's own password when +from_repo:+ is one.
13
+ #
14
+ # +snapshot_ids:+ names the snapshots to copy; all of them if empty.
15
+ # +hosts:+, +paths:+, and +tags:+ each accept a single value or an
16
+ # Array to filter by.
19
17
  def copy(from_repo:, from_password: nil, snapshot_ids: [], from_key_hint: nil, hosts: [], paths: [], tags: [])
20
18
  from_repo_flag, from_repo_value = resolved_from_repo_flag(from_repo)
21
19
  from_password = from_repo_password(from_repo) if from_password.nil?
@@ -2,12 +2,9 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic diff`: compares two snapshots.
6
- #
7
- # @param from [String] snapshot ID
8
- # @param to [String] snapshot ID
9
- # @param metadata [Boolean] also report metadata-only changes (access mode, timestamps, ...)
10
- # @return [Yobi::DiffOutcome]
5
+ # +restic diff+: compares two snapshots, +from:+ and +to:+ (both
6
+ # snapshot IDs). +metadata: true+ also reports metadata-only changes
7
+ # (access mode, timestamps, ...). Returns a Yobi::DiffOutcome.
11
8
  def diff(from:, to:, metadata: false)
12
9
  argv = build_argv("diff", from, to) do |a|
13
10
  a.flag(:metadata) if metadata
@@ -18,16 +15,7 @@ module Yobi
18
15
  end
19
16
  end
20
17
 
21
- # Classifies a raw diff message Hash by its own message_type, wrapping
22
- # it in the matching typed class. Used both as the {Yobi::ResticOutput}
23
- # `transform:` for {Yobi::Repository#diff} and by {Yobi::DiffOutcome}'s
24
- # own post-hoc accessors.
25
- #
26
- # @private
27
- module DiffMessageWrapper
28
- # @param raw [Hash]
29
- # @return [Yobi::DiffChange, Yobi::DiffStatistics, Hash]
30
- # the raw Hash itself for a message_type this version of Yobi doesn't recognize
18
+ module DiffMessageWrapper # :nodoc:
31
19
  def self.call(raw)
32
20
  case raw["message_type"]
33
21
  when "change" then DiffChange.new(raw)
@@ -37,28 +25,26 @@ module Yobi
37
25
  end
38
26
  end
39
27
 
40
- # The outcome of one {Yobi::Repository#diff} call.
28
+ # The outcome of one Yobi::Repository#diff call.
41
29
  class DiffOutcome
42
- # @return [Yobi::ResticOutput]
30
+ # The Yobi::ResticOutput backing this outcome.
43
31
  attr_reader :output
44
32
 
45
- # @private
46
- def initialize(execution)
33
+ def initialize(execution) # :nodoc:
47
34
  @output = execution[:output]
48
35
  end
49
36
 
50
- # @return [Yobi::DiffStatistics] Restic's own `"statistics"` fields
37
+ # The Yobi::DiffStatistics of Restic's own +"statistics"+ fields.
51
38
  def statistics
52
39
  @statistics ||= output.messages("statistics").first || DiffStatistics.new({})
53
40
  end
54
41
  alias_method :report, :statistics
55
42
 
56
- # @return [Enumerable<Yobi::DiffChange>]
43
+ # Every Yobi::DiffChange from the run.
57
44
  def changes
58
45
  @changes ||= output.messages("change")
59
46
  end
60
47
 
61
- # @return [void]
62
48
  def pretty_print(q)
63
49
  q.object_group(self) do
64
50
  q.breakable
@@ -67,109 +53,92 @@ module Yobi
67
53
  end
68
54
  end
69
55
 
70
- # One `"change"` message from a diff run. `modifier` is Restic's own
71
- # concatenation of single-character codes: `"+"` added, `"-"` removed,
72
- # `"U"` metadata updated, `"M"` content modified, `"T"` type changed,
73
- # `"?"` bitrot detected.
56
+ # One +"change"+ message from a diff run. #modifier is Restic's own
57
+ # concatenation of single-character codes: +"+"+ added, +"-"+ removed,
58
+ # +"U"+ metadata updated, +"M"+ content modified, +"T"+ type changed,
59
+ # +"?"+ bitrot detected.
74
60
  class DiffChange < Yobi::FancyHash
75
- # @return [String]
76
61
  def path
77
62
  self["path"]
78
63
  end
79
64
 
80
- # @return [String]
81
65
  def modifier
82
66
  self["modifier"]
83
67
  end
84
68
 
85
- # @return [Boolean]
86
69
  def added?
87
70
  modifier.include?("+")
88
71
  end
89
72
 
90
- # @return [Boolean]
91
73
  def removed?
92
74
  modifier.include?("-")
93
75
  end
94
76
 
95
- # @return [Boolean]
96
77
  def type_changed?
97
78
  modifier.include?("T")
98
79
  end
99
80
 
100
- # @return [Boolean]
101
81
  def modified?
102
82
  modifier.include?("M")
103
83
  end
104
84
 
105
- # @return [Boolean]
106
85
  def metadata_updated?
107
86
  modifier.include?("U")
108
87
  end
109
88
 
110
- # @return [Boolean]
111
89
  def bitrot?
112
90
  modifier.include?("?")
113
91
  end
114
92
  end
115
93
 
116
- # The `"statistics"` message from a diff run, the final result once the
117
- # command finishes. Dispatched to {Yobi::DiffOutcome#statistics}.
94
+ # The +"statistics"+ message from a diff run, the final result once the
95
+ # command finishes. Dispatched to Yobi::DiffOutcome#statistics.
118
96
  class DiffStatistics < Yobi::FancyHash
119
- # @return [String, nil]
120
97
  def source_snapshot
121
98
  self["source_snapshot"]
122
99
  end
123
100
 
124
- # @return [String, nil]
125
101
  def target_snapshot
126
102
  self["target_snapshot"]
127
103
  end
128
104
 
129
- # @return [Integer]
130
105
  def changed_files
131
106
  self["changed_files"] || 0
132
107
  end
133
108
 
134
- # @return [Yobi::DiffStat]
109
+ # A Yobi::DiffStat of what was added.
135
110
  def added
136
111
  @added ||= DiffStat.new(self["added"] || {})
137
112
  end
138
113
 
139
- # @return [Yobi::DiffStat]
114
+ # A Yobi::DiffStat of what was removed.
140
115
  def removed
141
116
  @removed ||= DiffStat.new(self["removed"] || {})
142
117
  end
143
118
  end
144
119
 
145
- # The `"added"`/`"removed"` sub-object of a {Yobi::DiffStatistics} message.
120
+ # The +"added"+ / +"removed"+ sub-object of a Yobi::DiffStatistics message.
146
121
  class DiffStat < Yobi::FancyHash
147
- # @return [Integer]
148
122
  def files
149
123
  self["files"] || 0
150
124
  end
151
125
 
152
- # @return [Integer]
153
126
  def dirs
154
127
  self["dirs"] || 0
155
128
  end
156
129
 
157
- # @return [Integer]
158
130
  def others
159
131
  self["others"] || 0
160
132
  end
161
133
 
162
- # @return [Integer]
163
134
  def data_blobs
164
135
  self["data_blobs"] || 0
165
136
  end
166
137
 
167
- # @return [Integer]
168
138
  def tree_blobs
169
139
  self["tree_blobs"] || 0
170
140
  end
171
141
 
172
- # @return [Integer]
173
142
  def bytes
174
143
  self["bytes"] || 0
175
144
  end
@@ -4,24 +4,23 @@ require "tempfile"
4
4
 
5
5
  module Yobi
6
6
  class Repository
7
- # @private
8
- ARCHIVE_TYPES = Set.new(%w[tar zip].flat_map { [_1, _1.to_sym] }).freeze
7
+ ARCHIVE_TYPES = Set.new(%w[tar zip].flat_map { [_1, _1.to_sym] }).freeze # :nodoc:
9
8
 
10
- # `restic dump`: extracts a file/folder from a snapshot. A single file's
9
+ # +restic dump+: extracts a file/folder from a snapshot. A single file's
11
10
  # raw bytes are written as-is; a folder is written as a tar/zip archive.
12
- # Give at most one of `target:` or a block.
11
+ # Give at most one of +target:+ or a block.
13
12
  #
14
- # @param snapshot_id [String] also accepts Restic's own `"snapshotID:subfolder"` form directly
15
- # @param file [String] path within the snapshot to extract; `"/"` dumps the whole snapshot
16
- # @param target [String, nil] write straight to this file path instead of yielding a block
17
- # @param archive [String, Symbol, nil] `"tar"` (default) or `"zip"`, when `file` is a folder
18
- # @param hosts [Array<String>, String] only relevant when `snapshot_id` is `"latest"`
19
- # @param paths [Array<String>, String] only relevant when `snapshot_id` is `"latest"`
20
- # @param tags [Array<String>, String] only relevant when `snapshot_id` is `"latest"`
21
- # @yieldparam io [IO]
22
- # @return [true] if `target:` was given
23
- # @return [Yobi::IOHandle] if no block is given
24
- # @raise [ArgumentError] if given both `target:` and a block
13
+ # +snapshot_id:+ is a snapshot ID, or Restic's own
14
+ # +"snapshotID:subfolder"+ form. +file:+ is the path within the snapshot
15
+ # to extract; +"/"+ dumps the whole snapshot. +archive:+ is +"tar"+
16
+ # (default) or +"zip"+, and only applies when +file+ is a folder.
17
+ #
18
+ # +hosts:+, +paths:+ and +tags:+ are only relevant when +snapshot_id+
19
+ # is +"latest"+.
20
+ #
21
+ # With +target:+, writes straight to that file path and returns +true+.
22
+ # Without a block, returns a Yobi::IOHandle. Raises ArgumentError if
23
+ # given both +target:+ and a block.
25
24
  def dump(snapshot_id:, file:, target: nil, archive: nil, hosts: [], paths: [], tags: [], &block)
26
25
  raise ArgumentError, "give at most one of target: or a block, not both" if target && block
27
26
 
@@ -1,29 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
3
  require "time"
5
4
 
6
5
  module Yobi
7
6
  class Repository
8
- # `restic find`: searches for files/directories across snapshots by
7
+ # +restic find+: searches for files/directories across snapshots by
9
8
  # name pattern.
10
9
  #
11
- # @param patterns [Array<String>, String] glob patterns to match
12
- # @param blob [Boolean] match blob IDs instead of file names
13
- # @param pack [Boolean] match pack IDs instead of file names
14
- # @param tree [Boolean] match tree IDs instead of file names
15
- # @param hosts [Array<String>, String] filter by hostname(s)
16
- # @param human_readable [Boolean] format sizes for humans
17
- # @param ignore_case [Boolean] case-insensitive matching
18
- # @param long [Boolean] include long listing format
19
- # @param newest [String, nil] only consider snapshots at or before this time
20
- # @param oldest [String, nil] only consider snapshots at or after this time
21
- # @param paths [Array<String>, String] filter by originally backed-up path(s)
22
- # @param reverse [Boolean] reverse sort order
23
- # @param show_pack_id [Boolean] include the pack ID each match is stored in
24
- # @param snapshot_ids [Array<String>, String] restrict to these snapshot ID(s)
25
- # @param tags [Array<String>, String] filter by tag(s)
26
- # @return [Array<Yobi::MatchesPerSnapshot>]
10
+ # +patterns:+ accepts a single glob or an Array. +blob:+, +pack:+ and
11
+ # +tree:+ match blob/pack/tree IDs instead of file names.
12
+ #
13
+ # +hosts:+, +paths:+, +snapshot_ids:+, and +tags:+ each accept a single
14
+ # value or an Array to filter by. +newest:+ / +oldest:+ restrict the
15
+ # snapshot window.
16
+ #
17
+ # +ignore_case:+, +long:+, +human_readable:+, +reverse:+ and
18
+ # +show_pack_id:+ toggle formatting/matching options.
19
+ #
20
+ # Returns an Array of Yobi::MatchesPerSnapshot.
27
21
  def find(patterns:, blob: false, pack: false, tree: false, hosts: [], human_readable: false,
28
22
  ignore_case: false, long: false, newest: nil, oldest: nil, paths: [], reverse: false,
29
23
  show_pack_id: false, snapshot_ids: [], tags: [])
@@ -45,92 +39,77 @@ module Yobi
45
39
  a.end_of_options.append(patterns)
46
40
  end
47
41
  execution = run_restic(argv)
48
- JSON.parse(execution[:output].to_s).map { |raw| MatchesPerSnapshot.new(raw) }
42
+ parse_json_output(execution).map { |raw| MatchesPerSnapshot.new(raw) }
49
43
  end
50
44
  end
51
45
 
52
- # One matched file/directory from a {Yobi::Repository#find} call.
46
+ # One matched file/directory from a Yobi::Repository#find call.
53
47
  class FindMatch < Yobi::FancyHash
54
- # @return [String]
55
48
  def path
56
49
  self["path"]
57
50
  end
58
51
 
59
- # @return [String]
60
52
  def type
61
53
  self["type"]
62
54
  end
63
55
 
64
- # @return [Integer]
65
56
  def size
66
57
  self["size"]
67
58
  end
68
59
 
69
- # @return [String]
70
60
  def permissions
71
61
  self["permissions"]
72
62
  end
73
63
 
74
- # @return [Integer]
75
64
  def uid
76
65
  self["uid"]
77
66
  end
78
67
 
79
- # @return [Integer]
80
68
  def gid
81
69
  self["gid"]
82
70
  end
83
71
 
84
- # @return [String]
85
72
  def user
86
73
  self["user"]
87
74
  end
88
75
 
89
- # @return [String]
90
76
  def group
91
77
  self["group"]
92
78
  end
93
79
 
94
- # @return [Integer]
95
80
  def inode
96
81
  self["inode"]
97
82
  end
98
83
 
99
- # @return [Time]
100
84
  def mtime
101
85
  @mtime ||= Time.parse(self["mtime"])
102
86
  end
103
87
 
104
- # @return [Time]
105
88
  def atime
106
89
  @atime ||= Time.parse(self["atime"])
107
90
  end
108
91
 
109
- # @return [Time]
110
92
  def ctime
111
93
  @ctime ||= Time.parse(self["ctime"])
112
94
  end
113
95
  end
114
96
 
115
- # One snapshot's worth of matches from a {Yobi::Repository#find} call.
97
+ # One snapshot's worth of matches from a Yobi::Repository#find call.
116
98
  class MatchesPerSnapshot < Yobi::FancyHash
117
- # Restic's own `find --json` output only gives the snapshot's ID here,
99
+ # Restic's own +find --json+ output only gives the snapshot's ID here,
118
100
  # not a full record - unlike every other place in this API where
119
- # "snapshot" means a {Yobi::Snapshot}. Call {Yobi::Repository#snapshots}
101
+ # "snapshot" means a Yobi::Snapshot. Call Yobi::Repository#snapshots
120
102
  # yourself if you need the full record.
121
- #
122
- # @return [String]
123
103
  def snapshot
124
104
  self["snapshot"]
125
105
  end
126
106
  alias_method :snapshot_id, :snapshot
127
107
 
128
- # @return [Integer]
129
108
  def hits
130
109
  self["hits"]
131
110
  end
132
111
 
133
- # @return [Array<Yobi::FindMatch>]
112
+ # An Array of Yobi::FindMatch objects for this snapshot.
134
113
  def matches
135
114
  @matches ||= (self["matches"] || []).map { |raw| Yobi::FindMatch.new(raw) }
136
115
  end
@@ -1,39 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
-
5
3
  module Yobi
6
4
  class Repository
7
- # `restic forget`: applies a retention policy, removing snapshots
8
- # that don't match any `keep_*` rule.
5
+ # +restic forget+: applies a retention policy, removing snapshots that
6
+ # don't match any +keep_*+ rule.
7
+ #
8
+ # +keep_last:+ always keeps that many of the most recent snapshots.
9
+ # +keep_hourly:+, +keep_daily:+, +keep_weekly:+, +keep_monthly:+, and
10
+ # +keep_yearly:+ each keep that many of the most recent per period.
11
+ # +keep_within:+ keeps all snapshots within a given duration (e.g.
12
+ # +"30d"+); the +keep_within_hourly:+ ... +keep_within_yearly:+ variants
13
+ # apply the same duration within their respective periods.
14
+ # +keep_tags:+ always keeps snapshots carrying any of the listed tags.
15
+ #
16
+ # +hosts:+, +tags:+, and +paths:+ each accept a single value or an
17
+ # Array to filter by.
18
+ #
19
+ # +compact:+ compacts the printed policy summary. +group_by:+ (e.g.
20
+ # +"host,paths"+) picks the grouping used to apply the policy.
21
+ # +dry_run:+ reports what would happen without doing it.
22
+ # +unsafe_allow_remove_all:+ allows removing every snapshot.
23
+ #
24
+ # +prune: true+ also reclaims disk space. +max_unused:+, +max_repack_size:+,
25
+ # +repack_cacheable_only:+, +repack_uncompressed:+ and
26
+ # +repack_smaller_than:+ are passed through to that implied prune.
9
27
  #
10
- # @param keep_last [Integer, nil] always keep this many of the most recent snapshots
11
- # @param keep_hourly [Integer, nil]
12
- # @param keep_daily [Integer, nil]
13
- # @param keep_weekly [Integer, nil]
14
- # @param keep_monthly [Integer, nil]
15
- # @param keep_yearly [Integer, nil]
16
- # @param keep_within [String, nil] keep all snapshots within this duration, e.g. `"30d"`
17
- # @param keep_within_hourly [String, nil]
18
- # @param keep_within_daily [String, nil]
19
- # @param keep_within_weekly [String, nil]
20
- # @param keep_within_monthly [String, nil]
21
- # @param keep_within_yearly [String, nil]
22
- # @param keep_tags [Array<String>, String] always keep snapshots carrying any of these tags
23
- # @param hosts [Array<String>, String] filter by hostname(s)
24
- # @param tags [Array<String>, String] filter by tag(s)
25
- # @param paths [Array<String>, String] filter by originally backed-up path(s)
26
- # @param compact [Boolean] compact the printed policy summary
27
- # @param group_by [String, nil] grouping used to apply the policy, e.g. `"host,paths"`
28
- # @param dry_run [Boolean] report what would happen without doing it
29
- # @param prune [Boolean] also reclaim disk space (equivalent to `restic forget --prune`)
30
- # @param unsafe_allow_remove_all [Boolean] allow removing every snapshot
31
- # @param max_unused [String, nil] passed through to the implied prune, e.g. `"10%"`
32
- # @param max_repack_size [String, nil] passed through to the implied prune
33
- # @param repack_cacheable_only [Boolean] passed through to the implied prune
34
- # @param repack_uncompressed [Boolean] passed through to the implied prune
35
- # @param repack_smaller_than [String, nil] passed through to the implied prune
36
- # @return [Array<Yobi::ForgetGroup>]
28
+ # Returns an Array of Yobi::ForgetGroup.
37
29
  def forget(keep_last: nil, keep_hourly: nil, keep_daily: nil, keep_weekly: nil, keep_monthly: nil,
38
30
  keep_yearly: nil, keep_within: nil, keep_within_hourly: nil, keep_within_daily: nil,
39
31
  keep_within_weekly: nil, keep_within_monthly: nil, keep_within_yearly: nil, keep_tags: [],
@@ -69,52 +61,54 @@ module Yobi
69
61
  a.flag(:repack_smaller_than, repack_smaller_than) unless repack_smaller_than.nil?
70
62
  end
71
63
  execution = run_restic(argv)
72
- JSON.parse(execution[:output].to_s).map { |raw| ForgetGroup.new(raw) }
64
+ # Permissive because `restic forget` can surround its JSON summary
65
+ # with non-JSON lines (prune chatter on <= 0.18.1, or a
66
+ # "Remove(...) failed" line on partial-removal). See
67
+ # #parse_json_output_permissively for the full rundown.
68
+ parse_json_output_permissively(execution).map { |raw| ForgetGroup.new(raw) }
73
69
  end
74
70
  end
75
71
 
76
72
  # One grouping Restic's forget policy was evaluated against (by default,
77
73
  # grouped by host+paths).
78
74
  class ForgetGroup < Yobi::FancyHash
79
- # @return [String]
80
75
  def host
81
76
  self["host"]
82
77
  end
83
78
 
84
- # @return [Array<String>]
85
79
  def tags
86
80
  @tags ||= self["tags"] || []
87
81
  end
88
82
 
89
- # @return [Array<String>]
90
83
  def paths
91
84
  self["paths"]
92
85
  end
93
86
 
94
- # @return [Array<Yobi::Snapshot>]
87
+ # Yobi::Snapshot instances kept by the policy.
95
88
  def keep
96
89
  @keep ||= (self["keep"] || []).map { |entry| Yobi::Snapshot.new(entry) }
97
90
  end
98
91
 
99
- # @return [Array<Yobi::Snapshot>]
92
+ # Yobi::Snapshot instances removed by the policy.
100
93
  def remove
101
94
  @remove ||= (self["remove"] || []).map { |entry| Yobi::Snapshot.new(entry) }
102
95
  end
103
96
 
104
- # @return [Array<Yobi::KeepReason>] one per kept snapshot, explaining which policy rule(s) kept it
97
+ # One Yobi::KeepReason per kept snapshot, explaining which policy
98
+ # rule(s) kept it.
105
99
  def reasons
106
100
  @reasons ||= (self["reasons"] || []).map { |entry| Yobi::KeepReason.new(entry) }
107
101
  end
108
102
  end
109
103
 
110
- # Why one snapshot survived a {Yobi::Repository#forget} run.
104
+ # Why one snapshot survived a Yobi::Repository#forget run.
111
105
  class KeepReason < Yobi::FancyHash
112
- # @return [Yobi::Snapshot]
106
+ # The Yobi::Snapshot that was kept.
113
107
  def snapshot
114
108
  @snapshot ||= Yobi::Snapshot.new(self["snapshot"])
115
109
  end
116
110
 
117
- # @return [Array<String>] Restic's own human-readable rule-match strings, e.g. `"last snapshot"`
111
+ # Restic's own human-readable rule-match strings, e.g. +"last snapshot"+.
118
112
  def matches
119
113
  @matches ||= self["matches"] || []
120
114
  end
@@ -1,21 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
-
5
3
  module Yobi
6
4
  class Repository
7
- # `restic init`: creates the repository at {#url}.
5
+ # +restic init+: creates the repository at #url.
6
+ #
7
+ # +copy_chunker_params: true+ copies chunker parameters from +from_repo:+
8
+ # (so a later #copy between the two can deduplicate). +from_repo:+ is
9
+ # the source repository's URL, a Yobi::Repository (its own #url/#password
10
+ # are used automatically), or a +[:file, "..."]+ tuple reading the URL
11
+ # from a file. +from_password:+ takes the same shape as #initialize's
12
+ # +password:+, and defaults to the source Repository's own password
13
+ # when +from_repo:+ is one.
8
14
  #
9
- # @param copy_chunker_params [Boolean] copy chunker parameters from `from_repo:`
10
- # @param from_key_hint [String, nil]
11
- # @param from_password [String, Array, Symbol, #call, nil] the source
12
- # repository's password (when copying chunker params from one), same
13
- # shape as {#initialize}'s `password:`
14
- # @param from_repo [String, Yobi::Repository, Array, nil] the source repository's
15
- # URL; a `Repository` instance (its own `#url`/`#password` are used
16
- # automatically); or a `[:file, "..."]` tuple reading the URL from a file
17
- # @param repository_version [String, nil]
18
- # @return [Yobi::Initialized]
15
+ # Returns a Yobi::Initialized.
19
16
  def init(copy_chunker_params: false, from_key_hint: nil, from_password: nil, from_repo: nil, repository_version: nil)
20
17
  from_repo_flag, from_repo_value = resolved_from_repo_flag(from_repo) unless from_repo.nil?
21
18
 
@@ -33,22 +30,18 @@ module Yobi
33
30
  end
34
31
 
35
32
  execution = run_restic(argv, extra_env: password_env(from_password, "RESTIC_FROM_PASSWORD"))
36
- Initialized.new(JSON.parse(execution[:output].to_s))
33
+ Initialized.new(parse_json_output(execution))
37
34
  end
38
35
 
39
- # Constructs a new {Yobi::Repository} at `url`, already initialized with
40
- # chunker parameters copied from this one (`{#init}(copy_chunker_params:
41
- # true, from_repo: self)`), so a later {#copy} between the two can
36
+ # Constructs a new Yobi::Repository at +url+, already initialized with
37
+ # chunker parameters copied from this one (+#init(copy_chunker_params:
38
+ # true, from_repo: self)+), so a later #copy between the two can
42
39
  # deduplicate. A one-time setup call, not an ongoing sync: nothing keeps
43
40
  # the two repositories in sync afterward, and this doesn't mutate this
44
- # `Repository` instance or copy any snapshots itself.
41
+ # Repository instance or copy any snapshots itself.
45
42
  #
46
- # @param url [String] the new repository's location
47
- # @param password [String, Array, Symbol, #call] the new repository's own password
48
- # @param backend_credentials [Hash{String => String}, #call] the new repository's own backend credentials
49
- # @param restic [Yobi::Restic, String, nil] a `Restic` instance to share, defaults to this repository's own
50
- # @param repository_version [String, nil]
51
- # @return [Yobi::Repository] the newly initialized mirror
43
+ # +url:+, +password:+, and +backend_credentials:+ take the same shapes
44
+ # as #initialize. +restic:+ defaults to this repository's own.
52
45
  def init_mirror(url:, password:, backend_credentials: {}, restic: @restic, repository_version: nil)
53
46
  Repository.new(url: url, password: password, backend_credentials: backend_credentials, restic: restic).tap do |mirror|
54
47
  mirror.init(copy_chunker_params: true, from_repo: self, repository_version: repository_version)
@@ -56,14 +49,14 @@ module Yobi
56
49
  end
57
50
  end
58
51
 
59
- # The `"initialized"` message from an {Yobi::Repository#init} run.
52
+ # The +"initialized"+ message from a Yobi::Repository#init run.
60
53
  class Initialized < Yobi::FancyHash
61
- # @return [String] ID of the created repository
54
+ # ID of the created repository.
62
55
  def id
63
56
  self["id"]
64
57
  end
65
58
 
66
- # @return [String] URL of the repository
59
+ # URL of the repository.
67
60
  def repository
68
61
  self["repository"]
69
62
  end