duplicati 0.0.1

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/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ repo_token: nVBmrsBEd9K48oqRBAijkavSfMKtxW82w
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - ruby-head
6
+ notifications:
7
+ recipients:
8
+ - jarmo.p@gmail.com
9
+ matrix:
10
+ allow_failures:
11
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'coveralls', :require => false
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jarmo Pertman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # Duplicati
2
+ [![Build Status](https://secure.travis-ci.org/jarmo/duplicati-rb.png)](http://travis-ci.org/jarmo/duplicati-rb)
3
+ [![Coverage](https://coveralls.io/repos/jarmo/duplicati-rb/badge.png?branch=master)](https://coveralls.io/r/jarmo/duplicati-rb)
4
+
5
+ This gem is a [Duplicati](http://duplicati.com) backup utility wrapper written in Ruby with easier API and sensible configuration defaults compared to Duplicati's own utilities.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'duplicati'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install duplicati
20
+
21
+ ## Usage
22
+
23
+ ````
24
+ require "duplicati"
25
+
26
+ Duplicati.backup(
27
+ :backup_paths => ["/foo/bar", "/foo/baz"],
28
+ :backup_store_path => "file:///backup",
29
+ :backup_encryption_key => "very-secret-key"
30
+ )
31
+ ````
32
+
33
+ Refer to [Duplicati documentation](http://duplicati.com/howtos) for different backup store locations.
34
+
35
+ ## Limitations
36
+
37
+ * Currently only backup is supported. Use command line or GUI utility directly for restoring.
38
+ * You need to start Ruby with administrative privileges under Windows to backup files in use.
39
+
40
+ ## Contributing
41
+
42
+ 1. Fork it
43
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
44
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
45
+ 4. Push to the branch (`git push origin my-new-feature`)
46
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/duplicati.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'duplicati/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "duplicati"
8
+ gem.version = Duplicati::VERSION
9
+ gem.authors = ["Jarmo Pertman"]
10
+ gem.email = ["jarmo.p@gmail.com"]
11
+ gem.description = %q{Duplicati backup utility wrapper in Ruby with easier API and sensible configuration defaults.}
12
+ gem.summary = %q{Duplicati backup utility wrapper in Ruby with easier API and sensible configuration defaults.}
13
+ gem.homepage = "https://github.com/jarmo/duplicati-rb"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency "rake"
21
+ gem.add_development_dependency "rspec"
22
+ end
@@ -0,0 +1,22 @@
1
+ class Duplicati
2
+ class Backup
3
+ def initialize(opts={})
4
+ @opts = opts
5
+ end
6
+
7
+ def command
8
+ %Q["#{@opts[:duplicati_path]}" backup "#{(@opts[:backup_paths] || []).join(File::PATH_SEPARATOR)}" "#{@opts[:backup_store_path]}"
9
+ --passphrase="#{@opts[:backup_encryption_key]}"
10
+ --auto-cleanup
11
+ --full-if-older-than=1M
12
+ --usn-policy=on
13
+ --snapshot-policy=on
14
+ --full-if-sourcefolder-changed
15
+ 2>&1 1>> "#{@opts[:log_path]}" &&
16
+
17
+ "#{@opts[:duplicati_path]}" delete-all-but-n 5 "#{@opts[:backup_store_path]}"
18
+ --force
19
+ 2>&1 1>> "#{@opts[:log_path]}"]
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ class Duplicati
2
+ module Notification
3
+ class Growl
4
+ class << self
5
+ def notify(success)
6
+ return unless load_gem
7
+
8
+ growl = GNTP.new("Backup")
9
+ growl.register(:notifications => [{
10
+ :name => "backup-notify",
11
+ :enabled => true,
12
+ }])
13
+
14
+ growl.notify(
15
+ :name => "backup-notify",
16
+ :title => "Backup",
17
+ :text => success ? "Backup successfully finished!" : "Backup failed!",
18
+ :icon => File.expand_path(success ? "success.png" : "failed.png", File.dirname(__FILE__))
19
+ )
20
+ rescue => e
21
+ Kernel.warn "Failed to notify via Growl: #{e.message}"
22
+ end
23
+
24
+ private
25
+
26
+ def load_gem
27
+ require "ruby_gntp"
28
+ true
29
+ rescue LoadError
30
+ Kernel.warn "ruby_gntp gem is not installed, which is needed for Growl notifications!"
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ class Duplicati
2
+ VERSION = "0.0.1"
3
+ end
data/lib/duplicati.rb ADDED
@@ -0,0 +1,59 @@
1
+ require File.expand_path("duplicati/version", File.dirname(__FILE__))
2
+ require File.expand_path("duplicati/backup", File.dirname(__FILE__))
3
+ require File.expand_path("duplicati/notification/growl", File.dirname(__FILE__))
4
+
5
+ class Duplicati
6
+
7
+ class << self
8
+ def backup(opts={})
9
+ new(opts).backup
10
+ end
11
+ end
12
+
13
+ attr_reader :opts, :execution_success
14
+
15
+ def initialize(opts={})
16
+ opts[:log_path] ||= "duplicati.log"
17
+ opts[:duplicati_path] = duplicati_path(opts[:duplicati_path])
18
+ opts[:notifications] ||= [Notification::Growl]
19
+ @opts = opts
20
+ end
21
+
22
+ def backup
23
+ execute Backup.new(
24
+ options :duplicati_path, :backup_paths, :backup_store_path, :backup_encryption_key, :log_path
25
+ ).command
26
+ end
27
+
28
+ private
29
+
30
+ def duplicati_path(path_from_options)
31
+ path = path_from_options || ENV["DUPLICATI_PATH"] || "/Program Files/Duplicati"
32
+ File.join(path, "Duplicati.CommandLine")
33
+ end
34
+
35
+ def execute(command)
36
+ old_log_file_size = File.read(@opts[:log_path]).strip.size rescue 0
37
+ @execution_success = system(format command) && File.read(@opts[:log_path]).strip.size > old_log_file_size
38
+ notify
39
+ end
40
+
41
+ def options(*options_to_extract)
42
+ options_to_extract.reduce({}) do |memo, option|
43
+ memo[option] = @opts[option]
44
+ memo
45
+ end
46
+ end
47
+
48
+ def format(command)
49
+ command.gsub($/, "").squeeze(" ")
50
+ end
51
+
52
+ def notify
53
+ @opts[:notifications].each do |notification|
54
+ notification.notify @execution_success
55
+ end
56
+ end
57
+
58
+ end
59
+
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ describe Duplicati::Backup do
4
+ it "#command" do
5
+ Duplicati::Backup.new(
6
+ :duplicati_path => "/bin/duplicati-commandline",
7
+ :backup_paths => ["/foo/bar", "/baz/bar"],
8
+ :backup_store_path => "file:///foo/backup",
9
+ :backup_encryption_key => "secret",
10
+ :log_path => "/zzz/output.log"
11
+ ).command.should == %Q["/bin/duplicati-commandline" backup "/foo/bar#{File::PATH_SEPARATOR}/baz/bar" "file:///foo/backup"
12
+ --passphrase="secret"
13
+ --auto-cleanup
14
+ --full-if-older-than=1M
15
+ --usn-policy=on
16
+ --snapshot-policy=on
17
+ --full-if-sourcefolder-changed
18
+ 2>&1 1>> "/zzz/output.log" &&
19
+
20
+ "/bin/duplicati-commandline" delete-all-but-n 5 "file:///foo/backup"
21
+ --force
22
+ 2>&1 1>> "/zzz/output.log"]
23
+ end
24
+ end
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+
3
+ describe Duplicati::Notification::Growl do
4
+
5
+ before do
6
+ Kernel.stub(:warn)
7
+ Object.any_instance.stub(:require).with("ruby_gntp")
8
+ GNTP = double('gntp').as_null_object unless defined?(GNTP) && GNTP.null_object?
9
+ end
10
+
11
+ let(:growl) { subject.class }
12
+
13
+ it "notifies with success message" do
14
+ GNTP.should_receive(:notify).with notification_args("Backup successfully finished!", "success.png")
15
+ growl.notify(true)
16
+ end
17
+
18
+ it "notifies with failure message" do
19
+ GNTP.should_receive(:notify).with notification_args("Backup failed!", "failed.png")
20
+ growl.notify(false)
21
+ end
22
+
23
+ it "does not blow up when ruby_gntp gem is not installed" do
24
+ Object.any_instance.unstub(:require)
25
+ GNTP.should_not_receive(:notify)
26
+
27
+ expect {
28
+ growl.notify(false)
29
+ }.to_not raise_error
30
+ end
31
+
32
+ it "does not blow up when notifying fails" do
33
+ GNTP.stub(:register) { raise "foo" }
34
+
35
+ expect {
36
+ growl.notify(false)
37
+ }.to_not raise_error
38
+ end
39
+
40
+ def notification_args(text, icon)
41
+ {
42
+ :name => "backup-notify",
43
+ :title => "Backup",
44
+ :text => text,
45
+ :icon => File.expand_path(icon, File.join(File.dirname(__FILE__), "../../../lib/duplicati/notification"))
46
+ }
47
+ end
48
+
49
+ end
@@ -0,0 +1,140 @@
1
+ require "spec_helper"
2
+
3
+ describe Duplicati do
4
+ context "#initialize" do
5
+
6
+ it "has specified default log path" do
7
+ Duplicati.new.opts[:log_path].should == "duplicati.log"
8
+ end
9
+
10
+ it "allows to specify log path" do
11
+ Duplicati.new(:log_path => "/foo/bar").opts[:log_path].should == "/foo/bar"
12
+ end
13
+
14
+ it "has default duplicati path" do
15
+ Duplicati.new.opts[:duplicati_path].should == "/Program Files/Duplicati/Duplicati.CommandLine"
16
+ end
17
+
18
+ it "allows to specify duplicati path via options" do
19
+ Duplicati.new(:duplicati_path => "/zzz/baz").opts[:duplicati_path].should == "/zzz/baz/Duplicati.CommandLine"
20
+ end
21
+
22
+ it "allows to specify duplicati path via environment variable" do
23
+ begin
24
+ ENV["DUPLICATI_PATH"] = "/env/path"
25
+ Duplicati.new.opts[:duplicati_path].should == "/env/path/Duplicati.CommandLine"
26
+ ensure
27
+ ENV.delete "DUPLICATI_PATH"
28
+ end
29
+ end
30
+
31
+ it "has default notifications" do
32
+ Duplicati.new.opts[:notifications].should == [Duplicati::Notification::Growl]
33
+ end
34
+
35
+ it "allows to specify notifications" do
36
+ Duplicati.new(:notifications => [Object]).opts[:notifications].should == [Object]
37
+ end
38
+
39
+ it "allows to specify other options" do
40
+ Duplicati.new(:foo => true).opts[:foo].should be_true
41
+ end
42
+
43
+ end
44
+
45
+ context "#backup" do
46
+ it "executes the backup command" do
47
+ Duplicati::Backup.any_instance.should_receive(:command).and_return("backup command")
48
+ duplicati = Duplicati.new
49
+ duplicati.should_receive(:execute).with("backup command")
50
+
51
+ duplicati.backup
52
+ end
53
+
54
+ it "proxies options to backup command" do
55
+ duplicati_path = "/foo/bar"
56
+ options = {
57
+ :duplicati_path => duplicati_path,
58
+ :backup_paths => ["foo", "bar"],
59
+ :backup_encryption_key => "secret",
60
+ :backup_store_path => "baz",
61
+ :log_path => "tmp"
62
+ }
63
+ expected_formatted_options = options.dup
64
+ expected_formatted_options[:duplicati_path] += "/Duplicati.CommandLine"
65
+ Duplicati::Backup.should_receive(:new).with(expected_formatted_options).and_return(double('backup').as_null_object)
66
+ Duplicati.any_instance.stub(:execute)
67
+
68
+ Duplicati.new(options).backup
69
+ end
70
+ end
71
+
72
+ context ".backup" do
73
+ it "is a convenience method for .new#backup" do
74
+ Duplicati::Backup.any_instance.should_receive(:command).and_return("backup command")
75
+ Duplicati.any_instance.should_receive(:execute).with("backup command")
76
+
77
+ Duplicati.backup
78
+ end
79
+ end
80
+
81
+ context "#execute" do
82
+
83
+ before do
84
+ Duplicati.any_instance.stub(:notify)
85
+ end
86
+
87
+ it "executes the command" do
88
+ cmd = "multiline
89
+ command with spaces"
90
+ Object.any_instance.should_receive(:system).with("multiline command with spaces")
91
+
92
+ Duplicati.new(:log_path => "foo").send(:execute, cmd)
93
+ end
94
+
95
+ context "#execution_success" do
96
+ it "is false when command itself fails" do
97
+ Object.any_instance.should_receive(:system).and_return false
98
+
99
+ duplicati = Duplicati.new
100
+ duplicati.send(:execute, "")
101
+ duplicati.execution_success.should be_false
102
+ end
103
+
104
+ it "is false when command succeeds and log file size does not increase" do
105
+ Object.any_instance.should_receive(:system).and_return true
106
+ File.should_receive(:read).with("foo").and_return "", ""
107
+
108
+ duplicati = Duplicati.new :log_path => "foo"
109
+ duplicati.send(:execute, "")
110
+ duplicati.execution_success.should be_false
111
+ end
112
+
113
+ it "is true when command succeeds and log file size increases" do
114
+ Object.any_instance.should_receive(:system).and_return true
115
+ File.should_receive(:read).with("foo").and_return "", "output"
116
+
117
+ duplicati = Duplicati.new :log_path => "foo"
118
+ duplicati.send(:execute, "")
119
+ duplicati.execution_success.should be_true
120
+ end
121
+ end
122
+
123
+ context "#notify" do
124
+ before do
125
+ Duplicati.any_instance.unstub(:notify)
126
+ end
127
+
128
+ it "notifies with all possible notifications" do
129
+ Object.any_instance.stub(:system).and_return false
130
+ notification1 = double('notification1').as_null_object
131
+ notification2 = double('notification2').as_null_object
132
+
133
+ notification1.should_receive(:notify).with(false)
134
+ notification2.should_receive(:notify).with(false)
135
+ Duplicati.new(:notifications => [notification1, notification2]).send(:execute, "")
136
+ end
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,9 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require "duplicati"
5
+
6
+ RSpec.configure do |config|
7
+ config.color = true
8
+ config.order = :random
9
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: duplicati
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jarmo Pertman
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Duplicati backup utility wrapper in Ruby with easier API and sensible
47
+ configuration defaults.
48
+ email:
49
+ - jarmo.p@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .coveralls.yml
55
+ - .gitignore
56
+ - .travis.yml
57
+ - Gemfile
58
+ - LICENSE.txt
59
+ - README.md
60
+ - Rakefile
61
+ - duplicati.gemspec
62
+ - lib/duplicati.rb
63
+ - lib/duplicati/backup.rb
64
+ - lib/duplicati/notification/failed.png
65
+ - lib/duplicati/notification/growl.rb
66
+ - lib/duplicati/notification/success.png
67
+ - lib/duplicati/version.rb
68
+ - spec/duplicati/backup_spec.rb
69
+ - spec/duplicati/notification/growl_spec.rb
70
+ - spec/duplicati_spec.rb
71
+ - spec/spec_helper.rb
72
+ homepage: https://github.com/jarmo/duplicati-rb
73
+ licenses: []
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 1.8.24
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Duplicati backup utility wrapper in Ruby with easier API and sensible configuration
96
+ defaults.
97
+ test_files:
98
+ - spec/duplicati/backup_spec.rb
99
+ - spec/duplicati/notification/growl_spec.rb
100
+ - spec/duplicati_spec.rb
101
+ - spec/spec_helper.rb
102
+ has_rdoc: