duplicati 0.0.1 → 0.0.2
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/lib/duplicati/backup.rb +44 -7
- data/lib/duplicati/version.rb +1 -1
- data/lib/duplicati.rb +12 -2
- data/spec/duplicati/backup_spec.rb +82 -8
- data/spec/duplicati_spec.rb +32 -2
- data/spec/spec_helper.rb +1 -0
- metadata +2 -2
data/lib/duplicati/backup.rb
CHANGED
@@ -1,22 +1,59 @@
|
|
1
1
|
class Duplicati
|
2
2
|
class Backup
|
3
|
-
def initialize(opts
|
4
|
-
@
|
3
|
+
def initialize(opts)
|
4
|
+
@duplicati_path = opts[:duplicati_path]
|
5
|
+
@backup_paths = opts[:backup_paths] or raise ":backup_paths option is missing for backup!"
|
6
|
+
@backup_store_path = opts[:backup_store_path] or raise ":backup_store_path option is missing for backup!"
|
7
|
+
@backup_encryption_key = opts[:backup_encryption_key]
|
8
|
+
@inclusion_filters = opts[:inclusion_filters] || []
|
9
|
+
@exclusion_filters = opts[:exclusion_filters] || []
|
10
|
+
@log_path = opts[:log_path]
|
5
11
|
end
|
6
12
|
|
7
13
|
def command
|
8
|
-
%Q["#{@
|
9
|
-
|
14
|
+
%Q["#{@duplicati_path}" backup "#{backup_paths}" "#{@backup_store_path}"
|
15
|
+
#{encryption_option}
|
16
|
+
#{inclusion_filters}
|
17
|
+
#{exclusion_filters}
|
10
18
|
--auto-cleanup
|
11
19
|
--full-if-older-than=1M
|
12
20
|
--usn-policy=on
|
13
21
|
--snapshot-policy=on
|
14
22
|
--full-if-sourcefolder-changed
|
15
|
-
2>&1 1>> "#{@
|
23
|
+
2>&1 1>> "#{@log_path}" &&
|
16
24
|
|
17
|
-
"#{@
|
25
|
+
"#{@duplicati_path}" delete-all-but-n 5 "#{@backup_store_path}"
|
18
26
|
--force
|
19
|
-
2>&1 1>> "#{@
|
27
|
+
2>&1 1>> "#{@log_path}"]
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def encryption_option
|
33
|
+
@backup_encryption_key ?
|
34
|
+
%Q[--passphrase="#{@backup_encryption_key}"] :
|
35
|
+
%Q[--no-encryption]
|
36
|
+
end
|
37
|
+
|
38
|
+
def backup_paths
|
39
|
+
@backup_paths.map do |path|
|
40
|
+
path = path.strip.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
|
41
|
+
Dir.glob(path.gsub(/\/$/, ""))
|
42
|
+
end.flatten.join(File::PATH_SEPARATOR)
|
43
|
+
end
|
44
|
+
|
45
|
+
def inclusion_filters
|
46
|
+
filters "include", @inclusion_filters
|
47
|
+
end
|
48
|
+
|
49
|
+
def exclusion_filters
|
50
|
+
filters "exclude", @exclusion_filters
|
51
|
+
end
|
52
|
+
|
53
|
+
def filters(type, filters)
|
54
|
+
filters.reduce([]) do |memo, filter|
|
55
|
+
memo << %Q[--#{type}-regexp="#{filter.source}"]
|
56
|
+
end.join(" ")
|
20
57
|
end
|
21
58
|
end
|
22
59
|
end
|
data/lib/duplicati/version.rb
CHANGED
data/lib/duplicati.rb
CHANGED
@@ -16,12 +16,20 @@ class Duplicati
|
|
16
16
|
opts[:log_path] ||= "duplicati.log"
|
17
17
|
opts[:duplicati_path] = duplicati_path(opts[:duplicati_path])
|
18
18
|
opts[:notifications] ||= [Notification::Growl]
|
19
|
+
opts[:inclusion_filters] ||= [opts[:inclusion_filter]].compact
|
20
|
+
opts[:exclusion_filters] ||= [opts[:exclusion_filter]].compact
|
21
|
+
|
22
|
+
if !opts[:inclusion_filters].empty? && opts[:exclusion_filters].empty?
|
23
|
+
opts[:exclusion_filters] = [%r{.*[^\\/]}]
|
24
|
+
end
|
25
|
+
|
19
26
|
@opts = opts
|
20
27
|
end
|
21
28
|
|
22
29
|
def backup
|
23
30
|
execute Backup.new(
|
24
|
-
options :duplicati_path, :backup_paths, :backup_store_path,
|
31
|
+
options :duplicati_path, :backup_paths, :backup_store_path,
|
32
|
+
:backup_encryption_key, :inclusion_filters, :exclusion_filters, :log_path
|
25
33
|
).command
|
26
34
|
end
|
27
35
|
|
@@ -34,7 +42,9 @@ class Duplicati
|
|
34
42
|
|
35
43
|
def execute(command)
|
36
44
|
old_log_file_size = File.read(@opts[:log_path]).strip.size rescue 0
|
37
|
-
|
45
|
+
formatted_command = format command
|
46
|
+
puts formatted_command if $DEBUG
|
47
|
+
@execution_success = system(formatted_command) && File.read(@opts[:log_path]).strip.size > old_log_file_size
|
38
48
|
notify
|
39
49
|
end
|
40
50
|
|
@@ -1,15 +1,39 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Duplicati::Backup do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
:
|
11
|
-
|
4
|
+
|
5
|
+
context "#initialize" do
|
6
|
+
|
7
|
+
it "raises an Exception if backup paths are not provided" do
|
8
|
+
expect {
|
9
|
+
Duplicati::Backup.new(:backup_store_path => "")
|
10
|
+
}.to raise_error(RuntimeError, ":backup_paths option is missing for backup!")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "raises an Exception if backup store path is not provided" do
|
14
|
+
expect {
|
15
|
+
Duplicati::Backup.new(:backup_paths => [])
|
16
|
+
}.to raise_error(RuntimeError, ":backup_store_path option is missing for backup!")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "#command" do
|
21
|
+
|
22
|
+
let(:basedir) { File.dirname(__FILE__) }
|
23
|
+
|
24
|
+
it "generates backup command for Duplicati using different options" do
|
25
|
+
Duplicati::Backup.new(
|
26
|
+
:duplicati_path => "/bin/duplicati-commandline",
|
27
|
+
:backup_paths => [basedir, File.join(basedir, "../")],
|
28
|
+
:backup_store_path => "file:///foo/backup",
|
29
|
+
:backup_encryption_key => "secret",
|
30
|
+
:inclusion_filters => [/include-me/],
|
31
|
+
:exclusion_filters => [/exclude-me/],
|
32
|
+
:log_path => "/zzz/output.log"
|
33
|
+
).command.should == %Q["/bin/duplicati-commandline" backup "#{basedir}#{File::PATH_SEPARATOR}#{File.join(basedir, "../").chop}" "file:///foo/backup"
|
12
34
|
--passphrase="secret"
|
35
|
+
--include-regexp="include-me"
|
36
|
+
--exclude-regexp="exclude-me"
|
13
37
|
--auto-cleanup
|
14
38
|
--full-if-older-than=1M
|
15
39
|
--usn-policy=on
|
@@ -20,5 +44,55 @@ describe Duplicati::Backup do
|
|
20
44
|
"/bin/duplicati-commandline" delete-all-but-n 5 "file:///foo/backup"
|
21
45
|
--force
|
22
46
|
2>&1 1>> "/zzz/output.log"]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "generates backup command for Duplicati without using any encryption when encryption key is not provided" do
|
50
|
+
command = Duplicati::Backup.new(:backup_paths => [], :backup_store_path => "", :inclusion_filters => [], :exclusion_filters => []).command
|
51
|
+
command.should include("--no-encryption")
|
52
|
+
command.should_not include("--passphrase")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "works with globs for backup paths" do
|
56
|
+
Duplicati::Backup.new(:backup_paths => [File.join(basedir, "../*_helper*")], :backup_store_path => "", :inclusion_filters => [], :exclusion_filters => []).
|
57
|
+
command.split(" ")[2].should == "\"#{File.join(basedir, "../spec_helper.rb")}\""
|
58
|
+
end
|
59
|
+
|
60
|
+
it "ignores not-existing backup paths" do
|
61
|
+
Duplicati::Backup.new(:backup_paths => ["/foo/bar", basedir, "/baz/bar"], :backup_store_path => "", :inclusion_filters => [], :exclusion_filters => []).
|
62
|
+
command.split(" ")[2].should == "\"#{basedir}\""
|
63
|
+
end
|
64
|
+
|
65
|
+
it "works with inclusion filters" do
|
66
|
+
command_parts = Duplicati::Backup.new(:backup_paths => [], :backup_store_path => "", :inclusion_filters => [/a\.exe/, /b\.exe/]).
|
67
|
+
command.split(" ")
|
68
|
+
|
69
|
+
command_parts[5].should == "--include-regexp=\"a\\.exe\""
|
70
|
+
command_parts[6].should == "--include-regexp=\"b\\.exe\""
|
71
|
+
end
|
72
|
+
|
73
|
+
it "works with exclusion filters" do
|
74
|
+
command_parts = Duplicati::Backup.new(:backup_paths => [], :backup_store_path => "", :exclusion_filters => [/a\.exe/, /b\.exe/]).
|
75
|
+
command.split(" ")
|
76
|
+
|
77
|
+
command_parts[5].should == "--exclude-regexp=\"a\\.exe\""
|
78
|
+
command_parts[6].should == "--exclude-regexp=\"b\\.exe\""
|
79
|
+
end
|
80
|
+
|
81
|
+
it "works with inclusion and exclusion filters together" do
|
82
|
+
command_parts = Duplicati::Backup.new(:backup_paths => [], :backup_store_path => "", :inclusion_filters => [/a/, /b/], :exclusion_filters => [/c/, /d/]).
|
83
|
+
command.split(" ")
|
84
|
+
|
85
|
+
command_parts[5].should == "--include-regexp=\"a\""
|
86
|
+
command_parts[6].should == "--include-regexp=\"b\""
|
87
|
+
command_parts[7].should == "--exclude-regexp=\"c\""
|
88
|
+
command_parts[8].should == "--exclude-regexp=\"d\""
|
89
|
+
end
|
90
|
+
|
91
|
+
it "does not include filters into command when they're not specified" do
|
92
|
+
command = Duplicati::Backup.new(:backup_paths => [], :backup_store_path => "").command
|
93
|
+
command.should_not include("--include-regexp")
|
94
|
+
command.should_not include("--exclude-regexp")
|
95
|
+
end
|
96
|
+
|
23
97
|
end
|
24
98
|
end
|
data/spec/duplicati_spec.rb
CHANGED
@@ -40,12 +40,40 @@ describe Duplicati do
|
|
40
40
|
Duplicati.new(:foo => true).opts[:foo].should be_true
|
41
41
|
end
|
42
42
|
|
43
|
+
it "has no inclusion filters by default" do
|
44
|
+
Duplicati.new.opts[:inclusion_filters].should == []
|
45
|
+
end
|
46
|
+
|
47
|
+
it "allows to specify a single inclusion filter" do
|
48
|
+
Duplicati.new(:inclusion_filter => /aa/).opts[:inclusion_filters].should == [/aa/]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "allows to specify multiple inclusion filters" do
|
52
|
+
Duplicati.new(:inclusion_filters => [/aa/, /bb/]).opts[:inclusion_filters].should == [/aa/, /bb/]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "will set up an 'exclude all others files' exclusion filter automatically when inclusion filter is used" do
|
56
|
+
Duplicati.new(:inclusion_filter => /aa/).opts[:exclusion_filters].should == [%r{.*[^\\/]}]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "has no exclusion filters by default" do
|
60
|
+
Duplicati.new.opts[:exclusion_filters].should == []
|
61
|
+
end
|
62
|
+
|
63
|
+
it "allows to specify a single exclusion filter" do
|
64
|
+
Duplicati.new(:exclusion_filter => /aa/).opts[:exclusion_filters].should == [/aa/]
|
65
|
+
end
|
66
|
+
|
67
|
+
it "allows to specify multiple exclusion filters" do
|
68
|
+
Duplicati.new(:exclusion_filters => [/aa/, /bb/]).opts[:exclusion_filters].should == [/aa/, /bb/]
|
69
|
+
end
|
70
|
+
|
43
71
|
end
|
44
72
|
|
45
73
|
context "#backup" do
|
46
74
|
it "executes the backup command" do
|
47
75
|
Duplicati::Backup.any_instance.should_receive(:command).and_return("backup command")
|
48
|
-
duplicati = Duplicati.new
|
76
|
+
duplicati = Duplicati.new(:backup_paths => [], :backup_store_path => "")
|
49
77
|
duplicati.should_receive(:execute).with("backup command")
|
50
78
|
|
51
79
|
duplicati.backup
|
@@ -58,6 +86,8 @@ describe Duplicati do
|
|
58
86
|
:backup_paths => ["foo", "bar"],
|
59
87
|
:backup_encryption_key => "secret",
|
60
88
|
:backup_store_path => "baz",
|
89
|
+
:inclusion_filters => [],
|
90
|
+
:exclusion_filters => [],
|
61
91
|
:log_path => "tmp"
|
62
92
|
}
|
63
93
|
expected_formatted_options = options.dup
|
@@ -74,7 +104,7 @@ describe Duplicati do
|
|
74
104
|
Duplicati::Backup.any_instance.should_receive(:command).and_return("backup command")
|
75
105
|
Duplicati.any_instance.should_receive(:execute).with("backup command")
|
76
106
|
|
77
|
-
Duplicati.backup
|
107
|
+
Duplicati.backup(:backup_paths => [], :backup_store_path => "")
|
78
108
|
end
|
79
109
|
end
|
80
110
|
|
data/spec/spec_helper.rb
CHANGED
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.2
|
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: 2012-12-
|
12
|
+
date: 2012-12-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|