yobi 0.3.1 → 1.1.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +17 -0
  4. data/README.md +83 -44
  5. data/lib/yobi/argv_builder.rb +1 -35
  6. data/lib/yobi/cancellable_proxy.rb +48 -0
  7. data/lib/yobi/cancellation.rb +97 -0
  8. data/lib/yobi/errors.rb +45 -30
  9. data/lib/yobi/fancy_hash.rb +1 -5
  10. data/lib/yobi/io_handle.rb +9 -17
  11. data/lib/yobi/mount_handle.rb +7 -11
  12. data/lib/yobi/repository/backup.rb +59 -90
  13. data/lib/yobi/repository/cat.rb +23 -43
  14. data/lib/yobi/repository/check.rb +21 -34
  15. data/lib/yobi/repository/copy.rb +11 -13
  16. data/lib/yobi/repository/diff.rb +18 -49
  17. data/lib/yobi/repository/dump.rb +14 -15
  18. data/lib/yobi/repository/find.rb +17 -37
  19. data/lib/yobi/repository/forget.rb +35 -39
  20. data/lib/yobi/repository/init.rb +19 -24
  21. data/lib/yobi/repository/key.rb +18 -33
  22. data/lib/yobi/repository/list.rb +5 -6
  23. data/lib/yobi/repository/ls.rb +20 -43
  24. data/lib/yobi/repository/migrate.rb +6 -6
  25. data/lib/yobi/repository/mount.rb +22 -32
  26. data/lib/yobi/repository/prune.rb +11 -9
  27. data/lib/yobi/repository/recover.rb +2 -4
  28. data/lib/yobi/repository/repair.rb +16 -23
  29. data/lib/yobi/repository/restore.rb +31 -54
  30. data/lib/yobi/repository/rewrite.rb +15 -20
  31. data/lib/yobi/repository/snapshots.rb +7 -8
  32. data/lib/yobi/repository/stats.rb +26 -21
  33. data/lib/yobi/repository/tag.rb +23 -33
  34. data/lib/yobi/repository/unlock.rb +2 -4
  35. data/lib/yobi/repository.rb +96 -25
  36. data/lib/yobi/restic.rb +93 -114
  37. data/lib/yobi/restic_output.rb +2 -74
  38. data/lib/yobi/snapshot.rb +15 -27
  39. data/lib/yobi/version.rb +1 -2
  40. data/lib/yobi.rb +2 -0
  41. data/sig/yobi.rbs +83 -6
  42. metadata +5 -2
@@ -2,12 +2,10 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic repair index`: creates a new index based on the pack files
5
+ # +restic repair index+: creates a new index based on the pack files
6
6
  # present in this repository. Successor to the deprecated
7
- # `rebuild-index` command.
8
- #
9
- # @param read_all_packs [Boolean] read every pack file fully instead of just headers
10
- # @return [true]
7
+ # +rebuild-index+ command. +read_all_packs: true+ reads every pack file
8
+ # fully instead of just headers.
11
9
  def repair_index(read_all_packs: false)
12
10
  argv = build_argv("repair", "index") do |a|
13
11
  a.flag(:read_all_packs) if read_all_packs
@@ -16,34 +14,29 @@ module Yobi
16
14
  true
17
15
  end
18
16
 
19
- # `restic repair packs`: extracts intact blobs from the given pack
20
- # files, rebuilds the index to drop the damaged packs, and removes
17
+ # +restic repair packs+: extracts intact blobs from the given pack
18
+ # +ids:+, rebuilds the index to drop the damaged packs, and removes
21
19
  # them from the repository. Restic also writes a backup copy of each
22
- # given pack file (named `pack-<id>`) into the calling process's
20
+ # given pack file (named +pack-<id>+) into the calling process's
23
21
  # current working directory before removing it. There's no flag to
24
22
  # disable this.
25
- #
26
- # @param ids [Array<String>, String] pack IDs
27
- # @return [true]
28
23
  def repair_packs(ids:)
29
24
  argv = build_argv("repair", "packs", ids)
30
25
  run_restic(argv)
31
26
  true
32
27
  end
33
28
 
34
- # `restic repair snapshots`: scans the given snapshots and generates
35
- # new ones with damaged directories/file contents removed. This
36
- # causes data loss for the content actually removed; prefer a fresh
37
- # {Yobi::Repository#backup} where the source data is still available.
38
- # Depends on a correct index. Call {#repair_index} first.
29
+ # +restic repair snapshots+: scans the given snapshots and generates
30
+ # new ones with damaged directories/file contents removed. This causes
31
+ # data loss for the content actually removed; prefer a fresh
32
+ # Yobi::Repository#backup where the source data is still available.
33
+ # Depends on a correct index - call #repair_index first.
39
34
  #
40
- # @param snapshot_ids [Array<String>, String] snapshots to repair; all of them if empty
41
- # @param dry_run [Boolean] report what would happen without doing it
42
- # @param forget [Boolean] remove the original damaged snapshots after repairing
43
- # @param hosts [Array<String>, String] filter by hostname(s)
44
- # @param paths [Array<String>, String] filter by originally backed-up path(s)
45
- # @param tags [Array<String>, String] filter by tag(s)
46
- # @return [true]
35
+ # +snapshot_ids:+ names the snapshots to repair, or all of them if
36
+ # empty. +dry_run:+ reports what would happen without doing it.
37
+ # +forget:+ removes the original damaged snapshots after repairing.
38
+ # +hosts:+, +paths:+, and +tags:+ each accept a single value or an
39
+ # Array to filter by.
47
40
  def repair_snapshots(snapshot_ids: [], dry_run: false, forget: false, hosts: [], paths: [], tags: [])
48
41
  argv = build_argv("repair", "snapshots", snapshot_ids) do |a|
49
42
  a.flag(:dry_run) if dry_run
@@ -2,32 +2,28 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic restore`: extracts a snapshot's contents to a target directory.
5
+ # +restic restore+: extracts a snapshot's contents to a target directory.
6
6
  #
7
- # @param snapshot_id [String] also accepts `"latest"`
8
- # @param target [String] directory to restore into
9
- # @param excludes [Array<String>, String] exclude files matching these glob patterns
10
- # @param exclude_files [Array<String>, String] path(s) to file(s) listing exclude patterns
11
- # @param exclude_xattrs [Array<String>, String] exclude extended attributes matching these pattern(s)
12
- # @param hosts [Array<String>, String] only relevant when `snapshot_id:` is `"latest"`
13
- # @param iexcludes [Array<String>, String] like `excludes`, case-insensitive
14
- # @param iexclude_files [Array<String>, String] like `exclude_files`, case-insensitive
15
- # @param iincludes [Array<String>, String] like `includes`, case-insensitive
16
- # @param iinclude_files [Array<String>, String] like `include_files`, case-insensitive
17
- # @param includes [Array<String>, String] only restore files matching these glob patterns
18
- # @param include_files [Array<String>, String] path(s) to file(s) listing include patterns
19
- # @param include_xattrs [Array<String>, String] only restore extended attributes matching these pattern(s)
20
- # @param paths [Array<String>, String] only relevant when `snapshot_id:` is `"latest"`
21
- # @param tags [Array<String>, String] only relevant when `snapshot_id:` is `"latest"`
22
- # @param delete [Boolean] delete files in `target:` not present in the snapshot
23
- # @param dry_run [Boolean] report what would happen without doing it
24
- # @param overwrite [String, Symbol, nil] `"always"`, `"if-changed"`, or `"if-newer"`
25
- # @param ownership_by_name [Boolean] map ownership by user/group name instead of numeric ID
26
- # @param sparse [Boolean] write sparse files
27
- # @param verbose [Boolean] stream a {Yobi::RestoreVerboseStatus} per file to the block
28
- # @param verify [Boolean] verify restored file content against the repository
29
- # @yieldparam message [Yobi::RestoreStatus, Yobi::RestoreVerboseStatus]
30
- # @return [Yobi::RestoreOutcome]
7
+ # +snapshot_id:+ is a snapshot ID or +"latest"+. +target:+ is the
8
+ # directory to restore into.
9
+ #
10
+ # +excludes:+, +exclude_files:+, +exclude_xattrs:+, +includes:+,
11
+ # +include_files:+, +include_xattrs:+ and their +i+-prefixed
12
+ # (case-insensitive) variants each accept a single value or an Array.
13
+ #
14
+ # +hosts:+, +paths:+ and +tags:+ each accept a single value or an
15
+ # Array and are only relevant when +snapshot_id+ is +"latest"+.
16
+ #
17
+ # +delete:+ deletes files in +target+ not present in the snapshot.
18
+ # +dry_run:+ reports what would happen without doing it.
19
+ # +overwrite:+ is +"always"+, +"if-changed"+, or +"if-newer"+.
20
+ # +ownership_by_name:+ maps ownership by user/group name instead of
21
+ # numeric ID. +sparse:+ writes sparse files. +verify:+ verifies restored
22
+ # file content against the repository.
23
+ #
24
+ # When +verbose: true+, a Yobi::RestoreVerboseStatus is streamed to the
25
+ # block per file. The block also receives Yobi::RestoreStatus messages
26
+ # as they arrive. Returns a Yobi::RestoreOutcome.
31
27
  def restore(snapshot_id:, target:, excludes: [], exclude_files: [], exclude_xattrs: [], hosts: [],
32
28
  iexcludes: [], iexclude_files: [], iincludes: [], iinclude_files: [], includes: [], include_files: [],
33
29
  include_xattrs: [], paths: [], tags: [], delete: false, dry_run: false, overwrite: nil,
@@ -58,7 +54,7 @@ module Yobi
58
54
 
59
55
  output = Yobi::ResticOutput.new(transform: Yobi::RestoreMessageWrapper)
60
56
  # Restic's own restore summary isn't meant for the live block - only
61
- # {RestoreOutcome#summary}'s post-hoc access sees it - but it shares
57
+ # RestoreOutcome#summary's post-hoc access sees it - but it shares
62
58
  # the same @transform (needed there since it isn't a status/verbose_status
63
59
  # message), so it has to be filtered back out here instead.
64
60
  live_block = block && proc { |message| block.call(message) if message.is_a?(RestoreStatus) || message.is_a?(RestoreVerboseStatus) }
@@ -67,16 +63,7 @@ module Yobi
67
63
  end
68
64
  end
69
65
 
70
- # Classifies a raw restore message Hash by its own message_type, wrapping
71
- # it in the matching typed class. Used both by {Yobi::Repository#restore}
72
- # (as the {Yobi::ResticOutput} `transform:` for a live streaming run) and
73
- # by {Yobi::RestoreOutcome}'s own post-hoc accessors.
74
- #
75
- # @private
76
- module RestoreMessageWrapper
77
- # @param raw [Hash]
78
- # @return [Yobi::RestoreStatus, Yobi::RestoreVerboseStatus, Hash]
79
- # the raw Hash itself for a message_type this version of Yobi doesn't recognize
66
+ module RestoreMessageWrapper # :nodoc:
80
67
  def self.call(raw)
81
68
  case raw["message_type"]
82
69
  when "status" then RestoreStatus.new(raw)
@@ -86,23 +73,21 @@ module Yobi
86
73
  end
87
74
  end
88
75
 
89
- # The outcome of one {Yobi::Repository#restore} call.
76
+ # The outcome of one Yobi::Repository#restore call.
90
77
  class RestoreOutcome
91
- # @return [Yobi::ResticOutput]
78
+ # The Yobi::ResticOutput backing this outcome.
92
79
  attr_reader :output
93
80
 
94
- # @private
95
- def initialize(execution)
81
+ def initialize(execution) # :nodoc:
96
82
  @output = execution[:output]
97
83
  end
98
84
 
99
- # @return [Hash] Restic's own `"summary"` fields (`"total_files"`, `"files_restored"`, ...)
85
+ # Restic's own +"summary"+ fields (+"total_files"+, +"files_restored"+, ...).
100
86
  def summary
101
87
  @summary ||= output.messages("summary").first || {}
102
88
  end
103
89
  alias_method :report, :summary
104
90
 
105
- # @return [void]
106
91
  def pretty_print(q)
107
92
  q.object_group(self) do
108
93
  q.breakable
@@ -111,49 +96,41 @@ module Yobi
111
96
  end
112
97
  end
113
98
 
114
- # One `"status"` message from a live restore run.
115
- # https://restic.readthedocs.io/en/stable/075_scripting.html#restore
99
+ # One +"status"+ message from a live restore run. See
100
+ # https://restic.readthedocs.io/en/stable/075_scripting.html#restore.
116
101
  class RestoreStatus < Yobi::FancyHash
117
- # @return [Float, nil]
118
102
  def percent_done
119
103
  self["percent_done"]
120
104
  end
121
105
 
122
- # @return [Integer, nil]
123
106
  def total_files
124
107
  self["total_files"]
125
108
  end
126
109
 
127
- # @return [Integer, nil]
128
110
  def files_restored
129
111
  self["files_restored"]
130
112
  end
131
113
 
132
- # @return [Integer, nil]
133
114
  def total_bytes
134
115
  self["total_bytes"]
135
116
  end
136
117
 
137
- # @return [Integer, nil]
138
118
  def bytes_restored
139
119
  self["bytes_restored"]
140
120
  end
141
121
  end
142
122
 
143
- # One `"verbose_status"` message from a restore run, one per file. Only
144
- # emitted when `verbose: true` is passed to {Yobi::Repository#restore}.
123
+ # One +"verbose_status"+ message from a restore run, one per file. Only
124
+ # emitted when +verbose: true+ is passed to Yobi::Repository#restore.
145
125
  class RestoreVerboseStatus < Yobi::FancyHash
146
- # @return [String]
147
126
  def action
148
127
  self["action"]
149
128
  end
150
129
 
151
- # @return [String]
152
130
  def item
153
131
  self["item"]
154
132
  end
155
133
 
156
- # @return [Integer]
157
134
  def size
158
135
  self["size"]
159
136
  end
@@ -2,29 +2,24 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic rewrite`: creates new snapshots from existing ones with
5
+ # +restic rewrite+: creates new snapshots from existing ones with
6
6
  # exclude/include filters applied, or metadata changed. With
7
- # `snapshot_ids:` and the other filters all left at their defaults,
7
+ # +snapshot_ids:+ and the other filters all left at their defaults,
8
8
  # rewrites every snapshot in the repository.
9
9
  #
10
- # @param snapshot_ids [Array<String>, String] snapshots to rewrite; all of them if empty
11
- # @param hosts [Array<String>, String] filter by hostname(s)
12
- # @param tags [Array<String>, String] filter by tag(s)
13
- # @param paths [Array<String>, String] filter by originally backed-up path(s)
14
- # @param excludes [Array<String>, String] exclude files matching these glob patterns
15
- # @param exclude_files [Array<String>, String] path(s) to file(s) listing exclude patterns
16
- # @param iexcludes [Array<String>, String] like `excludes`, case-insensitive
17
- # @param iexclude_files [Array<String>, String] like `exclude_files`, case-insensitive
18
- # @param includes [Array<String>, String] only include files matching these glob patterns
19
- # @param include_files [Array<String>, String] path(s) to file(s) listing include patterns
20
- # @param iincludes [Array<String>, String] like `includes`, case-insensitive
21
- # @param iinclude_files [Array<String>, String] like `include_files`, case-insensitive
22
- # @param dry_run [Boolean] report what would happen without doing it
23
- # @param forget [Boolean] remove the original snapshots afterward, instead of tagging the new ones `"rewrite"` and keeping both
24
- # @param new_host [String, nil] change the recorded hostname
25
- # @param new_time [String, nil] change the recorded timestamp
26
- # @param snapshot_summary [Boolean] regenerate the snapshot summary
27
- # @return [true]
10
+ # +snapshot_ids:+ names the snapshots to rewrite, or all of them if
11
+ # empty. +hosts:+, +tags:+, and +paths:+ each accept a single value or
12
+ # an Array to filter by.
13
+ #
14
+ # +excludes:+, +exclude_files:+, +includes:+, +include_files:+ and
15
+ # their +i+-prefixed (case-insensitive) variants each accept a single
16
+ # value or an Array.
17
+ #
18
+ # +dry_run:+ reports what would happen without doing it. +forget:+
19
+ # removes the original snapshots afterward, instead of tagging the new
20
+ # ones +"rewrite"+ and keeping both. +new_host:+ / +new_time:+ change
21
+ # the recorded hostname/timestamp. +snapshot_summary:+ regenerates the
22
+ # snapshot summary.
28
23
  def rewrite(snapshot_ids: [], hosts: [], tags: [], paths: [], excludes: [], exclude_files: [],
29
24
  iexcludes: [], iexclude_files: [], includes: [], include_files: [], iincludes: [], iinclude_files: [],
30
25
  dry_run: false, forget: false, new_host: nil, new_time: nil, snapshot_summary: false)
@@ -4,15 +4,14 @@ require "json"
4
4
 
5
5
  module Yobi
6
6
  class Repository
7
- # `restic snapshots`: lists snapshots, optionally filtered.
7
+ # +restic snapshots+: lists snapshots, optionally filtered.
8
8
  #
9
- # @param tags [Array<String>, String] filter by tag(s)
10
- # @param hosts [Array<String>, String] filter by hostname(s)
11
- # @param paths [Array<String>, String] filter by originally backed-up path(s)
12
- # @param compact [Boolean] compact the printed listing
13
- # @param group_by [String, nil] group results, e.g. `"host"`
14
- # @param latest [Integer, nil] limit to the N most recent per group
15
- # @return [Array<Yobi::Snapshot>]
9
+ # +tags:+, +hosts:+, and +paths:+ each accept a single value or an
10
+ # Array to filter by. +compact:+ compacts the printed listing.
11
+ # +group_by:+ (e.g. +"host"+) groups results. +latest:+ limits to the
12
+ # N most recent per group.
13
+ #
14
+ # Returns an Array of Yobi::Snapshot.
16
15
  def snapshots(tags: [], hosts: [], paths: [], compact: false, group_by: nil, latest: nil)
17
16
  argv = build_argv("snapshots") do |a|
18
17
  a.repeat_flag(:tag, tags)
@@ -2,21 +2,23 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # @private
6
- STATS_MODES = %w[restore-size files-by-contents blobs-per-file raw-data].each_with_object({}) do |value, hash|
5
+ STATS_MODES = %w[restore-size files-by-contents blobs-per-file raw-data].each_with_object({}) do |value, hash| # :nodoc:
7
6
  hash[value] = value
8
7
  hash[value.tr("-", "_").to_sym] = value
9
8
  end.freeze
10
9
 
11
- # `restic stats`: accumulates statistics about the repository's data.
10
+ # +restic stats+: accumulates statistics about the repository's data.
12
11
  #
13
- # @param snapshot_ids [Array<String>, String] restrict to these snapshot ID(s); the whole repository if empty
14
- # @param hosts [Array<String>, String] filter by hostname(s)
15
- # @param mode [String, Symbol, nil] `"restore-size"`/`:restore_size` (default), `"files-by-contents"`/`:files_by_contents`, `"blobs-per-file"`/`:blobs_per_file`, or `"raw-data"`/`:raw_data`
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 [Yobi::RepositoryStats]
19
- # @raise [ArgumentError] if `mode:` isn't one of the values listed above
12
+ # +snapshot_ids:+ restricts to specific snapshot IDs, or the whole
13
+ # repository if empty. +hosts:+, +paths:+, and +tags:+ each accept a
14
+ # single value or an Array to filter by.
15
+ #
16
+ # +mode:+ is +"restore-size"+ / +:restore_size+ (default),
17
+ # +"files-by-contents"+ / +:files_by_contents+, +"blobs-per-file"+ /
18
+ # +:blobs_per_file+, or +"raw-data"+ / +:raw_data+. Anything else
19
+ # raises ArgumentError.
20
+ #
21
+ # Returns a Yobi::RepositoryStats.
20
22
  def stats(snapshot_ids: [], hosts: [], mode: nil, paths: [], tags: [])
21
23
  argv = build_argv("stats", snapshot_ids) do |a|
22
24
  a.repeat_flag(:host, hosts)
@@ -27,49 +29,52 @@ module Yobi
27
29
  a.repeat_flag(:tag, tags)
28
30
  end
29
31
  execution = run_restic(argv)
30
- RepositoryStats.new(parse_json_output(execution))
32
+ # Permissive because Restic 0.19.0 prints a "[0:00] 100.00% ..."
33
+ # progress line before the JSON summary. Fixed in 0.19.1.
34
+ RepositoryStats.new(parse_json_output_permissively(execution))
31
35
  end
32
36
  end
33
37
 
34
- # The result of one {Yobi::Repository#stats} call.
35
- # https://restic.readthedocs.io/en/stable/075_scripting.html#stats
38
+ # The result of one Yobi::Repository#stats call. See
39
+ # https://restic.readthedocs.io/en/stable/075_scripting.html#stats.
36
40
  class RepositoryStats < Yobi::FancyHash
37
- # @return [Integer] repository size in bytes
41
+ # Repository size in bytes.
38
42
  def total_size
39
43
  self["total_size"] || 0
40
44
  end
41
45
 
42
- # @return [Integer] number of files backed up in the repository
46
+ # Number of files backed up in the repository.
43
47
  def total_file_count
44
48
  self["total_file_count"] || 0
45
49
  end
46
50
 
47
- # @return [Integer] number of blobs in the repository
51
+ # Number of blobs in the repository.
48
52
  def total_blob_count
49
53
  self["total_blob_count"] || 0
50
54
  end
51
55
 
52
- # @return [Integer] number of processed snapshots
56
+ # Number of processed snapshots.
53
57
  def snapshots_count
54
58
  self["snapshots_count"] || 0
55
59
  end
56
60
 
57
- # @return [Integer] repository size in bytes if blobs were uncompressed
61
+ # Repository size in bytes if blobs were uncompressed.
58
62
  def total_uncompressed_size
59
63
  self["total_uncompressed_size"] || 0
60
64
  end
61
65
 
62
- # @return [Float] factor by which the already compressed data has shrunk due to compression
66
+ # Factor by which the already compressed data has shrunk due to
67
+ # compression.
63
68
  def compression_ratio
64
69
  self["compression_ratio"] || 0.0
65
70
  end
66
71
 
67
- # @return [Float] percentage of already compressed data
72
+ # Percentage of already compressed data.
68
73
  def compression_progress
69
74
  self["compression_progress"] || 0.0
70
75
  end
71
76
 
72
- # @return [Float] overall space saving due to compression
77
+ # Overall space saving due to compression.
73
78
  def compression_space_saving
74
79
  self["compression_space_saving"] || 0.0
75
80
  end
@@ -2,18 +2,22 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic tag`: modifies tags on existing snapshots. Tags are part of
6
- # a snapshot's content-addressed identity, so changing them produces a
5
+ # +restic tag+: modifies tags on existing snapshots. Tags are part of a
6
+ # snapshot's content-addressed identity, so changing them produces a
7
7
  # new snapshot ID for each affected snapshot.
8
8
  #
9
- # @param snapshot_ids [Array<String>, String] snapshots to modify; all matching `hosts:`/`paths:` if empty
10
- # @param add [Array<String>, String] tags to add, keeping existing ones
11
- # @param remove [Array<String>, String] tags to remove
12
- # @param set [Array<String>, String] replace all tags with exactly this set (exclusive with `add:`/`remove:`)
13
- # @param tags [Array<String>, String] filter which snapshots to modify, by their current tags
14
- # @param hosts [Array<String>, String] filter which snapshots to modify, by hostname
15
- # @param paths [Array<String>, String] filter which snapshots to modify, by originally backed-up path
16
- # @return [Yobi::TagOutcome]
9
+ # +snapshot_ids:+ names the snapshots to modify, or all matching
10
+ # +hosts:+/+paths:+ if empty.
11
+ #
12
+ # +add:+ appends tags, keeping existing ones. +remove:+ strips tags.
13
+ # +set:+ replaces all tags with exactly the given set (exclusive with
14
+ # +add:+/+remove:+).
15
+ #
16
+ # +tags:+, +hosts:+, and +paths:+ each accept a single value or an
17
+ # Array to filter which snapshots to modify (by current tags,
18
+ # hostname, or originally backed-up path respectively).
19
+ #
20
+ # Returns a Yobi::TagOutcome.
17
21
  def tag(snapshot_ids: [], add: [], remove: [], set: [], tags: [], hosts: [], paths: [])
18
22
  argv = build_argv("tag", snapshot_ids) do |a|
19
23
  a.repeat_flag(:add, add)
@@ -29,16 +33,7 @@ module Yobi
29
33
  end
30
34
  end
31
35
 
32
- # Classifies a raw tag message Hash by its own message_type, wrapping it
33
- # in the matching typed class. Used both as the {Yobi::ResticOutput}
34
- # `transform:` for {Yobi::Repository#tag} and by {Yobi::TagOutcome}'s own
35
- # post-hoc accessors.
36
- #
37
- # @private
38
- module TagMessageWrapper
39
- # @param raw [Hash]
40
- # @return [Yobi::TagChange, Yobi::TagSummary, Hash]
41
- # the raw Hash itself for a message_type this version of Yobi doesn't recognize
36
+ module TagMessageWrapper # :nodoc:
42
37
  def self.call(raw)
43
38
  case raw["message_type"]
44
39
  when "changed" then TagChange.new(raw)
@@ -48,28 +43,26 @@ module Yobi
48
43
  end
49
44
  end
50
45
 
51
- # The outcome of one {Yobi::Repository#tag} call.
46
+ # The outcome of one Yobi::Repository#tag call.
52
47
  class TagOutcome
53
- # @return [Yobi::ResticOutput]
48
+ # The Yobi::ResticOutput backing this outcome.
54
49
  attr_reader :output
55
50
 
56
- # @private
57
- def initialize(execution)
51
+ def initialize(execution) # :nodoc:
58
52
  @output = execution[:output]
59
53
  end
60
54
 
61
- # @return [Yobi::TagSummary] Restic's own `"summary"` fields (`"changed_snapshots"`)
55
+ # The Yobi::TagSummary of Restic's own +"summary"+ fields.
62
56
  def summary
63
57
  @summary ||= output.messages("summary").first || TagSummary.new({})
64
58
  end
65
59
  alias_method :report, :summary
66
60
 
67
- # @return [Enumerable<Yobi::TagChange>] one per snapshot actually modified
61
+ # One Yobi::TagChange per snapshot actually modified.
68
62
  def changes
69
63
  @changes ||= output.messages("changed")
70
64
  end
71
65
 
72
- # @return [void]
73
66
  def pretty_print(q)
74
67
  q.object_group(self) do
75
68
  q.breakable
@@ -78,25 +71,22 @@ module Yobi
78
71
  end
79
72
  end
80
73
 
81
- # One `"changed"` message from a tag run. `old_snapshot_id` is now stale:
74
+ # One +"changed"+ message from a tag run. #old_snapshot_id is now stale:
82
75
  # tags are part of a snapshot's content-addressed identity, so changing
83
76
  # them produces a new snapshot ID.
84
77
  class TagChange < Yobi::FancyHash
85
- # @return [String]
86
78
  def old_snapshot_id
87
79
  self["old_snapshot_id"]
88
80
  end
89
81
 
90
- # @return [String]
91
82
  def new_snapshot_id
92
83
  self["new_snapshot_id"]
93
84
  end
94
85
  end
95
86
 
96
- # The `"summary"` message from a tag run, the final result once the
97
- # command finishes. Dispatched to {Yobi::TagOutcome#summary}.
87
+ # The +"summary"+ message from a tag run, the final result once the
88
+ # command finishes. Dispatched to Yobi::TagOutcome#summary.
98
89
  class TagSummary < Yobi::FancyHash
99
- # @return [Integer]
100
90
  def changed_snapshots
101
91
  self["changed_snapshots"] || 0
102
92
  end
@@ -2,10 +2,8 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic unlock`: removes stale locks left by other Restic processes.
6
- #
7
- # @param remove_all [Boolean] remove every lock, not just stale ones
8
- # @return [true]
5
+ # +restic unlock+: removes stale locks left by other Restic processes.
6
+ # +remove_all: true+ removes every lock, not just stale ones.
9
7
  def unlock(remove_all: false)
10
8
  argv = build_argv("unlock") do |a|
11
9
  a.flag(:remove_all) if remove_all