rsyncbackup 1.0.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.
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +49 -0
- data/LICENSE.txt +7 -0
- data/README.md +33 -0
- data/README.rdoc +50 -0
- data/Rakefile +66 -0
- data/bin/rsyncbackup +39 -0
- data/features/backup.feature +24 -0
- data/features/rsyncbackup.feature +20 -0
- data/features/step_definitions/backup_steps.rb +15 -0
- data/features/step_definitions/rsyncbackup_steps.rb +1 -0
- data/features/support/env.rb +16 -0
- data/features/test_files/source/five +0 -0
- data/features/test_files/source/four +0 -0
- data/features/test_files/source/one +0 -0
- data/features/test_files/source/six +0 -0
- data/features/test_files/source/three +0 -0
- data/features/test_files/source/two +0 -0
- data/features/test_files/target/.gitignore +2 -0
- data/lib/rsyncbackup/utilities.rb +86 -0
- data/lib/rsyncbackup/version.rb +4 -0
- data/lib/rsyncbackup.rb +84 -0
- data/rsyncbackup-0.0.1.gem +0 -0
- data/rsyncbackup.gemspec +24 -0
- data/spec/rsyncbackup_spec.rb +86 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/utilities_spec.rb +110 -0
- metadata +170 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rsyncbackup (0.0.1)
|
5
|
+
methadone (~> 1.2.4)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
aruba (0.5.1)
|
11
|
+
childprocess (~> 0.3.6)
|
12
|
+
cucumber (>= 1.1.1)
|
13
|
+
rspec-expectations (>= 2.7.0)
|
14
|
+
builder (3.1.4)
|
15
|
+
childprocess (0.3.7)
|
16
|
+
ffi (~> 1.0, >= 1.0.6)
|
17
|
+
cucumber (1.2.1)
|
18
|
+
builder (>= 2.1.2)
|
19
|
+
diff-lcs (>= 1.1.3)
|
20
|
+
gherkin (~> 2.11.0)
|
21
|
+
json (>= 1.4.6)
|
22
|
+
diff-lcs (1.1.3)
|
23
|
+
ffi (1.3.1)
|
24
|
+
gherkin (2.11.6)
|
25
|
+
json (>= 1.7.6)
|
26
|
+
json (1.7.6)
|
27
|
+
methadone (1.2.4)
|
28
|
+
bundler
|
29
|
+
rake (0.9.6)
|
30
|
+
rdoc (3.12.1)
|
31
|
+
json (~> 1.4)
|
32
|
+
rspec (2.12.0)
|
33
|
+
rspec-core (~> 2.12.0)
|
34
|
+
rspec-expectations (~> 2.12.0)
|
35
|
+
rspec-mocks (~> 2.12.0)
|
36
|
+
rspec-core (2.12.2)
|
37
|
+
rspec-expectations (2.12.1)
|
38
|
+
diff-lcs (~> 1.1.3)
|
39
|
+
rspec-mocks (2.12.2)
|
40
|
+
|
41
|
+
PLATFORMS
|
42
|
+
ruby
|
43
|
+
|
44
|
+
DEPENDENCIES
|
45
|
+
aruba
|
46
|
+
rake (~> 0.9.2)
|
47
|
+
rdoc
|
48
|
+
rspec
|
49
|
+
rsyncbackup!
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2013 YOUR NAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Rsyncbackup
|
2
|
+
|
3
|
+
Yet another rsyncbackup script, this time in ruby.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rsyncbackup'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rsyncbackup
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
rsyncbackup [options] SOURCE TARGET
|
22
|
+
|
23
|
+
`SOURCE` and `TARGET` can be any valid rsync spec.
|
24
|
+
|
25
|
+
Consult the --help for details.
|
26
|
+
|
27
|
+
## Contributing
|
28
|
+
|
29
|
+
1. Fork it, clone it
|
30
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
31
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
32
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
33
|
+
5. Create new Pull Request
|
data/README.rdoc
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
= rsyncbackup - Yet another rsync backup script, this time in ruby, as a methadone CLI app
|
2
|
+
|
3
|
+
Author:: Tamara Temple <tamara@tamaratemple.com>
|
4
|
+
Copyright:: Copyright (c) 2013 Tamara Temple
|
5
|
+
License:: mit, see LICENSE.txt
|
6
|
+
|
7
|
+
Everyone writes at least one rsync backup script; I've written a
|
8
|
+
few. This time, it's in ruby, utilizing davetron5000's methadone gem
|
9
|
+
to craete a nice command line application with arguments, help
|
10
|
+
functions and even testing built on.
|
11
|
+
|
12
|
+
== Install
|
13
|
+
|
14
|
+
gem install rsyncbackup
|
15
|
+
|
16
|
+
--or--
|
17
|
+
|
18
|
+
git clone https://github.com/tamouse/rsyncbackup-rb
|
19
|
+
|
20
|
+
== Synopsis (breif, use help for more complete info)
|
21
|
+
|
22
|
+
rsyncbackup [options] source target
|
23
|
+
|
24
|
+
source and target can be any legal rsync descriptor.
|
25
|
+
|
26
|
+
== Description
|
27
|
+
|
28
|
+
`rsyncbackup` makes dated backups by hard-linking in the `target` directory.
|
29
|
+
|
30
|
+
== Examples
|
31
|
+
|
32
|
+
Backup your phone when mounted to your desktop:
|
33
|
+
|
34
|
+
rsyncbackup /media/MOT /media/Backup/Droid3Backup
|
35
|
+
|
36
|
+
Back up your pmwiki excluding worker files:
|
37
|
+
|
38
|
+
rsyncbackup --exclusions pmwiki.exclude youruser@yourserver.com:path/to/pmwiki /media/Backup/pmwiki
|
39
|
+
|
40
|
+
== Links
|
41
|
+
|
42
|
+
* {Source on Github}[https://github.com/tamouse/rsyncbackup-rb]
|
43
|
+
* RDoc[LINK TO RDOC.INFO] (coming soon!)
|
44
|
+
|
45
|
+
== Contributing
|
46
|
+
|
47
|
+
1. Fork it!
|
48
|
+
2. Crack a branch
|
49
|
+
3. Make your changes.
|
50
|
+
4. Submit a pull request.
|
data/Rakefile
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
def dump_load_path
|
2
|
+
puts $LOAD_PATH.join("\n")
|
3
|
+
found = nil
|
4
|
+
$LOAD_PATH.each do |path|
|
5
|
+
if File.exists?(File.join(path,"rspec"))
|
6
|
+
puts "Found rspec in #{path}"
|
7
|
+
if File.exists?(File.join(path,"rspec","core"))
|
8
|
+
puts "Found core"
|
9
|
+
if File.exists?(File.join(path,"rspec","core","rake_task"))
|
10
|
+
puts "Found rake_task"
|
11
|
+
found = path
|
12
|
+
else
|
13
|
+
puts "!! no rake_task"
|
14
|
+
end
|
15
|
+
else
|
16
|
+
puts "!!! no core"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
if found.nil?
|
21
|
+
puts "Didn't find rspec/core/rake_task anywhere"
|
22
|
+
else
|
23
|
+
puts "Found in #{path}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
require 'bundler'
|
27
|
+
require 'rake/clean'
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rspec/core/rake_task'
|
31
|
+
rescue LoadError
|
32
|
+
dump_load_path
|
33
|
+
raise
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'cucumber'
|
37
|
+
require 'cucumber/rake/task'
|
38
|
+
gem 'rdoc' # we need the installed RDoc gem, not the system one
|
39
|
+
require 'rdoc/task'
|
40
|
+
|
41
|
+
include Rake::DSL
|
42
|
+
|
43
|
+
Bundler::GemHelper.install_tasks
|
44
|
+
|
45
|
+
|
46
|
+
RSpec::Core::RakeTask.new do |t|
|
47
|
+
# Put spec opts in a file named .rspec in root
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
CUKE_RESULTS = 'results.html'
|
52
|
+
CLEAN << CUKE_RESULTS
|
53
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
54
|
+
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty --no-source -x"
|
55
|
+
t.fork = false
|
56
|
+
end
|
57
|
+
|
58
|
+
Rake::RDocTask.new do |rd|
|
59
|
+
|
60
|
+
rd.main = "README.rdoc"
|
61
|
+
|
62
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
63
|
+
end
|
64
|
+
|
65
|
+
task :default => [:spec,:features]
|
66
|
+
|
data/bin/rsyncbackup
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'methadone'
|
5
|
+
require 'rsyncbackup'
|
6
|
+
|
7
|
+
class App
|
8
|
+
include Methadone::Main
|
9
|
+
include Methadone::CLILogging
|
10
|
+
|
11
|
+
if logger.info?
|
12
|
+
logger.level=Logger::WARN
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
main do |source, target|
|
17
|
+
options[:source] = source
|
18
|
+
options[:target] = target
|
19
|
+
syncer = Rsyncbackup.new(options)
|
20
|
+
syncer.run
|
21
|
+
syncer.finalize
|
22
|
+
end
|
23
|
+
|
24
|
+
description "Yet another rsync backup script, this time in ruby"
|
25
|
+
|
26
|
+
on("-e FILE","--exclusions","Exclusions file")
|
27
|
+
on("-d","--[no-]dry-run","Mark this as a dry run (or not)")
|
28
|
+
on("-v","--verbose","Be chatty")
|
29
|
+
|
30
|
+
|
31
|
+
arg :source, "Source to backup from"
|
32
|
+
arg :target, "Target backup directory"
|
33
|
+
|
34
|
+
version Rsyncbackup::VERSION
|
35
|
+
|
36
|
+
use_log_level_option
|
37
|
+
|
38
|
+
go!
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: Perform Backups
|
2
|
+
In order to ensure files are correctly backed up
|
3
|
+
I will need to run the backup script
|
4
|
+
|
5
|
+
Scenario: Run a backup from source to target
|
6
|
+
Given a directory named "source"
|
7
|
+
Given a file named "source/one" with:
|
8
|
+
"""
|
9
|
+
xxxx
|
10
|
+
"""
|
11
|
+
Given a file named "source/two" with:
|
12
|
+
"""
|
13
|
+
xxxx
|
14
|
+
"""
|
15
|
+
Given a directory named "target"
|
16
|
+
When I successfully run `rsyncbackup --log-level debug source target`
|
17
|
+
Then a file named "target/.lastfull" should exist
|
18
|
+
And a directory named "target/.incomplete" should not exist
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Feature: help lists all options and arguments
|
2
|
+
In order to ensure user knows what to do
|
3
|
+
I want to display a useful help
|
4
|
+
So I know how to run the rsync backup
|
5
|
+
|
6
|
+
Scenario: App just runs
|
7
|
+
When I get help for "rsyncbackup"
|
8
|
+
Then the exit status should be 0
|
9
|
+
And the banner should be present
|
10
|
+
And the banner should document that this app takes options
|
11
|
+
And the following options should be documented:
|
12
|
+
|--[no-]dry-run|
|
13
|
+
|--exclusions|
|
14
|
+
|--help|
|
15
|
+
|--log-level|
|
16
|
+
|--verbose|
|
17
|
+
|--version|
|
18
|
+
And the banner should document that this app's arguments are:
|
19
|
+
|source|
|
20
|
+
|target|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
= BACKUP_STEPS.RB
|
4
|
+
|
5
|
+
*Author*:: Tamara Temple <tamara@tamaratemple.com>
|
6
|
+
*Since*:: 2013-02-10
|
7
|
+
*Copyright*:: (c) 2013 Tamara Temple Web Development
|
8
|
+
*License*:: MIT
|
9
|
+
|
10
|
+
=end
|
11
|
+
|
12
|
+
Then /^"(.*?)" should have a new backup$/ do |arg1|
|
13
|
+
last_dir = File.read("#{arg1}/.lastfull")
|
14
|
+
File.directory?("#{arg1}/last_dir")
|
15
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
# Put your step definitions here
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'aruba/cucumber'
|
2
|
+
require 'methadone/cucumber'
|
3
|
+
|
4
|
+
ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
|
5
|
+
LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
|
6
|
+
|
7
|
+
Before do
|
8
|
+
# Using "announce" causes massive warnings on 1.9.2
|
9
|
+
@puts = true
|
10
|
+
@original_rubylib = ENV['RUBYLIB']
|
11
|
+
ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
After do
|
15
|
+
ENV['RUBYLIB'] = @original_rubylib
|
16
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,86 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
= UTILITIES.RB
|
4
|
+
|
5
|
+
*Author*:: Tamara Temple <tamara@tamaratemple.com>
|
6
|
+
*Since*:: 2013-02-10
|
7
|
+
*Copyright*:: (c) 2013 Tamara Temple Web Development
|
8
|
+
*License*:: MIT
|
9
|
+
|
10
|
+
=end
|
11
|
+
|
12
|
+
require 'methadone'
|
13
|
+
|
14
|
+
class Rsyncbackup
|
15
|
+
|
16
|
+
# returns the command string to execute with all parameters set
|
17
|
+
def build_command
|
18
|
+
|
19
|
+
cmd = []
|
20
|
+
cmd << options[:rsync_cmd]
|
21
|
+
cmd << '--verbose --progress --itemize-changes' if logger.info?
|
22
|
+
cmd << '--archive' if options[:archive]
|
23
|
+
cmd << '--one-file-system' if options[:one_file_system]
|
24
|
+
cmd << '--hard-links' if options[:hard_links]
|
25
|
+
cmd << '--human-readable' if options[:human_readable]
|
26
|
+
cmd << '--inplace' if options[:inplace]
|
27
|
+
cmd << '--numeric-ids' if options[:numeric_ids]
|
28
|
+
cmd << '--delete' if options[:delete]
|
29
|
+
cmd << "--exclude-file #{options[:exclusions]}" if File.exist?(options[:exclusions])
|
30
|
+
cmd << "--link-dest #{options[:link_dest]}" if options[:link_dest]
|
31
|
+
cmd << options[:source]
|
32
|
+
cmd << temp_target_path
|
33
|
+
|
34
|
+
cmd.join(' ').tap{|t| debug "Command: #{t}" }
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
# returns the directory name of the last full backup
|
39
|
+
# returns nil otherwise
|
40
|
+
def last_full_backup
|
41
|
+
|
42
|
+
lastfull = "#{options[:target]}/.lastfull"
|
43
|
+
if File.exist?(lastfull)
|
44
|
+
last_full_directory = IO.readlines(lastfull).first.chomp
|
45
|
+
else
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
# returns the directory name for the current backup
|
52
|
+
# directory name consists of a time format: YYYY-MM-DDTHH-MM-SS
|
53
|
+
def backup_dir_name
|
54
|
+
@backup_dir_name ||= Time.now.strftime("%FT%H-%M-%S")
|
55
|
+
end
|
56
|
+
|
57
|
+
# returns the full target path, including backup directory name
|
58
|
+
def full_target_path
|
59
|
+
@full_target_path ||= options[:target]+"/"+backup_dir_name
|
60
|
+
end
|
61
|
+
|
62
|
+
# returns the temporary target path
|
63
|
+
def temp_target_path
|
64
|
+
@temp_target_path ||= options[:target]+"/.incomplete"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
# returns the path to the rsync executable
|
70
|
+
# If none found, raises an Exception
|
71
|
+
def rsync_executable
|
72
|
+
rsync = `which rsync`.chomp
|
73
|
+
raise "No rsync executable. Are you sure it\'s installed?" if rsync.empty?
|
74
|
+
rsync
|
75
|
+
end
|
76
|
+
|
77
|
+
# Strip the trailing directory separator from the rsync
|
78
|
+
# source or target.
|
79
|
+
#
|
80
|
+
# *s*:: string to strip
|
81
|
+
def strip_trailing_separator_if_any(s)
|
82
|
+
raise "not a String" unless s.is_a?(String)
|
83
|
+
s = s.gsub(%r{/$},'')
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/lib/rsyncbackup.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require "rsyncbackup/version"
|
2
|
+
require "rsyncbackup/utilities"
|
3
|
+
require 'open3'
|
4
|
+
require 'methadone'
|
5
|
+
|
6
|
+
class Rsyncbackup
|
7
|
+
include Methadone::CLILogging
|
8
|
+
|
9
|
+
attr_accessor :options
|
10
|
+
|
11
|
+
def initialize(opts={})
|
12
|
+
@options = {
|
13
|
+
dry_run: false,
|
14
|
+
exclusions: DEFAULT_EXCLUSIONS,
|
15
|
+
archive: true,
|
16
|
+
one_file_system: true,
|
17
|
+
hard_links: true,
|
18
|
+
human_readable: true,
|
19
|
+
inplace: true,
|
20
|
+
numeric_ids: true,
|
21
|
+
delete: true,
|
22
|
+
rsync_cmd: rsync_executable
|
23
|
+
}.merge(opts)
|
24
|
+
|
25
|
+
options[:source] = strip_trailing_separator_if_any(options[:source])
|
26
|
+
options[:target] = strip_trailing_separator_if_any(options[:target])
|
27
|
+
options[:link_dest] ||= last_full_backup
|
28
|
+
|
29
|
+
if logger.warn? && options[:verbose] == true
|
30
|
+
logger.level=Logger::INFO
|
31
|
+
end
|
32
|
+
|
33
|
+
debug "options: #{options.inspect}"
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def run
|
39
|
+
cmd = build_command
|
40
|
+
|
41
|
+
info "Rsync command: #{cmd}"
|
42
|
+
if options[:dry_run]
|
43
|
+
info "Dry run only"
|
44
|
+
end
|
45
|
+
|
46
|
+
if File.exist? temp_target_path
|
47
|
+
warn "Preexisting temporary target. Moving it aside."
|
48
|
+
File.rename temp_target_path, "#{temp_target_path}-#{"%0.4d" % Random.rand(1000)}"
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait|
|
53
|
+
stdin.close
|
54
|
+
until stdout.eof?
|
55
|
+
info stdout.gets
|
56
|
+
end
|
57
|
+
until stderr.eof?
|
58
|
+
errors = stderr.gets
|
59
|
+
end
|
60
|
+
result = wait.value
|
61
|
+
raise "Command failed. Return code: #{result}\n#{errors}" unless result == 0
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
def finalize
|
67
|
+
|
68
|
+
incomplete = "#{options[:target]}/.incomplete"
|
69
|
+
complete = "#{options[:target]}/#{backup_dir_name}"
|
70
|
+
|
71
|
+
if File.exist?(incomplete) &&
|
72
|
+
!File.exist?(complete)
|
73
|
+
File.rename(incomplete, complete)
|
74
|
+
end
|
75
|
+
|
76
|
+
File.open("#{options[:target]}/.lastfull",'w') do |fh|
|
77
|
+
fh.puts backup_dir_name
|
78
|
+
end
|
79
|
+
|
80
|
+
info "Backup saved in #{options[:target]}/#{backup_dir_name}"
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
end
|
Binary file
|
data/rsyncbackup.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rsyncbackup/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "rsyncbackup"
|
8
|
+
gem.version = Rsyncbackup::VERSION
|
9
|
+
gem.authors = ["Tamara Temple"]
|
10
|
+
gem.email = ["tamouse@gmail.com"]
|
11
|
+
gem.description = %q{Yet another rsyncbackup script, this time in ruby}
|
12
|
+
gem.summary = %q{Yet another rsyncbackup script, this time in ruby}
|
13
|
+
gem.homepage = "http://github.com/tamouse/rsyncbackup-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
|
+
gem.add_development_dependency('rdoc')
|
20
|
+
gem.add_development_dependency('aruba')
|
21
|
+
gem.add_development_dependency('rake', '~> 0.9.2')
|
22
|
+
gem.add_development_dependency('rspec')
|
23
|
+
gem.add_dependency('methadone', '~> 1.2.4')
|
24
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rsyncbackup'
|
3
|
+
|
4
|
+
describe Rsyncbackup do
|
5
|
+
|
6
|
+
context "Interface Checks" do
|
7
|
+
it { Rsyncbackup.should respond_to(:new) }
|
8
|
+
it { Rsyncbackup.new(source: 'source', target: 'target').should be_a(Rsyncbackup) }
|
9
|
+
end
|
10
|
+
|
11
|
+
context "Options Validity" do
|
12
|
+
let(:syncer) { Rsyncbackup.new(source: 'source', target: 'target') }
|
13
|
+
|
14
|
+
it "has a command" do
|
15
|
+
syncer.options[:rsync_cmd].should =~ /rsync/
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
context "#run" do
|
21
|
+
|
22
|
+
def expect_command_match(part_of_command)
|
23
|
+
Open3.stub(:popen3) do |command|
|
24
|
+
mismatch_message = "mismatch!! expected: #{part_of_command}. received: #{command}"
|
25
|
+
case part_of_command
|
26
|
+
when Regexp
|
27
|
+
raise mismatch_message unless command =~ part_of_command
|
28
|
+
when String
|
29
|
+
raise mismatch_message unless command.include?(part_of_command)
|
30
|
+
else
|
31
|
+
raise mismatch_message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:source) {'features/test_files/source' }
|
37
|
+
let(:target) {'features/test_files/target'}
|
38
|
+
|
39
|
+
let(:syncer) { Rsyncbackup.new(source: source, target: target) }
|
40
|
+
|
41
|
+
it { syncer.should respond_to(:run) }
|
42
|
+
|
43
|
+
it "should allow a different executable" do
|
44
|
+
exec="echo --"
|
45
|
+
syncer.options[:rsync_cmd] = exec
|
46
|
+
expect_command_match(exec)
|
47
|
+
syncer.run
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should allow a different exclusions file" do
|
51
|
+
exclusions=File.expand_path("myexclusions")
|
52
|
+
File.open(exclusions,'w') do |fh|
|
53
|
+
fh.puts "my exclusions go here"
|
54
|
+
end
|
55
|
+
syncer.options[:exclusions]=exclusions
|
56
|
+
expect_command_match("--exclude-file #{exclusions}")
|
57
|
+
syncer.run
|
58
|
+
File.unlink(exclusions)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
context "#finalize" do
|
64
|
+
|
65
|
+
let(:source) {'features/test_files/source' }
|
66
|
+
let(:target) {'features/test_files/target'}
|
67
|
+
|
68
|
+
let(:syncer) { Rsyncbackup.new(source: source , target: target ) }
|
69
|
+
it { syncer.should respond_to(:finalize) }
|
70
|
+
|
71
|
+
it "should rename temporary backup directory" do
|
72
|
+
syncer.run
|
73
|
+
syncer.finalize
|
74
|
+
File.exist?(File.join(target,'.lastfull')).should be_true
|
75
|
+
File.directory?(File.join(target,syncer.backup_dir_name)).should be_true
|
76
|
+
File.directory?(File.join(target,syncer.backup_dir_name,File.basename(source))).should be_true
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
|
+
config.run_all_when_everything_filtered = true
|
10
|
+
config.filter_run :focus
|
11
|
+
|
12
|
+
# Run specs in random order to surface order dependencies. If you find an
|
13
|
+
# order dependency and want to debug it, you can fix the order by providing
|
14
|
+
# the seed, which is printed after each run.
|
15
|
+
# --seed 1234
|
16
|
+
config.order = 'random'
|
17
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
= UTILITIES_SPEC.RB
|
4
|
+
|
5
|
+
*Author*:: Tamara Temple <tamara@tamaratemple.com>
|
6
|
+
*Since*:: 2013-02-10
|
7
|
+
*Copyright*:: (c) 2013 Tamara Temple Web Development
|
8
|
+
*License*:: MIT
|
9
|
+
|
10
|
+
=end
|
11
|
+
|
12
|
+
describe Rsyncbackup do
|
13
|
+
context "utilities" do
|
14
|
+
|
15
|
+
context "#build_command" do
|
16
|
+
let(:syncer) { Rsyncbackup.new(source: 'source', target: 'target') }
|
17
|
+
|
18
|
+
it { syncer.should respond_to(:build_command) }
|
19
|
+
|
20
|
+
it "should build a valid command" do
|
21
|
+
cmd = syncer.build_command
|
22
|
+
cmd.should =~ /--archive/
|
23
|
+
cmd.should =~ /--one-file-system/
|
24
|
+
cmd.should =~ /--hard-links/
|
25
|
+
cmd.should =~ /--human-readable/
|
26
|
+
cmd.should =~ /--inplace/
|
27
|
+
cmd.should =~ /--numeric-ids/
|
28
|
+
cmd.should =~ /--delete/
|
29
|
+
cmd.should =~ /source/
|
30
|
+
cmd.should =~ /target\/\.incomplete/
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "#last_full_backup" do
|
35
|
+
let (:syncer) { Rsyncbackup.new(source: 'source', target: '/tmp') }
|
36
|
+
|
37
|
+
it { syncer.should respond_to(:last_full_backup) }
|
38
|
+
|
39
|
+
it "should be nil if no last full backup" do
|
40
|
+
syncer.last_full_backup.should be_nil
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should be the directory name if there was a last full backup marker" do
|
44
|
+
last_full_directory = Time.new(2013,01,01,03,10,00).strftime("%FT%H-%M-%S")
|
45
|
+
last_full_file = "#{syncer.options[:target]}/.lastfull"
|
46
|
+
|
47
|
+
File.open(last_full_file,'w') do |fh|
|
48
|
+
fh.puts last_full_directory
|
49
|
+
end
|
50
|
+
|
51
|
+
syncer.last_full_backup.should == last_full_directory
|
52
|
+
|
53
|
+
File.unlink last_full_file
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
context "#backup_dir_name" do
|
59
|
+
let(:syncer) { Rsyncbackup.new(source: 'source', target: 'target') }
|
60
|
+
|
61
|
+
it { syncer.should respond_to(:backup_dir_name) }
|
62
|
+
|
63
|
+
it "should give a valid backup directory name: YYYY-MM-DDTHH-MM-SS" do
|
64
|
+
name = syncer.backup_dir_name
|
65
|
+
name.should =~ /(\d){4}-(\d){2}-(\d){2}T(\d){2}-(\d){2}-(\d){2}/
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "#strip_trailing_separator_if_any" do
|
70
|
+
|
71
|
+
context "with trailing separators" do
|
72
|
+
let(:syncer) { Rsyncbackup.new(source: 'source/', target: 'target/') }
|
73
|
+
|
74
|
+
it "should respond to #strip_trailing_separator_if_any" do
|
75
|
+
syncer.should respond_to(:strip_trailing_separator_if_any)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should remove trailing slash from source" do
|
79
|
+
syncer.strip_trailing_separator_if_any('source/').should == 'source'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should remove trailing slash from target" do
|
83
|
+
syncer.strip_trailing_separator_if_any('target/').should == 'target'
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
context "withOUT trailing separators" do
|
89
|
+
let(:syncer) { Rsyncbackup.new(source: 'source', target: 'target') }
|
90
|
+
|
91
|
+
it "should respond to #strip_trailing_separator_if_any" do
|
92
|
+
syncer.should respond_to(:strip_trailing_separator_if_any)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should remove trailing slash from source" do
|
96
|
+
syncer.strip_trailing_separator_if_any('source').should == 'source'
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should remove trailing slash from target" do
|
100
|
+
syncer.strip_trailing_separator_if_any('target').should == 'target'
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
metadata
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rsyncbackup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tamara Temple
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rdoc
|
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: aruba
|
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
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.9.2
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.2
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: methadone
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.2.4
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.2.4
|
94
|
+
description: Yet another rsyncbackup script, this time in ruby
|
95
|
+
email:
|
96
|
+
- tamouse@gmail.com
|
97
|
+
executables:
|
98
|
+
- rsyncbackup
|
99
|
+
extensions: []
|
100
|
+
extra_rdoc_files: []
|
101
|
+
files:
|
102
|
+
- .gitignore
|
103
|
+
- .rspec
|
104
|
+
- Gemfile
|
105
|
+
- Gemfile.lock
|
106
|
+
- LICENSE.txt
|
107
|
+
- README.md
|
108
|
+
- README.rdoc
|
109
|
+
- Rakefile
|
110
|
+
- bin/rsyncbackup
|
111
|
+
- features/backup.feature
|
112
|
+
- features/rsyncbackup.feature
|
113
|
+
- features/step_definitions/backup_steps.rb
|
114
|
+
- features/step_definitions/rsyncbackup_steps.rb
|
115
|
+
- features/support/env.rb
|
116
|
+
- features/test_files/source/five
|
117
|
+
- features/test_files/source/four
|
118
|
+
- features/test_files/source/one
|
119
|
+
- features/test_files/source/six
|
120
|
+
- features/test_files/source/three
|
121
|
+
- features/test_files/source/two
|
122
|
+
- features/test_files/target/.gitignore
|
123
|
+
- lib/rsyncbackup.rb
|
124
|
+
- lib/rsyncbackup/utilities.rb
|
125
|
+
- lib/rsyncbackup/version.rb
|
126
|
+
- rsyncbackup-0.0.1.gem
|
127
|
+
- rsyncbackup.gemspec
|
128
|
+
- spec/rsyncbackup_spec.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
- spec/utilities_spec.rb
|
131
|
+
homepage: http://github.com/tamouse/rsyncbackup-rb
|
132
|
+
licenses: []
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
145
|
+
requirements:
|
146
|
+
- - ! '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 1.8.24
|
152
|
+
signing_key:
|
153
|
+
specification_version: 3
|
154
|
+
summary: Yet another rsyncbackup script, this time in ruby
|
155
|
+
test_files:
|
156
|
+
- features/backup.feature
|
157
|
+
- features/rsyncbackup.feature
|
158
|
+
- features/step_definitions/backup_steps.rb
|
159
|
+
- features/step_definitions/rsyncbackup_steps.rb
|
160
|
+
- features/support/env.rb
|
161
|
+
- features/test_files/source/five
|
162
|
+
- features/test_files/source/four
|
163
|
+
- features/test_files/source/one
|
164
|
+
- features/test_files/source/six
|
165
|
+
- features/test_files/source/three
|
166
|
+
- features/test_files/source/two
|
167
|
+
- features/test_files/target/.gitignore
|
168
|
+
- spec/rsyncbackup_spec.rb
|
169
|
+
- spec/spec_helper.rb
|
170
|
+
- spec/utilities_spec.rb
|