restic-service 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/install-dev.sh +13 -3
- data/install.sh +14 -2
- data/lib/restic/service.rb +1 -0
- data/lib/restic/service/auto_update.rb +106 -0
- data/lib/restic/service/cli.rb +81 -7
- data/lib/restic/service/conf.rb +22 -0
- data/lib/restic/service/targets/restic.rb +58 -17
- data/lib/restic/service/targets/restic_b2.rb +7 -1
- data/lib/restic/service/targets/restic_sftp.rb +9 -4
- data/lib/restic/service/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f4ae91f2cc1e092886efb337eaf4fcc6b31f1e8
|
4
|
+
data.tar.gz: c1aed6a2ed9345f1209c36b4cfb19e537607a1d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 968968cee27de96246836215811f1d50ce80f6039b33f5c9b66539c60843c555c6e2601b09d4b95a161e47d43029835263c2cf5a01f77ceaa110bb7e7bcfb465
|
7
|
+
data.tar.gz: abc0903185556bf615d1f4ec1c5c1632f716b74757647e857189d8637505ae397fdeb04528e61dee2a86ac31185db78f56afb4f73044b099f9c2b73d41710579
|
data/install-dev.sh
CHANGED
@@ -4,6 +4,10 @@ PROGRAM=restic-service
|
|
4
4
|
|
5
5
|
dev_path=$PWD
|
6
6
|
|
7
|
+
if test "x$1" = "x--systemd"; then
|
8
|
+
systemd=1
|
9
|
+
fi
|
10
|
+
|
7
11
|
target=`mktemp -d`
|
8
12
|
cd $target
|
9
13
|
cat > Gemfile <<GEMFILE
|
@@ -11,16 +15,22 @@ source "https://rubygems.org"
|
|
11
15
|
gem '${PROGRAM}', path: "$dev_path"
|
12
16
|
GEMFILE
|
13
17
|
|
14
|
-
|
18
|
+
bundler install --binstubs --without development --path vendor
|
19
|
+
for i in vendor/ruby/*; do
|
20
|
+
gem_home_relative=$i
|
21
|
+
done
|
22
|
+
GEM_HOME=$PWD/$gem_home_relative gem install bundler --no-document
|
23
|
+
for stub in bin/*; do
|
24
|
+
sed -i "/usr.bin.env/a ENV['GEM_HOME']='/opt/restic-service/$gem_home_relative'" $stub
|
25
|
+
done
|
15
26
|
|
16
|
-
bundler install --standalone --binstubs
|
17
27
|
if test -d /opt/${PROGRAM}; then
|
18
28
|
sudo rm -rf /opt/${PROGRAM}
|
19
29
|
fi
|
20
30
|
sudo cp -r . /opt/${PROGRAM}
|
21
31
|
sudo chmod go+rX /opt/${PROGRAM}
|
22
32
|
|
23
|
-
if test -d /lib/systemd/system; then
|
33
|
+
if test "x$systemd" = "x1" && test -d /lib/systemd/system; then
|
24
34
|
target_gem=`bundler show ${PROGRAM}`
|
25
35
|
sudo cp $target_gem/${PROGRAM}.service /lib/systemd/system
|
26
36
|
( sudo systemctl stop ${PROGRAM}.service
|
data/install.sh
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
PROGRAM=restic-service
|
4
4
|
|
5
|
+
if test "x$1" = "x--systemd"; then
|
6
|
+
systemd=1
|
7
|
+
fi
|
8
|
+
|
5
9
|
target=`mktemp -d`
|
6
10
|
cd $target
|
7
11
|
cat > Gemfile <<GEMFILE
|
@@ -9,14 +13,22 @@ source "https://rubygems.org"
|
|
9
13
|
gem '${PROGRAM}'
|
10
14
|
GEMFILE
|
11
15
|
|
12
|
-
bundler install --
|
16
|
+
bundler install --binstubs --without development --path vendor
|
17
|
+
for i in vendor/ruby/*; do
|
18
|
+
gem_home_relative=$i
|
19
|
+
done
|
20
|
+
GEM_HOME=$PWD/$gem_home_relative gem install bundler --no-document
|
21
|
+
for stub in bin/*; do
|
22
|
+
sed -i "/usr.bin.env/a ENV['GEM_HOME']='/opt/restic-service/$gem_home_relative'" $stub
|
23
|
+
done
|
24
|
+
|
13
25
|
if test -d /opt/${PROGRAM}; then
|
14
26
|
sudo rm -rf /opt/${PROGRAM}
|
15
27
|
fi
|
16
28
|
sudo cp -r . /opt/${PROGRAM}
|
17
29
|
sudo chmod go+rX /opt/${PROGRAM}
|
18
30
|
|
19
|
-
if test -d /lib/systemd/system; then
|
31
|
+
if test "x$systemd" = "x1" && test -d /lib/systemd/system; then
|
20
32
|
target_gem=`bundler show ${PROGRAM}`
|
21
33
|
sudo cp $target_gem/${PROGRAM}.service /lib/systemd/system
|
22
34
|
( sudo systemctl stop ${PROGRAM}.service
|
data/lib/restic/service.rb
CHANGED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
|
3
|
+
module Restic
|
4
|
+
module Service
|
5
|
+
class AutoUpdate
|
6
|
+
class FailedUpdate < RuntimeError
|
7
|
+
end
|
8
|
+
|
9
|
+
RESTIC_RELEASE_VERSION = "0.8.3"
|
10
|
+
|
11
|
+
def self.restic_release_url(platform)
|
12
|
+
"https://github.com/restic/restic/releases/download/v#{RESTIC_RELEASE_VERSION}/restic_#{RESTIC_RELEASE_VERSION}_#{platform}.bz2"
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(binary_path)
|
17
|
+
@root = File.dirname(File.dirname(binary_path))
|
18
|
+
unless File.file?(File.join(@root, "Gemfile"))
|
19
|
+
raise FailedUpdate, "cannot guess installation path (tried #{@root})"
|
20
|
+
end
|
21
|
+
|
22
|
+
@gem_home = ENV['GEM_HOME']
|
23
|
+
end
|
24
|
+
|
25
|
+
def patch_binstubs
|
26
|
+
bindir = File.join(@root, 'bin')
|
27
|
+
Dir.new(bindir).each do |entry|
|
28
|
+
entry = File.join(bindir, entry)
|
29
|
+
if File.file?(entry)
|
30
|
+
patched_contents = File.readlines(entry)
|
31
|
+
patched_contents.insert(1, "ENV['GEM_HOME'] = '#{@gem_home}'\n")
|
32
|
+
File.open(entry, 'w') do |io|
|
33
|
+
io.write patched_contents.join("")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def current_gem_version(gem_name)
|
40
|
+
gemfile_lock = File.join(@root, "Gemfile.lock")
|
41
|
+
File.readlines(gemfile_lock).each do |line|
|
42
|
+
match = /^\s+#{gem_name} \((.*)\)$/.match(line)
|
43
|
+
return match[1] if match
|
44
|
+
end
|
45
|
+
raise FailedUpdate, "cannot find the version line for #{gem_name} in #{gemfile_lock}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def update_restic_service
|
49
|
+
current_version = current_gem_version "restic-service"
|
50
|
+
reader, writer = IO.pipe
|
51
|
+
if !system("bundle", "update", out: writer, err: writer)
|
52
|
+
writer.close
|
53
|
+
puts reader.read(1024)
|
54
|
+
raise FailedUpdate, "failed to run bundle update"
|
55
|
+
end
|
56
|
+
patch_binstubs
|
57
|
+
new_version = current_gem_version "restic-service"
|
58
|
+
[current_version, new_version]
|
59
|
+
ensure
|
60
|
+
writer.close if writer && !writer.closed?
|
61
|
+
reader.close if reader && !reader.closed?
|
62
|
+
end
|
63
|
+
|
64
|
+
def update_restic(platform, target_path)
|
65
|
+
release_url = self.class.restic_release_url(platform)
|
66
|
+
|
67
|
+
release_binary = nil
|
68
|
+
while !release_binary
|
69
|
+
response = Net::HTTP.get_response(URI(release_url))
|
70
|
+
case response
|
71
|
+
when Net::HTTPSuccess
|
72
|
+
release_binary = response.body
|
73
|
+
when Net::HTTPRedirection
|
74
|
+
release_url = response['location']
|
75
|
+
else
|
76
|
+
raise FailedUpdate, "failed to fetch restic at #{release_url}: #{response}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
tmpdir = Dir.mktmpdir
|
81
|
+
restic_path = File.join(tmpdir, "restic")
|
82
|
+
File.open("#{restic_path}.bz2", 'w') do |io|
|
83
|
+
io.write release_binary
|
84
|
+
end
|
85
|
+
|
86
|
+
if !system("bzip2", "-d", "#{restic_path}.bz2")
|
87
|
+
raise FailedUpdate, "failed to uncompress the restic release file"
|
88
|
+
end
|
89
|
+
|
90
|
+
if File.file?(target_path)
|
91
|
+
current = File.read(target_path)
|
92
|
+
new = File.read(restic_path)
|
93
|
+
return if current == new
|
94
|
+
end
|
95
|
+
|
96
|
+
FileUtils.mv restic_path, target_path
|
97
|
+
FileUtils.chmod 0755, target_path
|
98
|
+
true
|
99
|
+
|
100
|
+
ensure
|
101
|
+
FileUtils.rm_rf tmpdir if tmpdir
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
data/lib/restic/service/cli.rb
CHANGED
@@ -26,31 +26,50 @@ module Restic
|
|
26
26
|
Conf.load(conf_file_path)
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
29
|
+
def each_selected_and_available_target(conf, *targets)
|
30
30
|
has_target = false
|
31
31
|
conf.each_target do |target|
|
32
32
|
has_target = true
|
33
33
|
if !targets.empty? && !targets.include?(target.name)
|
34
34
|
next
|
35
|
-
|
36
|
-
|
37
|
-
if !target.available?
|
35
|
+
elsif !target.available?
|
38
36
|
puts "#{target.name} is not available"
|
39
37
|
next
|
40
38
|
end
|
41
39
|
|
40
|
+
yield(target)
|
41
|
+
end
|
42
|
+
|
43
|
+
if !has_target
|
44
|
+
STDERR.puts "WARNING: no targets in #{options[:conf]}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_sync(conf, *targets)
|
49
|
+
each_selected_and_available_target(conf, *targets) do |target|
|
42
50
|
puts
|
43
51
|
puts "-----"
|
44
52
|
puts "#{Time.now} - Synchronizing #{target.name}"
|
45
53
|
target.run
|
46
54
|
end
|
47
|
-
|
48
|
-
|
55
|
+
end
|
56
|
+
|
57
|
+
def run_forget(conf, *targets)
|
58
|
+
each_selected_and_available_target(conf, *targets) do |target|
|
59
|
+
unless target.respond_to?(:forget)
|
60
|
+
puts "#{target.name} does not supports forget"
|
61
|
+
next
|
62
|
+
end
|
63
|
+
|
64
|
+
puts
|
65
|
+
puts "-----"
|
66
|
+
puts "#{Time.now} - Running forget pass on #{target.name}"
|
67
|
+
target.forget
|
49
68
|
end
|
50
69
|
end
|
51
70
|
end
|
52
71
|
|
53
|
-
desc '
|
72
|
+
desc 'whereami', 'finds the available backup targets'
|
54
73
|
def whereami
|
55
74
|
STDOUT.sync = true
|
56
75
|
conf = load_conf
|
@@ -60,6 +79,53 @@ module Restic
|
|
60
79
|
end
|
61
80
|
end
|
62
81
|
|
82
|
+
desc 'install-restic PATH PLATFORM', 'install restic'
|
83
|
+
def install_restic(path, platform)
|
84
|
+
updater = AutoUpdate.new($0)
|
85
|
+
updater.update_restic(platform, path)
|
86
|
+
end
|
87
|
+
|
88
|
+
desc 'auto-update', 'perform auto-updating as configured in the configuration file'
|
89
|
+
def auto_update
|
90
|
+
conf = load_conf
|
91
|
+
STDOUT.sync = true
|
92
|
+
|
93
|
+
updater = AutoUpdate.new($0)
|
94
|
+
if conf.auto_update_restic_service?
|
95
|
+
puts "attempting to auto-update restic-service"
|
96
|
+
old_version, new_version = updater.update_restic_service
|
97
|
+
if old_version != new_version
|
98
|
+
puts "updated restic-service from #{old_version} to #{new_version}, restarting"
|
99
|
+
exec "bundle", "exec", Gem.ruby, $0, "auto-update"
|
100
|
+
else
|
101
|
+
puts "restic-service was already up-to-date: #{new_version}"
|
102
|
+
end
|
103
|
+
else
|
104
|
+
puts "updating restic-service disabled in configuration"
|
105
|
+
end
|
106
|
+
|
107
|
+
update_restic = conf.auto_update_restic?
|
108
|
+
if update_restic
|
109
|
+
begin
|
110
|
+
restic_path = conf.tool_path('restic')
|
111
|
+
rescue ArgumentError
|
112
|
+
puts "cannot auto-update restic, provide an explicit path in the 'tools' section of the configuration first"
|
113
|
+
update_restic = false
|
114
|
+
end
|
115
|
+
else
|
116
|
+
puts "updating restic disabled in configuration"
|
117
|
+
end
|
118
|
+
|
119
|
+
if update_restic
|
120
|
+
puts "attempting to auto-update restic"
|
121
|
+
if updater.update_restic(conf.restic_platform, restic_path)
|
122
|
+
puts "updated restic to version #{AutoUpdate::RESTIC_RELEASE_VERSION}"
|
123
|
+
else
|
124
|
+
puts "restic was already up-to-date"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
63
129
|
desc 'sync', 'synchronize all (some) targets'
|
64
130
|
def sync(*targets)
|
65
131
|
STDOUT.sync = true
|
@@ -67,6 +133,13 @@ module Restic
|
|
67
133
|
run_sync(conf, *targets)
|
68
134
|
end
|
69
135
|
|
136
|
+
desc 'forget', 'delete historical data'
|
137
|
+
def forget(*targets)
|
138
|
+
STDOUT.sync = true
|
139
|
+
conf = load_conf
|
140
|
+
run_forget(conf, *targets)
|
141
|
+
end
|
142
|
+
|
70
143
|
desc 'auto', 'periodically runs the backups, pass target names to restrict to these'
|
71
144
|
def auto(*targets)
|
72
145
|
STDOUT.sync = true
|
@@ -76,6 +149,7 @@ module Restic
|
|
76
149
|
puts ""
|
77
150
|
|
78
151
|
run_sync(conf, *targets)
|
152
|
+
run_forget(conf, *targets)
|
79
153
|
|
80
154
|
puts ""
|
81
155
|
puts "#{Time.now} Finished automatic synchronization pass"
|
data/lib/restic/service/conf.rb
CHANGED
@@ -61,6 +61,8 @@ module Restic
|
|
61
61
|
yaml['tools'][tool_name] ||= tool_name
|
62
62
|
end
|
63
63
|
|
64
|
+
yaml['auto_update'] ||= Array.new
|
65
|
+
|
64
66
|
target_names = Array.new
|
65
67
|
yaml['targets'] = yaml['targets'].map do |target|
|
66
68
|
if !target['name']
|
@@ -215,6 +217,18 @@ module Restic
|
|
215
217
|
end
|
216
218
|
end
|
217
219
|
|
220
|
+
def auto_update_restic_service?
|
221
|
+
@auto_update_restic_service
|
222
|
+
end
|
223
|
+
|
224
|
+
def auto_update_restic?
|
225
|
+
@auto_update_restic
|
226
|
+
end
|
227
|
+
|
228
|
+
def restic_platform
|
229
|
+
@auto_update_restic
|
230
|
+
end
|
231
|
+
|
218
232
|
# Add the information stored in a YAML-like hash into this
|
219
233
|
# configuration
|
220
234
|
#
|
@@ -228,6 +242,14 @@ module Restic
|
|
228
242
|
Conf.parse_bandwidth_limit(limit_yaml)
|
229
243
|
end
|
230
244
|
|
245
|
+
yaml['auto_update'].each do |update_target, do_update|
|
246
|
+
if update_target == 'restic-service'
|
247
|
+
@auto_update_restic_service = do_update
|
248
|
+
elsif update_target == 'restic'
|
249
|
+
@auto_update_restic = do_update
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
231
253
|
yaml['targets'].each do |yaml_target|
|
232
254
|
type = yaml_target['type']
|
233
255
|
target_class = Conf.target_class_from_type(type)
|
@@ -48,40 +48,81 @@ module Restic
|
|
48
48
|
@io_class = Integer(yaml['io_class'])
|
49
49
|
@io_priority = Integer(yaml['io_priority'])
|
50
50
|
@cpu_priority = Integer(yaml['cpu_priority'])
|
51
|
+
@forget = parse_forget_setup(yaml['forget'] || Hash.new)
|
51
52
|
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
Forget = Struct.new :prune, :tags, :hourly, :daily, :weekly, :monthly, :yearly do
|
55
|
+
def prune?
|
56
|
+
prune
|
57
|
+
end
|
58
|
+
end
|
59
|
+
FORGET_DURATION_KEYS = %w{tags hourly daily weekly monthly yearly}
|
60
|
+
FORGET_KEYS = ['prune', *FORGET_DURATION_KEYS].freeze
|
61
|
+
def parse_forget_setup(setup)
|
62
|
+
parsed = Forget.new true, []
|
63
|
+
if (invalid_key = setup.find { |k, _| !FORGET_KEYS.include?(k) })
|
64
|
+
raise ArgumentError, "#{invalid_key} is not a valid key within "\
|
65
|
+
"'forget', valid keys are: #{FORGET_KEYS.join(", ")}"
|
66
|
+
end
|
56
67
|
|
68
|
+
FORGET_KEYS.each do |key|
|
69
|
+
parsed[key] = setup.fetch(key, key == "prune")
|
70
|
+
end
|
71
|
+
parsed
|
72
|
+
end
|
73
|
+
|
74
|
+
def run_backup(*args, **options)
|
75
|
+
extra_args = []
|
76
|
+
if one_filesystem?
|
77
|
+
extra_args << '--one-file-system'
|
78
|
+
end
|
79
|
+
|
80
|
+
run_restic(*args, *extra_args,
|
81
|
+
*@excludes.flat_map { |e| ['--exclude', e] },
|
82
|
+
*@includes)
|
83
|
+
end
|
84
|
+
|
85
|
+
def run_restic(*args, **options)
|
86
|
+
home = ENV['HOME'] || '/root'
|
57
87
|
env = if args.first.kind_of?(Hash)
|
58
88
|
env = args.shift
|
59
89
|
else
|
60
90
|
env = Hash.new
|
61
91
|
end
|
62
92
|
|
63
|
-
ionice_args = []
|
64
|
-
if @io_class != 3
|
65
|
-
ionice_args << '-n' << @io_priority.to_s
|
66
|
-
end
|
67
|
-
|
68
93
|
extra_args = []
|
69
|
-
if one_filesystem?
|
70
|
-
extra_args << '--one-file-system'
|
71
|
-
end
|
72
94
|
if @bandwidth_limit
|
73
95
|
limit_KiB = @bandwidth_limit / 1000
|
74
96
|
extra_args << '--limit-download' << limit_KiB.to_s << '--limit-upload' << limit_KiB.to_s
|
75
97
|
end
|
76
98
|
|
77
|
-
|
99
|
+
ionice_args = []
|
100
|
+
if @io_class != 3
|
101
|
+
ionice_args << '-n' << @io_priority.to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
system(Hash['HOME' => home, 'RESTIC_PASSWORD' => @password].merge(env),
|
78
105
|
'ionice', '-c', @io_class.to_s, *ionice_args,
|
79
106
|
'nice', "-#{@cpu_priority}",
|
80
|
-
@restic_path.to_path, *args, *extra_args,
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
107
|
+
@restic_path.to_path, "--cleanup-cache", *args, *extra_args, in: :close, **options)
|
108
|
+
end
|
109
|
+
|
110
|
+
def run_forget(*args)
|
111
|
+
extra_args = []
|
112
|
+
FORGET_DURATION_KEYS.each do |key|
|
113
|
+
arg_key =
|
114
|
+
if key == "tags" then "tag"
|
115
|
+
else key
|
116
|
+
end
|
117
|
+
if value = @forget[key]
|
118
|
+
extra_args << "--keep-#{arg_key}" << value.to_s
|
119
|
+
end
|
120
|
+
end
|
121
|
+
if @forget.prune?
|
122
|
+
extra_args << "--prune"
|
123
|
+
puts "PRUNE"
|
124
|
+
end
|
125
|
+
run_restic(*args, *extra_args)
|
85
126
|
end
|
86
127
|
end
|
87
128
|
end
|
@@ -13,7 +13,13 @@ module Restic
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def run
|
16
|
-
|
16
|
+
run_backup(Hash['B2_ACCOUNT_ID' => @id, 'B2_ACCOUNT_KEY' => @key],
|
17
|
+
'-r', "b2:#{@bucket}:#{@path}", 'backup')
|
18
|
+
end
|
19
|
+
|
20
|
+
def forget
|
21
|
+
run_forget(Hash['B2_ACCOUNT_ID' => @id, 'B2_ACCOUNT_KEY' => @key],
|
22
|
+
'-r', "b2:#{@bucket}:#{@path}", 'forget')
|
17
23
|
end
|
18
24
|
end
|
19
25
|
end
|
@@ -44,16 +44,21 @@ module Restic
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def run
|
47
|
-
|
48
|
-
|
47
|
+
ssh = SSHKeys.new
|
48
|
+
ssh_config_name = ssh.ssh_setup_config(@target_name, @username, @host, @key_path)
|
49
|
+
|
50
|
+
run_backup('-r', "sftp:#{ssh_config_name}:#{@path}", 'backup')
|
51
|
+
ensure
|
52
|
+
ssh.ssh_cleanup_config
|
53
|
+
end
|
49
54
|
|
55
|
+
def forget
|
50
56
|
ssh = SSHKeys.new
|
51
57
|
ssh_config_name = ssh.ssh_setup_config(@target_name, @username, @host, @key_path)
|
52
58
|
|
53
|
-
|
59
|
+
run_forget('-r', "sftp:#{ssh_config_name}:#{@path}", 'forget')
|
54
60
|
ensure
|
55
61
|
ssh.ssh_cleanup_config
|
56
|
-
ENV['HOME'] = current_home
|
57
62
|
end
|
58
63
|
end
|
59
64
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: restic-service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Joyeux
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -100,6 +100,7 @@ files:
|
|
100
100
|
- install-dev.sh
|
101
101
|
- install.sh
|
102
102
|
- lib/restic/service.rb
|
103
|
+
- lib/restic/service/auto_update.rb
|
103
104
|
- lib/restic/service/cli.rb
|
104
105
|
- lib/restic/service/conf.rb
|
105
106
|
- lib/restic/service/ssh_keys.rb
|