coral_backup 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 166a85de67349ae7c3443bf59f11825895aa5808
4
+ data.tar.gz: 5fa3d934bc50506bcfc072a46a5ba05c2f820da0
5
+ SHA512:
6
+ metadata.gz: b4f6f9c58a9e2cfa1eb8ab3af3f272921aabcefa995c55f7014b876adb186a4c84e54a195775f056c015731c680544d31e6509c7685cfbc8796fd72453218cf9
7
+ data.tar.gz: b026ff1ff97901effe349c782ac286ff3509580e8d132a43dd48c33fbdbb3341c09d78af11966cd3e4b60f3cd49249ecbc53b1560c914fdd7dace4efbc2648c2
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/bundle/
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in coral_backup.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The zlib license (Zlib)
2
+
3
+ Copyright (c) 2015 OTSUKA Tatsuya
4
+
5
+ This software is provided 'as-is', without any express or implied
6
+ warranty. In no event will the authors be held liable for any damages
7
+ arising from the use of this software.
8
+
9
+ Permission is granted to anyone to use this software for any purpose,
10
+ including commercial applications, and to alter it and redistribute it
11
+ freely, subject to the following restrictions:
12
+
13
+ 1. The origin of this software must not be misrepresented; you must not
14
+ claim that you wrote the original software. If you use this software
15
+ in a product, an acknowledgment in the product documentation would be
16
+ appreciated but is not required.
17
+
18
+ 2. Altered source versions must be plainly marked as such, and must not be
19
+ misrepresented as being the original software.
20
+
21
+ 3. This notice may not be removed or altered from any source
22
+ distribution.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # Coral Backup
2
+
3
+ Coral Backup creates incremental backups of files that can be restored at a later date.
4
+
5
+ ## Installation
6
+
7
+ ```
8
+ $ gem install coral_backup
9
+ ```
10
+
11
+ ## Usage
12
+ ### Commands
13
+
14
+ * `coral add <ACTION>`: Add a new backup action
15
+ * `coral delete <ACTION>`: Delete the backup action
16
+ * `coral help [COMMAND]`: Describe available commands or one specific command
17
+ * `coral list`: Show all backup actions
18
+ * `coral run <ACTION>`: Run the backup action
19
+ * `coral show <ACTION>`: Show information about the backup action
20
+ * `coral version`: Print the version
21
+
22
+ ### Options
23
+
24
+ * `-d`, `--dry-run`: Show what would have been backed up, but do not back them up
25
+
26
+ ## Dependencies
27
+
28
+ * rsync 3.X.X binary
29
+
30
+ ## Development
31
+
32
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. Run `bundle exec coral` to use the gem in this directory, ignoring other installed copies of this gem.
33
+
34
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
35
+
36
+ ## Contributing
37
+
38
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Tatzyr/coral_backup.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "coral_backup"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'coral_backup/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "coral_backup"
8
+ spec.version = CoralBackup::VERSION
9
+ spec.authors = ["OTSUKA Tatsuya"]
10
+ spec.email = ["tatzyr@gmail.com"]
11
+
12
+ spec.summary = %q{Coral Backup creates incremental backups of files that can be restored at a later date.}
13
+ spec.homepage = "https://github.com/tatzyr/coral_backup"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.required_ruby_version = "~> 2.0"
21
+ spec.license = "Zlib"
22
+
23
+ spec.add_dependency "thor", "~> 0.19"
24
+ spec.add_dependency "toml-rb", "~> 0.3"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.10"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest"
29
+ end
data/exe/coral ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "coral_backup"
4
+
5
+ CoralBackup::CLI.start(ARGV)
@@ -0,0 +1,4 @@
1
+ require "coral_backup/cli"
2
+ require "coral_backup/file_selector"
3
+ require "coral_backup/settings"
4
+ require "coral_backup/version"
@@ -0,0 +1,146 @@
1
+ require "coral_backup/file_selector"
2
+ require "coral_backup/settings"
3
+ require "thor"
4
+
5
+ module CoralBackup
6
+ class CLI < Thor
7
+ def initialize(*args)
8
+ super
9
+ @settings = Settings.new
10
+ end
11
+
12
+ desc "add <ACTION>", "Add a new backup action"
13
+ def add(action_name)
14
+ if @settings.exist_action?(action_name)
15
+ warn "ERROR: Backup action `#{action_name}' already exists."
16
+ exit 1
17
+ end
18
+
19
+ puts "Drag and drop or input source directory."
20
+ source = FileSelector.single_select
21
+ unless FileTest.directory?(source)
22
+ warn "ERROR: Not a directory: #{source}"
23
+ exit 1
24
+ end
25
+ source << "/" unless source.end_with?("/")
26
+
27
+ puts "Drag and drop or input destination directory."
28
+ destination = FileSelector.single_select
29
+ unless FileTest.directory?(destination)
30
+ warn "ERROR: Not a directory: #{destination}"
31
+ exit 1
32
+ end
33
+ destination << "/" unless destination.end_with?("/")
34
+
35
+ puts "Drag and drop or input exclusion files/directories."
36
+ puts "Press Ctrl + D to finish."
37
+ exclusions = FileSelector.select
38
+ exclusions.map! {|filename|
39
+ if FileTest.directory?(filename)
40
+ filename << "/" unless filename.end_with?("/")
41
+ end
42
+ filename
43
+ }
44
+ exclusions.uniq!
45
+
46
+ @settings.add(action_name, source, destination, exclusions)
47
+ end
48
+
49
+ desc "delete <ACTION>", "Delete the backup action"
50
+ def delete(action_name)
51
+ @settings.delete(action_name)
52
+ rescue ArgumentError => e
53
+ warn "ERROR: #{e}"
54
+ exit 1
55
+ end
56
+
57
+ desc "list", "Show all backup actions"
58
+ def list
59
+ puts @settings.action_names
60
+ end
61
+
62
+ map run: :__run
63
+ desc "run <ACTION>", "Run the backup action"
64
+ option :"dry-run", :type => :boolean, aliases: :d, desc: "Show what would have been backed up, but do not back them up"
65
+ def __run(action_name)
66
+ unless rsync_version.split(".").first.to_i >= 3
67
+ warn "ERROR: rsync version must be larger than 3.X.X"
68
+ exit 1
69
+ end
70
+
71
+ time = Time.now
72
+ data = @settings.action_data(action_name)
73
+
74
+ destination = data[:destination]
75
+ dryrun = options[:"dry-run"]
76
+ exclusions = data[:exclusions]
77
+ source = data[:source]
78
+
79
+ args = ["rsync", "-rlptgoxS", "--delete", "-X", "--progress", "--stats"]
80
+ args << "--dry-run" if dryrun
81
+
82
+ new_destination = File.expand_path("#{action_name} backup #{Time.now.strftime("%F-%H%M%S")}", destination)
83
+ old_destination =
84
+ Dir.chdir(destination) {
85
+ Dir["*"]
86
+ }.select{|dirname|
87
+ dirname.match(/#{Regexp.escape(action_name)} backup \d{4}-\d{2}-\d{2}-\d{6}/)
88
+ }.sort.last
89
+
90
+ if old_destination
91
+ args << "--link-dest"
92
+ args << Pathname.new(File.expand_path(old_destination, destination)).relative_path_from(Pathname.new(new_destination)).to_s
93
+ end
94
+
95
+ exclusions.each do |exclusion|
96
+ args << "--exclude"
97
+ args << Pathname.new(exclusion).relative_path_from(Pathname.new(source)).to_s
98
+ end
99
+
100
+ args << source
101
+ args << new_destination
102
+
103
+ system(args.flatten.shelljoin)
104
+
105
+ @settings.update_time(action_name, time) unless dryrun
106
+ end
107
+
108
+
109
+ desc "show <ACTION>", "Show information about the backup action"
110
+ def show(action_name)
111
+ data = @settings.action_data(action_name)
112
+
113
+ puts "Source: #{data[:source]}"
114
+ puts "Destination: #{data[:destination]}"
115
+ print "exclusions: "
116
+ if data[:exclusions].empty?
117
+ puts "(No exclusions)"
118
+ else
119
+ puts "#{data[:exclusions].size} exclusion(s)"
120
+ puts data[:exclusions]
121
+ end
122
+
123
+ print "Last backup run at: "
124
+ if data[:last_run_time].nil?
125
+ puts "No backup yet"
126
+ else
127
+ puts data[:last_run_time]
128
+ end
129
+ rescue ArgumentError => e
130
+ warn "ERROR: #{e}"
131
+ exit 1
132
+ end
133
+
134
+ desc "version", "Print the version"
135
+ def version
136
+ puts VERSION
137
+ end
138
+
139
+ no_tasks do
140
+ def rsync_version
141
+ `rsync --version`.match(/^\s*rsync\s*version\s*(\d+\.\d+\.\d+)/)
142
+ $1
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,68 @@
1
+ require "shellwords"
2
+ require "readline"
3
+
4
+ module CoralBackup
5
+ class FileSelector
6
+ attr_reader :files
7
+
8
+ def initialize
9
+ @files = []
10
+ end
11
+
12
+ def add_file(filename)
13
+ if FileTest.exist?(filename)
14
+ @files << filename
15
+ else
16
+ raise Errno::ENOENT, filename
17
+ end
18
+ end
19
+
20
+ def self.select
21
+ file_selector = new
22
+ Readline.completion_proc = Readline::FILENAME_COMPLETION_PROC
23
+ while buf = Readline.readline("> ")
24
+ expanded = Shellwords.split(buf)
25
+ warn "WARNING: #{expanded.length} files are being added:" unless expanded.length == 1
26
+
27
+ expanded.each do |ex|
28
+ begin
29
+ file_selector.add_file(ex)
30
+ rescue Errno::ENOENT => e
31
+ warn e
32
+ else
33
+ warn ex
34
+ end
35
+ end
36
+ end
37
+
38
+ file_selector.files.uniq
39
+ end
40
+
41
+
42
+ def self.single_select
43
+ file_selector = new
44
+ Readline.completion_proc = Readline::FILENAME_COMPLETION_PROC
45
+ file_added = false
46
+ while !file_added && buf = Readline.readline("> ")
47
+ expanded = Shellwords.split(buf)
48
+ warn "WARNING: #{expanded.length} files are being added:" unless expanded.length == 1
49
+ begin
50
+ file_selector.add_file(expanded[0])
51
+ rescue Errno::ENOENT => e
52
+ warn e
53
+ else
54
+ file_added = true
55
+ end
56
+ end
57
+
58
+ unless file_selector.files.length == 1
59
+ warn "ERROR: Wrong number of directories (#{file_selector.files.length} for 1):"
60
+ file_selector.files.each do |e|
61
+ warn e
62
+ end
63
+ exit 1
64
+ end
65
+ file_selector.files[0]
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,58 @@
1
+ require "toml"
2
+
3
+ module CoralBackup
4
+ class Settings
5
+ INITIAL_SETTINGS = {
6
+ actions: {}
7
+ }
8
+
9
+ def initialize(filename = "#{Dir.home}/.coral")
10
+ @filename = filename
11
+ @settings = file_load
12
+ end
13
+
14
+ def action_data(action_name)
15
+ raise ArgumentError, "Backup action `#{action_name}' does not exist." unless exist_action?(action_name)
16
+ @settings[:actions][action_name.to_sym]
17
+ end
18
+
19
+ def action_names
20
+ @settings[:actions].keys.map(&:to_s)
21
+ end
22
+
23
+ def exist_action?(action_name)
24
+ action_names.include?(action_name)
25
+ end
26
+
27
+ def add(action_name, source, destination, exclusions)
28
+ raise ArgumentError, "Backup action `#{action_name}' already exists." if exist_action?(action_name)
29
+ @settings[:actions][action_name.to_sym] = { source: source, destination: destination, exclusions: exclusions }
30
+ file_dump
31
+ end
32
+
33
+ def delete(action_name)
34
+ raise ArgumentError, "Backup action `#{action_name}' does not exist." unless exist_action?(action_name)
35
+ @settings[:actions].delete(action_name.to_sym)
36
+ file_dump
37
+ end
38
+
39
+ def update_time(action_name, time)
40
+ raise ArgumentError, "Backup action `#{action_name}' does not exist." unless exist_action?(action_name)
41
+ @settings[:actions][action_name.to_sym][:last_run_time] = time.to_s
42
+ file_dump
43
+ end
44
+
45
+ private
46
+ def file_load
47
+ return INITIAL_SETTINGS unless FileTest.exist?(@filename)
48
+
49
+ TOML.load_file(@filename, symbolize_keys: true)
50
+ end
51
+
52
+ def file_dump
53
+ open(@filename, "w") do |f|
54
+ f.puts TOML.dump(@settings)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module CoralBackup
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coral_backup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - OTSUKA Tatsuya
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-06-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.19'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.19'
27
+ - !ruby/object:Gem::Dependency
28
+ name: toml-rb
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - tatzyr@gmail.com
86
+ executables:
87
+ - coral
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - coral_backup.gemspec
100
+ - exe/coral
101
+ - lib/coral_backup.rb
102
+ - lib/coral_backup/cli.rb
103
+ - lib/coral_backup/file_selector.rb
104
+ - lib/coral_backup/settings.rb
105
+ - lib/coral_backup/version.rb
106
+ homepage: https://github.com/tatzyr/coral_backup
107
+ licenses:
108
+ - Zlib
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '2.0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.4.8
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Coral Backup creates incremental backups of files that can be restored at
130
+ a later date.
131
+ test_files: []