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,36 +2,30 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic forget`: applies a retention policy, removing snapshots
6
- # 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
7
  #
8
- # @param keep_last [Integer, nil] always keep this many of the most recent snapshots
9
- # @param keep_hourly [Integer, nil]
10
- # @param keep_daily [Integer, nil]
11
- # @param keep_weekly [Integer, nil]
12
- # @param keep_monthly [Integer, nil]
13
- # @param keep_yearly [Integer, nil]
14
- # @param keep_within [String, nil] keep all snapshots within this duration, e.g. `"30d"`
15
- # @param keep_within_hourly [String, nil]
16
- # @param keep_within_daily [String, nil]
17
- # @param keep_within_weekly [String, nil]
18
- # @param keep_within_monthly [String, nil]
19
- # @param keep_within_yearly [String, nil]
20
- # @param keep_tags [Array<String>, String] always keep snapshots carrying any of these tags
21
- # @param hosts [Array<String>, String] filter by hostname(s)
22
- # @param tags [Array<String>, String] filter by tag(s)
23
- # @param paths [Array<String>, String] filter by originally backed-up path(s)
24
- # @param compact [Boolean] compact the printed policy summary
25
- # @param group_by [String, nil] grouping used to apply the policy, e.g. `"host,paths"`
26
- # @param dry_run [Boolean] report what would happen without doing it
27
- # @param prune [Boolean] also reclaim disk space (equivalent to `restic forget --prune`)
28
- # @param unsafe_allow_remove_all [Boolean] allow removing every snapshot
29
- # @param max_unused [String, nil] passed through to the implied prune, e.g. `"10%"`
30
- # @param max_repack_size [String, nil] passed through to the implied prune
31
- # @param repack_cacheable_only [Boolean] passed through to the implied prune
32
- # @param repack_uncompressed [Boolean] passed through to the implied prune
33
- # @param repack_smaller_than [String, nil] passed through to the implied prune
34
- # @return [Array<Yobi::ForgetGroup>]
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.
27
+ #
28
+ # Returns an Array of Yobi::ForgetGroup.
35
29
  def forget(keep_last: nil, keep_hourly: nil, keep_daily: nil, keep_weekly: nil, keep_monthly: nil,
36
30
  keep_yearly: nil, keep_within: nil, keep_within_hourly: nil, keep_within_daily: nil,
37
31
  keep_within_weekly: nil, keep_within_monthly: nil, keep_within_yearly: nil, keep_tags: [],
@@ -67,52 +61,54 @@ module Yobi
67
61
  a.flag(:repack_smaller_than, repack_smaller_than) unless repack_smaller_than.nil?
68
62
  end
69
63
  execution = run_restic(argv)
70
- parse_json_output(execution).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) }
71
69
  end
72
70
  end
73
71
 
74
72
  # One grouping Restic's forget policy was evaluated against (by default,
75
73
  # grouped by host+paths).
76
74
  class ForgetGroup < Yobi::FancyHash
77
- # @return [String]
78
75
  def host
79
76
  self["host"]
80
77
  end
81
78
 
82
- # @return [Array<String>]
83
79
  def tags
84
80
  @tags ||= self["tags"] || []
85
81
  end
86
82
 
87
- # @return [Array<String>]
88
83
  def paths
89
84
  self["paths"]
90
85
  end
91
86
 
92
- # @return [Array<Yobi::Snapshot>]
87
+ # Yobi::Snapshot instances kept by the policy.
93
88
  def keep
94
89
  @keep ||= (self["keep"] || []).map { |entry| Yobi::Snapshot.new(entry) }
95
90
  end
96
91
 
97
- # @return [Array<Yobi::Snapshot>]
92
+ # Yobi::Snapshot instances removed by the policy.
98
93
  def remove
99
94
  @remove ||= (self["remove"] || []).map { |entry| Yobi::Snapshot.new(entry) }
100
95
  end
101
96
 
102
- # @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.
103
99
  def reasons
104
100
  @reasons ||= (self["reasons"] || []).map { |entry| Yobi::KeepReason.new(entry) }
105
101
  end
106
102
  end
107
103
 
108
- # Why one snapshot survived a {Yobi::Repository#forget} run.
104
+ # Why one snapshot survived a Yobi::Repository#forget run.
109
105
  class KeepReason < Yobi::FancyHash
110
- # @return [Yobi::Snapshot]
106
+ # The Yobi::Snapshot that was kept.
111
107
  def snapshot
112
108
  @snapshot ||= Yobi::Snapshot.new(self["snapshot"])
113
109
  end
114
110
 
115
- # @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"+.
116
112
  def matches
117
113
  @matches ||= self["matches"] || []
118
114
  end
@@ -2,18 +2,17 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic init`: creates the repository at {#url}.
5
+ # +restic init+: creates the repository at #url.
6
6
  #
7
- # @param copy_chunker_params [Boolean] copy chunker parameters from `from_repo:`
8
- # @param from_key_hint [String, nil]
9
- # @param from_password [String, Array, Symbol, #call, nil] the source
10
- # repository's password (when copying chunker params from one), same
11
- # shape as {#initialize}'s `password:`
12
- # @param from_repo [String, Yobi::Repository, Array, nil] the source repository's
13
- # URL; a `Repository` instance (its own `#url`/`#password` are used
14
- # automatically); or a `[:file, "..."]` tuple reading the URL from a file
15
- # @param repository_version [String, nil]
16
- # @return [Yobi::Initialized]
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.
14
+ #
15
+ # Returns a Yobi::Initialized.
17
16
  def init(copy_chunker_params: false, from_key_hint: nil, from_password: nil, from_repo: nil, repository_version: nil)
18
17
  from_repo_flag, from_repo_value = resolved_from_repo_flag(from_repo) unless from_repo.nil?
19
18
 
@@ -34,19 +33,15 @@ module Yobi
34
33
  Initialized.new(parse_json_output(execution))
35
34
  end
36
35
 
37
- # Constructs a new {Yobi::Repository} at `url`, already initialized with
38
- # chunker parameters copied from this one (`{#init}(copy_chunker_params:
39
- # 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
40
39
  # deduplicate. A one-time setup call, not an ongoing sync: nothing keeps
41
40
  # the two repositories in sync afterward, and this doesn't mutate this
42
- # `Repository` instance or copy any snapshots itself.
41
+ # Repository instance or copy any snapshots itself.
43
42
  #
44
- # @param url [String] the new repository's location
45
- # @param password [String, Array, Symbol, #call] the new repository's own password
46
- # @param backend_credentials [Hash{String => String}, #call] the new repository's own backend credentials
47
- # @param restic [Yobi::Restic, String, nil] a `Restic` instance to share, defaults to this repository's own
48
- # @param repository_version [String, nil]
49
- # @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.
50
45
  def init_mirror(url:, password:, backend_credentials: {}, restic: @restic, repository_version: nil)
51
46
  Repository.new(url: url, password: password, backend_credentials: backend_credentials, restic: restic).tap do |mirror|
52
47
  mirror.init(copy_chunker_params: true, from_repo: self, repository_version: repository_version)
@@ -54,14 +49,14 @@ module Yobi
54
49
  end
55
50
  end
56
51
 
57
- # The `"initialized"` message from an {Yobi::Repository#init} run.
52
+ # The +"initialized"+ message from a Yobi::Repository#init run.
58
53
  class Initialized < Yobi::FancyHash
59
- # @return [String] ID of the created repository
54
+ # ID of the created repository.
60
55
  def id
61
56
  self["id"]
62
57
  end
63
58
 
64
- # @return [String] URL of the repository
59
+ # URL of the repository.
65
60
  def repository
66
61
  self["repository"]
67
62
  end
@@ -6,19 +6,17 @@ require "tempfile"
6
6
 
7
7
  module Yobi
8
8
  class Repository
9
- # `restic key add`: creates a new key (password) for this repository.
9
+ # +restic key add+: creates a new key (password) for this repository.
10
10
  #
11
- # @param host [String, nil] hostname to record on the new key
12
- # @param user [String, nil] username to record on the new key
13
- # @param new_password [String, Array, Symbol, #call] the new key's password,
14
- # accepting the same shapes as {#initialize}'s `password:` minus `[:command, ...]`: a
15
- # literal String; a `[:file, "..."]` tuple, resolved natively by Restic
16
- # itself; `:insecure_no_password`; or anything responding to `#call`. A
17
- # literal String or callable is written to a briefly-lived,
18
- # 0600-permissioned tempfile, since Restic itself only accepts a new
19
- # password by file or interactive prompt, unlike the repository's own
20
- # `password:`
21
- # @return [true]
11
+ # +new_password:+ accepts the same shapes as #initialize's +password:+
12
+ # minus +[:command, ...]+: a literal String; a +[:file, "..."]+ tuple,
13
+ # resolved natively by Restic itself; +:insecure_no_password+; or
14
+ # anything responding to +#call+. A literal String or callable is
15
+ # written to a briefly-lived, 0600-permissioned tempfile, since Restic
16
+ # itself only accepts a new password by file or interactive prompt.
17
+ #
18
+ # +host:+ and +user:+ optionally override the hostname/username
19
+ # recorded on the new key.
22
20
  def key_add(new_password:, host: nil, user: nil)
23
21
  with_new_password_flag(new_password) do |flag_name, flag_value|
24
22
  argv = build_argv("key", "add") do |a|
@@ -32,24 +30,19 @@ module Yobi
32
30
  end
33
31
  alias_method :add_key, :key_add
34
32
 
35
- # `restic key list`: lists every key (password) associated with this repository.
36
- #
37
- # @return [Array<Yobi::Key>]
33
+ # +restic key list+: every Yobi::Key associated with this repository.
38
34
  def key_list
39
35
  execution = run_restic(build_argv("key", "list"))
40
36
  parse_json_output(execution).map { |raw| Key.new(raw) }
41
37
  end
42
38
  alias_method :keys, :key_list
43
39
 
44
- # `restic key passwd`: creates a new key for this repository and
40
+ # +restic key passwd+: creates a new key for this repository and
45
41
  # removes the key currently in use. On success, also updates this
46
- # `Repository` instance's own `password:` to `new_password:`, so it
42
+ # Repository instance's own +password:+ to +new_password:+, so it
47
43
  # keeps working against this same repository afterward.
48
44
  #
49
- # @param host [String, nil] hostname to record on the new key
50
- # @param user [String, nil] username to record on the new key
51
- # @param new_password [String, Array, Symbol, #call] see {#key_add}
52
- # @return [true]
45
+ # +new_password:+, +host:+ and +user:+ take the same shapes as #key_add.
53
46
  def key_passwd(new_password:, host: nil, user: nil)
54
47
  with_new_password_flag(new_password) do |flag_name, flag_value|
55
48
  argv = build_argv("key", "passwd") do |a|
@@ -64,12 +57,8 @@ module Yobi
64
57
  end
65
58
  alias_method :change_password, :key_passwd
66
59
 
67
- # `restic key remove`: removes the given key ID from this repository.
68
- # Restic refuses to remove the key currently being used to access the
69
- # repository.
70
- #
71
- # @param id [String]
72
- # @return [true]
60
+ # +restic key remove+: removes the given key +id+ from this repository.
61
+ # Restic refuses to remove the key currently being used to access it.
73
62
  def key_remove(id:)
74
63
  run_restic(build_argv("key", "remove", id))
75
64
  true
@@ -105,29 +94,25 @@ module Yobi
105
94
  end
106
95
  end
107
96
 
108
- # One key (password) associated with the repository, from {Yobi::Repository#key_list}.
97
+ # One key (password) associated with the repository, from Yobi::Repository#key_list.
109
98
  class Key < Yobi::FancyHash
110
- # @return [String]
111
99
  def id
112
100
  self["id"]
113
101
  end
114
102
 
115
- # @return [String]
116
103
  def user_name
117
104
  self["userName"]
118
105
  end
119
106
 
120
- # @return [String]
121
107
  def host_name
122
108
  self["hostName"]
123
109
  end
124
110
 
125
- # @return [Boolean] whether this is the key currently in use
111
+ # Whether this is the key currently in use.
126
112
  def current?
127
113
  self["current"]
128
114
  end
129
115
 
130
- # @return [Time]
131
116
  def created
132
117
  @created ||= Time.parse(self["created"])
133
118
  end
@@ -2,12 +2,11 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic list`: lists every object ID of the given type in this
6
- # repository. Restic ignores `--json` for this command; each line is a
7
- # bare ID, except for `:blobs`, where each line is `"data <id>"` or `"tree <id>"`.
8
- #
9
- # @param type [Symbol, String] `:blobs`, `:packs`, `:index`, `:snapshots`, `:keys`, or `:locks`
10
- # @return [Array<String>]
5
+ # +restic list+: every object ID of the given +type+ in this repository.
6
+ # +type+ is +:blobs+, +:packs+, +:index+, +:snapshots+, +:keys+, or
7
+ # +:locks+. Restic ignores +--json+ for this command; each line is a
8
+ # bare ID, except for +:blobs+, where each line is +"data <id>"+ or
9
+ # +"tree <id>"+.
11
10
  def list(type)
12
11
  argv = build_argv("list", type)
13
12
  execution = run_restic(argv)
@@ -4,19 +4,18 @@ require "time"
4
4
 
5
5
  module Yobi
6
6
  class Repository
7
- # `restic ls`: lists a snapshot's files/directories.
7
+ # +restic ls+: lists a snapshot's files/directories.
8
8
  #
9
- # @param snapshot_id [String] also accepts `"latest"`
10
- # @param dirs [Array<String>, String] restrict to these directories within the snapshot
11
- # @param hosts [Array<String>, String] only relevant when `snapshot_id` is `"latest"`
12
- # @param human_readable [Boolean] format sizes for humans
13
- # @param long [Boolean] include long listing format
14
- # @param paths [Array<String>, String] only relevant when `snapshot_id` is `"latest"`
15
- # @param recursive [Boolean] recurse into subdirectories
16
- # @param reverse [Boolean] reverse sort order
17
- # @param sort [String, nil] sort key, e.g. `"size"`
18
- # @param tags [Array<String>, String] only relevant when `snapshot_id` is `"latest"`
19
- # @return [Yobi::LsOutcome]
9
+ # +snapshot_id:+ is a snapshot ID, or +"latest"+. +dirs:+ restricts the
10
+ # listing to specific directories within the snapshot.
11
+ #
12
+ # +hosts:+, +paths:+ and +tags:+ each accept a single value or an Array
13
+ # and are only relevant when +snapshot_id+ is +"latest"+.
14
+ #
15
+ # +recursive:+ recurses into subdirectories. +human_readable:+, +long:+,
16
+ # +reverse:+ and +sort:+ (e.g. +"size"+) toggle formatting/sort options.
17
+ #
18
+ # Returns a Yobi::LsOutcome.
20
19
  def ls(snapshot_id:, dirs: [], hosts: [], human_readable: false, long: false, paths: [],
21
20
  recursive: false, reverse: false, sort: nil, tags: [])
22
21
  argv = build_argv("ls", snapshot_id) do |a|
@@ -36,16 +35,7 @@ module Yobi
36
35
  end
37
36
  end
38
37
 
39
- # Classifies a raw ls message Hash by its own message_type, wrapping it
40
- # in the matching typed class. Used both as the {Yobi::ResticOutput}
41
- # `transform:` for {Yobi::Repository#ls} and by {Yobi::LsOutcome}'s own
42
- # post-hoc accessors.
43
- #
44
- # @private
45
- module LsMessageWrapper
46
- # @param raw [Hash]
47
- # @return [Yobi::Snapshot, Yobi::Node, Hash]
48
- # the raw Hash itself for a message_type this version of Yobi doesn't recognize
38
+ module LsMessageWrapper # :nodoc:
49
39
  def self.call(raw)
50
40
  case raw["message_type"]
51
41
  when "snapshot" then Snapshot.new(raw)
@@ -55,28 +45,26 @@ module Yobi
55
45
  end
56
46
  end
57
47
 
58
- # The outcome of one {Yobi::Repository#ls} call.
48
+ # The outcome of one Yobi::Repository#ls call.
59
49
  class LsOutcome
60
- # @return [Yobi::ResticOutput]
50
+ # The Yobi::ResticOutput backing this outcome.
61
51
  attr_reader :output
62
52
 
63
- # @private
64
- def initialize(execution)
53
+ def initialize(execution) # :nodoc:
65
54
  @output = execution[:output]
66
55
  end
67
56
 
68
- # @return [Yobi::Snapshot] the resolved snapshot, useful when `snapshot_id:` was `"latest"`
57
+ # The resolved Yobi::Snapshot, useful when +snapshot_id:+ was +"latest"+.
69
58
  def snapshot
70
59
  @snapshot ||= output.messages("snapshot").first
71
60
  end
72
61
 
73
- # @return [Enumerable<Yobi::Node>]
62
+ # Every Yobi::Node from the run.
74
63
  def nodes
75
64
  @nodes ||= output.messages("node")
76
65
  end
77
66
  alias_method :entries, :nodes
78
67
 
79
- # @return [void]
80
68
  def pretty_print(q)
81
69
  q.object_group(self) do
82
70
  q.breakable
@@ -87,61 +75,50 @@ module Yobi
87
75
  end
88
76
  end
89
77
 
90
- # One file/directory entry from a {Yobi::Repository#ls} call. Restic's
91
- # own term for this is a "node" - the same structure it uses internally
92
- # for every entry in a snapshot's tree, not just ones `ls` happens to show.
78
+ # One file/directory entry from a Yobi::Repository#ls call. Restic's own
79
+ # term for this is a "node" - the same structure it uses internally for
80
+ # every entry in a snapshot's tree, not just ones +ls+ happens to show.
93
81
  class Node < Yobi::FancyHash
94
- # @return [String]
95
82
  def name
96
83
  self["name"]
97
84
  end
98
85
 
99
- # @return [String]
100
86
  def type
101
87
  self["type"]
102
88
  end
103
89
 
104
- # @return [String]
105
90
  def path
106
91
  self["path"]
107
92
  end
108
93
 
109
- # @return [Integer]
110
94
  def size
111
95
  self["size"]
112
96
  end
113
97
 
114
- # @return [String]
115
98
  def permissions
116
99
  self["permissions"]
117
100
  end
118
101
 
119
- # @return [Integer]
120
102
  def uid
121
103
  self["uid"]
122
104
  end
123
105
 
124
- # @return [Integer]
125
106
  def gid
126
107
  self["gid"]
127
108
  end
128
109
 
129
- # @return [Integer]
130
110
  def inode
131
111
  self["inode"]
132
112
  end
133
113
 
134
- # @return [Time]
135
114
  def mtime
136
115
  @mtime ||= Time.parse(self["mtime"])
137
116
  end
138
117
 
139
- # @return [Time]
140
118
  def atime
141
119
  @atime ||= Time.parse(self["atime"])
142
120
  end
143
121
 
144
- # @return [Time]
145
122
  def ctime
146
123
  @ctime ||= Time.parse(self["ctime"])
147
124
  end
@@ -2,14 +2,14 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic migrate`: checks which migrations can be applied to this
6
- # repository, or applies the given ones. Restic ignores `--json` for
5
+ # +restic migrate+: checks which migrations can be applied to this
6
+ # repository, or applies the given ones. Restic ignores +--json+ for
7
7
  # this command, so there's no programmatic way to discover which
8
- # migrations are available. Run `restic migrate` directly for that.
8
+ # migrations are available - run +restic migrate+ directly for that.
9
9
  #
10
- # @param names [Array<String>, String] migration names to apply; only lists available ones if empty
11
- # @param force [Boolean] reapply a migration already marked as applied
12
- # @return [true]
10
+ # +names:+ names the migrations to apply, or only lists available ones
11
+ # if empty. +force: true+ reapplies a migration already marked as
12
+ # applied.
13
13
  def migrate(names: [], force: false)
14
14
  argv = build_argv("migrate", names) do |a|
15
15
  a.flag(:force) if force
@@ -4,27 +4,25 @@ require "open3"
4
4
 
5
5
  module Yobi
6
6
  class Repository
7
- # `restic mount`: serves this repository as a read-only FUSE
8
- # filesystem at `mountpoint`, which must already exist.
7
+ # +restic mount+: serves this repository as a read-only FUSE filesystem
8
+ # at +mountpoint:+, which must already exist.
9
9
  #
10
- # Without a block, returns a {Yobi::MountHandle} once Restic reports
11
- # itself ready; call `#stop` yourself once done. With one, yields the
12
- # `MountHandle` and stops it automatically once the block returns or raises.
10
+ # Without a block, returns a Yobi::MountHandle once Restic reports
11
+ # itself ready; call +#stop+ yourself once done. With one, yields the
12
+ # MountHandle and stops it automatically once the block returns or
13
+ # raises, returning the block's own value.
13
14
  #
14
- # @param mountpoint [String] must already exist
15
- # @param hosts [Array<String>, String] restrict which snapshots appear under `snapshots/`
16
- # @param paths [Array<String>, String] restrict which snapshots appear under `snapshots/`
17
- # @param tags [Array<String>, String] restrict which snapshots appear under `snapshots/`
18
- # @param allow_other [Boolean] allow other users to access the mount
19
- # @param no_default_permissions [Boolean] don't check file permissions
20
- # @param owner_root [Boolean] mount files as owned by root
21
- # @param path_templates [Array<String>, String] directory naming scheme(s) under `snapshots/`
22
- # @param time_template [String, nil] directory naming scheme for time-based paths
23
- # @param ready_timeout [Numeric] seconds to wait for Restic's readiness message before raising
24
- # @yieldparam mount [Yobi::MountHandle]
25
- # @return [Yobi::MountHandle] if no block is given
26
- # @return [Object] the block's own return value, otherwise
27
- # @raise [Yobi::MountTimeout]
15
+ # +hosts:+, +paths:+, and +tags:+ each accept a single value or an
16
+ # Array to restrict which snapshots appear under +snapshots/+.
17
+ #
18
+ # +allow_other:+ allows other users to access the mount.
19
+ # +no_default_permissions:+ skips file permission checks.
20
+ # +owner_root:+ mounts files as owned by root. +path_templates:+ and
21
+ # +time_template:+ control the directory naming schemes under
22
+ # +snapshots/+.
23
+ #
24
+ # +ready_timeout:+ is the number of seconds to wait for Restic's
25
+ # readiness message before raising Yobi::MountTimeout.
28
26
  def mount(mountpoint:, hosts: [], paths: [], tags: [], allow_other: false,
29
27
  no_default_permissions: false, owner_root: false, path_templates: [], time_template: nil,
30
28
  ready_timeout: 10)
@@ -58,20 +56,12 @@ module Yobi
58
56
  end
59
57
 
60
58
  class Restic
61
- # @private
62
- READY_LINE = "Now serving the repository at"
59
+ READY_LINE = "Now serving the repository at" # :nodoc:
63
60
 
64
- # For {Yobi::Repository#mount}. Spawns Restic and waits for its
65
- # readiness line (or `ready_timeout:` seconds, or Restic exiting on
66
- # its own) before returning.
67
- #
68
- # @param argv [Array<String>]
69
- # @param mountpoint [String]
70
- # @param extra_env [Hash{String => String}]
71
- # @param ready_timeout [Numeric]
72
- # @return [Yobi::MountHandle]
73
- # @raise [Yobi::MountTimeout]
74
- def run_mount(argv, mountpoint:, extra_env: {}, ready_timeout: 10)
61
+ # For Yobi::Repository#mount. Spawns Restic and waits for its readiness
62
+ # line (or +ready_timeout:+ seconds, or Restic exiting on its own)
63
+ # before returning a Yobi::MountHandle. Raises Yobi::MountTimeout.
64
+ def run_mount(argv, mountpoint:, extra_env: {}, ready_timeout: 10) # :nodoc:
75
65
  ensure_minimum_version!
76
66
  output = Yobi::ResticOutput.new
77
67
 
@@ -2,16 +2,18 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic prune`: removes data no longer referenced by any snapshot.
5
+ # +restic prune+: removes data no longer referenced by any snapshot.
6
6
  #
7
- # @param dry_run [Boolean] report what would happen without doing it
8
- # @param max_repack_size [String, nil] limit the amount of data repacked in this run
9
- # @param max_unused [String, nil] target maximum unused space after pruning, e.g. `"10%"`
10
- # @param repack_cacheable_only [Boolean] only repack packs that are cached locally
11
- # @param repack_smaller_than [String, nil] also repack packs smaller than this size
12
- # @param repack_uncompressed [Boolean] repack packs not yet using compression
13
- # @param unsafe_recover_no_free_space [String, nil] proceed even without enough free space, at the given risk acknowledgement
14
- # @return [true]
7
+ # +dry_run:+ reports what would happen without doing it. +max_unused:+
8
+ # targets a maximum unused space after pruning (e.g. +"10%"+).
9
+ # +max_repack_size:+ caps the amount of data repacked in one run.
10
+ #
11
+ # +repack_cacheable_only:+ only repacks packs cached locally.
12
+ # +repack_smaller_than:+ also repacks packs smaller than a given size.
13
+ # +repack_uncompressed:+ repacks packs not yet using compression.
14
+ #
15
+ # +unsafe_recover_no_free_space:+ proceeds even without enough free
16
+ # space, at the given risk acknowledgement string.
15
17
  def prune(dry_run: false, max_repack_size: nil, max_unused: nil, repack_cacheable_only: false,
16
18
  repack_smaller_than: nil, repack_uncompressed: false, unsafe_recover_no_free_space: nil)
17
19
  argv = build_argv("prune") do |a|
@@ -2,12 +2,10 @@
2
2
 
3
3
  module Yobi
4
4
  class Repository
5
- # `restic recover`: builds a new snapshot from any data found in this
5
+ # +restic recover+: builds a new snapshot from any data found in this
6
6
  # repository that isn't referenced by an existing snapshot (e.g. after
7
- # an accidental {#forget}). Call {#snapshots} afterward to find the
7
+ # an accidental #forget). Call #snapshots afterward to find the
8
8
  # recovered snapshot, if any was created.
9
- #
10
- # @return [true]
11
9
  def recover
12
10
  run_restic(build_argv("recover"))
13
11
  true