duplicati 0.0.7 → 0.0.8
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.
- data/README.md +20 -0
- data/lib/duplicati/backup.rb +0 -4
- data/lib/duplicati/clean.rb +16 -0
- data/lib/duplicati/version.rb +1 -1
- data/lib/duplicati.rb +22 -10
- data/spec/duplicati/backup_spec.rb +0 -4
- data/spec/duplicati/clean_spec.rb +26 -0
- data/spec/duplicati_spec.rb +50 -17
- metadata +7 -4
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# Duplicati
|
2
|
+
[](http://badge.fury.io/rb/duplicati)
|
2
3
|
[](http://travis-ci.org/jarmo/duplicati-rb)
|
3
4
|
[](https://coveralls.io/r/jarmo/duplicati-rb)
|
4
5
|
|
@@ -136,6 +137,25 @@ class CustomNotification
|
|
136
137
|
end
|
137
138
|
````
|
138
139
|
|
140
|
+
## Execution status
|
141
|
+
|
142
|
+
Duplicati has a ````#execution_success?```` method for determining the success
|
143
|
+
status of backup command:
|
144
|
+
|
145
|
+
````ruby
|
146
|
+
Duplicati.backup(options).execution_success? # => "true" when execution was a success.
|
147
|
+
````
|
148
|
+
|
149
|
+
## Fine Grained Commands Execution
|
150
|
+
|
151
|
+
It is possible to execute commands separately when needed:
|
152
|
+
|
153
|
+
````ruby
|
154
|
+
Duplicati.backup(options).execution_success?
|
155
|
+
# is same as
|
156
|
+
Duplicati.new(options).backup.clean.notify.execution_success?
|
157
|
+
````
|
158
|
+
|
139
159
|
## Limitations
|
140
160
|
|
141
161
|
* Currently only backup is supported. Use Duplicati's command line or GUI utility directly for restoring.
|
data/lib/duplicati/backup.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
class Duplicati
|
2
|
+
class Clean
|
3
|
+
def initialize(opts)
|
4
|
+
@duplicati_path = opts[:duplicati_path]
|
5
|
+
@backup_store_path = opts[:backup_store_path] or raise ":backup_store_path option is missing for clean!"
|
6
|
+
@log_path = opts[:log_path]
|
7
|
+
end
|
8
|
+
|
9
|
+
def command
|
10
|
+
%Q["#{@duplicati_path}" delete-all-but-n 5 "#{@backup_store_path}"
|
11
|
+
--force
|
12
|
+
2>&1 1>> "#{@log_path}"]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
data/lib/duplicati/version.rb
CHANGED
data/lib/duplicati.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require File.expand_path("duplicati/version", File.dirname(__FILE__))
|
2
2
|
require File.expand_path("duplicati/backup", File.dirname(__FILE__))
|
3
|
+
require File.expand_path("duplicati/clean", File.dirname(__FILE__))
|
3
4
|
require File.expand_path("duplicati/notification/base", File.dirname(__FILE__))
|
4
5
|
require File.expand_path("duplicati/notification/growl", File.dirname(__FILE__))
|
5
6
|
require File.expand_path("duplicati/notification/mail", File.dirname(__FILE__))
|
@@ -8,7 +9,7 @@ class Duplicati
|
|
8
9
|
|
9
10
|
class << self
|
10
11
|
def backup(opts={})
|
11
|
-
new(opts).backup
|
12
|
+
new(opts).backup.clean.notify
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
@@ -26,6 +27,7 @@ class Duplicati
|
|
26
27
|
end
|
27
28
|
|
28
29
|
@opts = opts
|
30
|
+
@execution_success = true
|
29
31
|
end
|
30
32
|
|
31
33
|
def backup
|
@@ -33,6 +35,24 @@ class Duplicati
|
|
33
35
|
options :duplicati_path, :backup_paths, :backup_store_path,
|
34
36
|
:backup_encryption_key, :inclusion_filters, :exclusion_filters, :log_path
|
35
37
|
).command
|
38
|
+
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def clean
|
43
|
+
execute Clean.new(
|
44
|
+
options :duplicati_path, :backup_store_path, :log_path
|
45
|
+
).command
|
46
|
+
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def notify
|
51
|
+
@opts[:notifications].each do |notification|
|
52
|
+
notification.notify execution_success?
|
53
|
+
end
|
54
|
+
|
55
|
+
self
|
36
56
|
end
|
37
57
|
|
38
58
|
private
|
@@ -47,8 +67,6 @@ class Duplicati
|
|
47
67
|
puts formatted_command if $DEBUG
|
48
68
|
system(formatted_command)
|
49
69
|
@exit_status = $?.exitstatus
|
50
|
-
notify
|
51
|
-
execution_success?
|
52
70
|
end
|
53
71
|
|
54
72
|
def options(*options_to_extract)
|
@@ -62,12 +80,6 @@ class Duplicati
|
|
62
80
|
command.gsub($/, "").squeeze(" ")
|
63
81
|
end
|
64
82
|
|
65
|
-
def notify
|
66
|
-
@opts[:notifications].each do |notification|
|
67
|
-
notification.notify execution_success?
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
83
|
# https://code.google.com/p/duplicati/issues/detail?id=678
|
72
84
|
# 0 - Success
|
73
85
|
# 1 - Success (but no changed files)
|
@@ -76,7 +88,7 @@ class Duplicati
|
|
76
88
|
# 100 - No connection to server -> Fatal error
|
77
89
|
# 200 - Invalid command/arguments
|
78
90
|
def execution_success?
|
79
|
-
@exit_status && @exit_status.between?(0, 2)
|
91
|
+
@execution_success &&= @exit_status && @exit_status.between?(0, 2)
|
80
92
|
end
|
81
93
|
|
82
94
|
end
|
@@ -40,10 +40,6 @@ describe Duplicati::Backup do
|
|
40
40
|
--usn-policy=auto
|
41
41
|
--snapshot-policy=auto
|
42
42
|
--full-if-sourcefolder-changed
|
43
|
-
2>&1 1>> "/zzz/output.log" &&
|
44
|
-
|
45
|
-
"/bin/duplicati-commandline" delete-all-but-n 5 "file:///foo/backup"
|
46
|
-
--force
|
47
43
|
2>&1 1>> "/zzz/output.log"]
|
48
44
|
end
|
49
45
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Duplicati::Clean do
|
4
|
+
|
5
|
+
context "#initialize" do
|
6
|
+
it "raises an Exception if backup store path is not provided" do
|
7
|
+
expect {
|
8
|
+
Duplicati::Clean.new(:backup_paths => [])
|
9
|
+
}.to raise_error(RuntimeError, ":backup_store_path option is missing for clean!")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "#command" do
|
14
|
+
|
15
|
+
it "generates clean command for Duplicati using different options" do
|
16
|
+
Duplicati::Clean.new(
|
17
|
+
:duplicati_path => "/bin/duplicati-commandline",
|
18
|
+
:backup_store_path => "file:///foo/backup",
|
19
|
+
:log_path => "/zzz/output.log"
|
20
|
+
).command.should == %Q["/bin/duplicati-commandline" delete-all-but-n 5 "file:///foo/backup"
|
21
|
+
--force
|
22
|
+
2>&1 1>> "/zzz/output.log"]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/spec/duplicati_spec.rb
CHANGED
@@ -73,12 +73,12 @@ describe Duplicati do
|
|
73
73
|
end
|
74
74
|
|
75
75
|
context "#backup" do
|
76
|
-
it "executes the backup command" do
|
76
|
+
it "executes the backup command and returns self" do
|
77
77
|
Duplicati::Backup.any_instance.should_receive(:command).and_return("backup command")
|
78
78
|
duplicati = Duplicati.new(:backup_paths => [], :backup_store_path => "")
|
79
79
|
duplicati.should_receive(:execute).with("backup command")
|
80
80
|
|
81
|
-
duplicati.backup
|
81
|
+
duplicati.backup.should == duplicati
|
82
82
|
end
|
83
83
|
|
84
84
|
it "proxies options to backup command" do
|
@@ -102,11 +102,14 @@ describe Duplicati do
|
|
102
102
|
end
|
103
103
|
|
104
104
|
context ".backup" do
|
105
|
-
it "is a convenience method for .new#backup" do
|
105
|
+
it "is a convenience method for .new#backup#clean#notify" do
|
106
106
|
Duplicati::Backup.any_instance.should_receive(:command).and_return("backup command")
|
107
107
|
Duplicati.any_instance.should_receive(:execute).with("backup command")
|
108
|
+
Duplicati::Clean.any_instance.should_receive(:command).and_return("clean command")
|
109
|
+
Duplicati.any_instance.should_receive(:execute).with("clean command")
|
110
|
+
Duplicati.any_instance.should_receive(:notify)
|
108
111
|
|
109
|
-
Duplicati.backup(:backup_paths => [], :backup_store_path => "")
|
112
|
+
Duplicati.backup(:backup_paths => [], :backup_store_path => "", :notifications => [])
|
110
113
|
end
|
111
114
|
end
|
112
115
|
|
@@ -130,7 +133,7 @@ describe Duplicati do
|
|
130
133
|
$?.should_receive(:exitstatus).and_return -1
|
131
134
|
|
132
135
|
duplicati = Duplicati.new
|
133
|
-
duplicati.send(:execute, "")
|
136
|
+
duplicati.send(:execute, "")
|
134
137
|
duplicati.should_not be_execution_success
|
135
138
|
end
|
136
139
|
|
@@ -139,7 +142,20 @@ describe Duplicati do
|
|
139
142
|
$?.should_receive(:exitstatus).and_return 3
|
140
143
|
|
141
144
|
duplicati = Duplicati.new
|
142
|
-
duplicati.send(:execute, "")
|
145
|
+
duplicati.send(:execute, "")
|
146
|
+
duplicati.should_not be_execution_success
|
147
|
+
end
|
148
|
+
|
149
|
+
it "is false when one of the commands fail with invalid exit status" do
|
150
|
+
duplicati = Duplicati.new
|
151
|
+
|
152
|
+
Object.any_instance.should_receive(:system).twice.and_return true
|
153
|
+
$?.should_receive(:exitstatus).and_return 0
|
154
|
+
duplicati.send(:execute, "")
|
155
|
+
duplicati.should be_execution_success
|
156
|
+
|
157
|
+
$?.should_receive(:exitstatus).and_return 3
|
158
|
+
duplicati.send(:execute, "")
|
143
159
|
duplicati.should_not be_execution_success
|
144
160
|
end
|
145
161
|
|
@@ -148,7 +164,7 @@ describe Duplicati do
|
|
148
164
|
$?.should_receive(:exitstatus).and_return 0
|
149
165
|
|
150
166
|
duplicati = Duplicati.new
|
151
|
-
duplicati.send(:execute, "")
|
167
|
+
duplicati.send(:execute, "")
|
152
168
|
duplicati.should be_execution_success
|
153
169
|
end
|
154
170
|
|
@@ -157,7 +173,7 @@ describe Duplicati do
|
|
157
173
|
$?.should_receive(:exitstatus).and_return 1
|
158
174
|
|
159
175
|
duplicati = Duplicati.new
|
160
|
-
duplicati.send(:execute, "")
|
176
|
+
duplicati.send(:execute, "")
|
161
177
|
duplicati.should be_execution_success
|
162
178
|
end
|
163
179
|
|
@@ -166,7 +182,19 @@ describe Duplicati do
|
|
166
182
|
$?.should_receive(:exitstatus).and_return 2
|
167
183
|
|
168
184
|
duplicati = Duplicati.new
|
169
|
-
duplicati.send(:execute, "")
|
185
|
+
duplicati.send(:execute, "")
|
186
|
+
duplicati.should be_execution_success
|
187
|
+
end
|
188
|
+
|
189
|
+
it "is true when all of the commands succeed with success exit status" do
|
190
|
+
duplicati = Duplicati.new
|
191
|
+
|
192
|
+
Object.any_instance.should_receive(:system).twice.and_return true
|
193
|
+
$?.should_receive(:exitstatus).twice.and_return 0
|
194
|
+
duplicati.send(:execute, "")
|
195
|
+
duplicati.should be_execution_success
|
196
|
+
|
197
|
+
duplicati.send(:execute, "")
|
170
198
|
duplicati.should be_execution_success
|
171
199
|
end
|
172
200
|
end
|
@@ -176,26 +204,31 @@ describe Duplicati do
|
|
176
204
|
Duplicati.any_instance.unstub(:notify)
|
177
205
|
end
|
178
206
|
|
207
|
+
it "returns self" do
|
208
|
+
duplicati = Duplicati.new
|
209
|
+
duplicati.notify.should == duplicati
|
210
|
+
end
|
211
|
+
|
179
212
|
it "notifies with all possible notifications with false execution success" do
|
180
|
-
Object.any_instance.stub(:system).and_return false
|
181
|
-
$?.should_receive(:exitstatus).and_return 3
|
182
213
|
notification1 = double('notification1').as_null_object
|
183
214
|
notification2 = double('notification2').as_null_object
|
184
|
-
|
185
215
|
notification1.should_receive(:notify).with(false)
|
186
216
|
notification2.should_receive(:notify).with(false)
|
187
|
-
|
217
|
+
|
218
|
+
duplicati = Duplicati.new(:notifications => [notification1, notification2])
|
219
|
+
duplicati.should_receive(:execution_success?).twice.and_return(false)
|
220
|
+
duplicati.notify
|
188
221
|
end
|
189
222
|
|
190
223
|
it "notifies with all possible notifications with true execution success" do
|
191
|
-
Object.any_instance.stub(:system).and_return true
|
192
|
-
$?.should_receive(:exitstatus).and_return 0
|
193
224
|
notification1 = double('notification1').as_null_object
|
194
225
|
notification2 = double('notification2').as_null_object
|
195
|
-
|
196
226
|
notification1.should_receive(:notify).with(true)
|
197
227
|
notification2.should_receive(:notify).with(true)
|
198
|
-
|
228
|
+
|
229
|
+
duplicati = Duplicati.new(:notifications => [notification1, notification2])
|
230
|
+
duplicati.should_receive(:execution_success?).twice.and_return(true)
|
231
|
+
duplicati.notify
|
199
232
|
end
|
200
233
|
end
|
201
234
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: duplicati
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -61,6 +61,7 @@ files:
|
|
61
61
|
- duplicati.gemspec
|
62
62
|
- lib/duplicati.rb
|
63
63
|
- lib/duplicati/backup.rb
|
64
|
+
- lib/duplicati/clean.rb
|
64
65
|
- lib/duplicati/notification/base.rb
|
65
66
|
- lib/duplicati/notification/failed.png
|
66
67
|
- lib/duplicati/notification/growl.rb
|
@@ -68,6 +69,7 @@ files:
|
|
68
69
|
- lib/duplicati/notification/success.png
|
69
70
|
- lib/duplicati/version.rb
|
70
71
|
- spec/duplicati/backup_spec.rb
|
72
|
+
- spec/duplicati/clean_spec.rb
|
71
73
|
- spec/duplicati/notification/growl_spec.rb
|
72
74
|
- spec/duplicati/notification/mail_spec.rb
|
73
75
|
- spec/duplicati_spec.rb
|
@@ -86,7 +88,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
88
|
version: '0'
|
87
89
|
segments:
|
88
90
|
- 0
|
89
|
-
hash: -
|
91
|
+
hash: -685447987
|
90
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
93
|
none: false
|
92
94
|
requirements:
|
@@ -95,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
97
|
version: '0'
|
96
98
|
segments:
|
97
99
|
- 0
|
98
|
-
hash: -
|
100
|
+
hash: -685447987
|
99
101
|
requirements: []
|
100
102
|
rubyforge_project:
|
101
103
|
rubygems_version: 1.8.24
|
@@ -105,6 +107,7 @@ summary: Duplicati backup utility wrapper in Ruby with easier API and sensible c
|
|
105
107
|
defaults.
|
106
108
|
test_files:
|
107
109
|
- spec/duplicati/backup_spec.rb
|
110
|
+
- spec/duplicati/clean_spec.rb
|
108
111
|
- spec/duplicati/notification/growl_spec.rb
|
109
112
|
- spec/duplicati/notification/mail_spec.rb
|
110
113
|
- spec/duplicati_spec.rb
|