bak-shell 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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +53 -0
- data/Rakefile +9 -0
- data/bak-shell.gemspec +23 -0
- data/bin/bak +12 -0
- data/lib/bak-shell/cli.rb +20 -0
- data/lib/bak-shell/commands/backup.rb +54 -0
- data/lib/bak-shell/commands/info.rb +101 -0
- data/lib/bak-shell/commands/purge.rb +60 -0
- data/lib/bak-shell/commands/restore.rb +48 -0
- data/lib/bak-shell/exceptions.rb +8 -0
- data/lib/bak-shell/indexer.rb +115 -0
- data/lib/bak-shell/version.rb +3 -0
- data/lib/bak-shell.rb +21 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 82a8960ce4c29af20821e579fd7b8c1f5acb3ea4
|
4
|
+
data.tar.gz: 60407ebba0cde615e224fa2949c1a10850052734
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5f3a4b5759f252dbc457f5ccfd8be6704e618d309d241ee88438951c1fc6017752f100e91aea026aac37f3c1dd4cc0875be019999db82ae54563db28371fa01a
|
7
|
+
data.tar.gz: 0d444113b60224ad3b8e044f79c7da429fe1e80ed981235937588f3de5a989e659ed6bb8286ae6552e55d1151ac30743149bd24a006ff2267fc259f81ff2cf90
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Robert Audi
|
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,53 @@
|
|
1
|
+
bak-shell
|
2
|
+
=========
|
3
|
+
|
4
|
+
A backup utility for the shell.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
```
|
10
|
+
> gem install bak-shell
|
11
|
+
```
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
```
|
17
|
+
> bak help
|
18
|
+
```
|
19
|
+
|
20
|
+
Contributing
|
21
|
+
------------
|
22
|
+
|
23
|
+
1. Fork it ( https://github.com/RobertAudi/bak-shell/fork )
|
24
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
25
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
26
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
27
|
+
5. Create a new Pull Request
|
28
|
+
|
29
|
+
License
|
30
|
+
-------
|
31
|
+
|
32
|
+
Copyright (c) 2014 Robert Audi
|
33
|
+
|
34
|
+
MIT License
|
35
|
+
|
36
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
37
|
+
a copy of this software and associated documentation files (the
|
38
|
+
"Software"), to deal in the Software without restriction, including
|
39
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
40
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
41
|
+
permit persons to whom the Software is furnished to do so, subject to
|
42
|
+
the following conditions:
|
43
|
+
|
44
|
+
The above copyright notice and this permission notice shall be
|
45
|
+
included in all copies or substantial portions of the Software.
|
46
|
+
|
47
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
48
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
49
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
50
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
51
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
52
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
53
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/bak-shell.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Ensure we require the local version and not one we might have installed already
|
2
|
+
require File.join([File.dirname(__FILE__),"lib","bak-shell","version.rb"])
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "bak-shell"
|
5
|
+
s.version = BakShell::VERSION
|
6
|
+
s.author = "Robert Audi"
|
7
|
+
s.email = "robert@audii.me"
|
8
|
+
s.homepage = "https://github.com/RobertAudi/bak-shell"
|
9
|
+
s.summary = "Backup utility for the shell"
|
10
|
+
s.description = "Backup utility that lets you back up files or directories, and manage those backups from the command line"
|
11
|
+
s.license = "MIT"
|
12
|
+
s.files = `git ls-files -z`.split("\x0")
|
13
|
+
|
14
|
+
s.bindir = "bin"
|
15
|
+
s.require_paths << "lib"
|
16
|
+
s.executables << "bak"
|
17
|
+
|
18
|
+
s.add_development_dependency "rake", "10.3.2"
|
19
|
+
s.add_development_dependency "bundler", "~> 1.6"
|
20
|
+
|
21
|
+
s.add_runtime_dependency "gli", "2.11.0"
|
22
|
+
s.add_runtime_dependency "rainbow", "2.0.0"
|
23
|
+
end
|
data/bin/bak
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module BakShell
|
2
|
+
class CLI
|
3
|
+
extend GLI::App
|
4
|
+
|
5
|
+
program_desc "Backup utility for the shell"
|
6
|
+
version BakShell::VERSION
|
7
|
+
|
8
|
+
commands_from File.expand_path(File.join(File.dirname(File.realpath(__FILE__)), "commands"))
|
9
|
+
|
10
|
+
on_error do |exception|
|
11
|
+
case exception
|
12
|
+
when BakShell::BaseError
|
13
|
+
$stderr.puts exception.message.color(:red)
|
14
|
+
false
|
15
|
+
else
|
16
|
+
true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module BakShell
|
2
|
+
class CLI
|
3
|
+
desc "Backup a file or directory"
|
4
|
+
arg_name "TARGET"
|
5
|
+
command :backup do |c|
|
6
|
+
c.desc "Replace latest version of backup (if present). Default: false"
|
7
|
+
c.switch :R, :replace, default_value: false, negatable: false
|
8
|
+
|
9
|
+
c.action do |global_options, options, args|
|
10
|
+
raise TargetMissingError.new("Target missing") if args.empty?
|
11
|
+
raise TooManyTargetsError.new("Only one target can be specified") if args.count > 1
|
12
|
+
|
13
|
+
target = File.expand_path(args.first)
|
14
|
+
raise InvalidTargetError.new("No such file or directory: #{target}") unless File.exists?(target)
|
15
|
+
|
16
|
+
puts "Backing up...".color(:green)
|
17
|
+
|
18
|
+
indexer = Indexer.instance
|
19
|
+
backup = indexer.backup_with_target(target) || indexer.add(target)
|
20
|
+
backup_dir = File.join(BakShell::BACKUP_DIR, backup.id)
|
21
|
+
|
22
|
+
unless backup.persistent
|
23
|
+
FileUtils.mkdir_p(backup_dir)
|
24
|
+
|
25
|
+
puts "Created new backup directory".color(:green)
|
26
|
+
end
|
27
|
+
|
28
|
+
base_target = File.basename(target)
|
29
|
+
destination = File.join(BakShell::BACKUP_DIR, backup.id, "#{base_target}.bak-latest-#{Time.now.to_f}")
|
30
|
+
previous_latest = Dir.glob(File.join(backup_dir, "*.bak-latest-*"))
|
31
|
+
|
32
|
+
if previous_latest.count == 1
|
33
|
+
previous_latest = previous_latest.first
|
34
|
+
|
35
|
+
if options[:replace]
|
36
|
+
FileUtils.rm_r(previous_latest, force: true)
|
37
|
+
|
38
|
+
puts "Removed old backup".color(:green)
|
39
|
+
else
|
40
|
+
FileUtils.mv(previous_latest, previous_latest.sub(/-latest/, ""))
|
41
|
+
|
42
|
+
puts "Versionned old backup".color(:green)
|
43
|
+
end
|
44
|
+
elsif previous_latest.count > 1
|
45
|
+
raise "Found more than one backup marked as latest"
|
46
|
+
end
|
47
|
+
|
48
|
+
FileUtils.cp_r(target, destination)
|
49
|
+
|
50
|
+
puts "Backup complete!".color(:green)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module BakShell
|
2
|
+
class CLI
|
3
|
+
desc "Show backup info"
|
4
|
+
arg_name "TARGET"
|
5
|
+
command :info do |c|
|
6
|
+
c.desc "Show all backups for a target (Doesn't work if no target is specified!!)"
|
7
|
+
c.switch :a, :all, default_value: false, negatable: false
|
8
|
+
|
9
|
+
c.desc "Show the path to the backup of a target (takes the index of the backup as argument)"
|
10
|
+
c.arg_name "index"
|
11
|
+
c.flag :p, :path, must_match: /\A[1-9][0-9]*\Z/
|
12
|
+
|
13
|
+
c.desc "Show the path to the latest backup of a target (equivalent to `-p 1`)"
|
14
|
+
c.switch :P, :"latest-path", default_value: false, negatable: false
|
15
|
+
|
16
|
+
c.action do |global_options, options, args|
|
17
|
+
raise TooManyTargetsError.new("Only one target can be specified") if args.count > 1
|
18
|
+
|
19
|
+
indexer = Indexer.instance
|
20
|
+
|
21
|
+
if args.empty?
|
22
|
+
illegal_options = {
|
23
|
+
a: false,
|
24
|
+
p: nil,
|
25
|
+
P: false
|
26
|
+
}
|
27
|
+
|
28
|
+
command_switches = c.switches.map { |k, s| { s.name => [s.name, s.aliases].flatten } }.inject({}) { |s, cs| s.merge(cs) }
|
29
|
+
command_flags = c.flags.map { |k, s| { s.name => [s.name, s.aliases].flatten } }.inject({}) { |s, cs| s.merge(cs) }
|
30
|
+
command_options = command_switches.merge(command_flags)
|
31
|
+
|
32
|
+
illegal_error_message = ""
|
33
|
+
options.each do |o, v|
|
34
|
+
if illegal_options.has_key?(o) && illegal_options[o] != v
|
35
|
+
illegal_error_message << "The #{command_options[o].map { |k| "`#{k.length > 1 ? "--" : "-"}#{k}`" }.join("/")} option cannot be used if no target is specified\n".color(:red)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
puts "#{illegal_error_message}\n" unless illegal_error_message.empty?
|
40
|
+
|
41
|
+
backups = Hash.new
|
42
|
+
indexer.backups.each { |b| backups[b[:id]] = Dir.glob(File.join(BakShell::BACKUP_DIR, b[:id], "*")).reverse }
|
43
|
+
|
44
|
+
puts "#{backups.values.inject(0) { |count, b| count + b.count }} backups found".color(:green)
|
45
|
+
|
46
|
+
backups.each do |id, b|
|
47
|
+
puts indexer.backup_with_id(id).target
|
48
|
+
puts "\t#{b.count} backups"
|
49
|
+
|
50
|
+
latest = Time.at(b.find { |v| v =~ /\.bak-latest-.*\Z/ }.sub(/\A.*\.bak-latest-/, "").to_f)
|
51
|
+
puts "\tLatest backup time: #{latest}"
|
52
|
+
end
|
53
|
+
else
|
54
|
+
if options[:P] && !options[:p].nil?
|
55
|
+
raise BakShell::InvalidOptionError.new("Conflicting options: `-p`/`--path` and `-P`/`--latest-path`")
|
56
|
+
end
|
57
|
+
|
58
|
+
target = File.expand_path(args.first)
|
59
|
+
backup = indexer.backup_with_target(target)
|
60
|
+
|
61
|
+
raise InvalidBackupError.new("No backup found for file or directory: #{target}") if backup.nil?
|
62
|
+
|
63
|
+
backup_dir = File.join(BakShell::BACKUP_DIR, backup.id)
|
64
|
+
|
65
|
+
if options[:P]
|
66
|
+
puts Dir.glob(File.join(backup_dir, "*.bak-latest-*")).first
|
67
|
+
else
|
68
|
+
backups = Dir.glob(File.join(backup_dir, "*")).reverse
|
69
|
+
backup_count = backups.count
|
70
|
+
|
71
|
+
if !options[:p].nil?
|
72
|
+
if options[:p].to_i > backup_count
|
73
|
+
raise BakShell::InvalidBackupError.new("Invalid backup index: #{options[:p]}")
|
74
|
+
end
|
75
|
+
|
76
|
+
puts backups[options[:p].to_i - 1]
|
77
|
+
else
|
78
|
+
backup_times = backups.map do |b|
|
79
|
+
Time.at(File.basename(b).sub(/\A.*\.bak-(latest-)?/, "").to_f).to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
puts "#{backup_count} found"
|
83
|
+
|
84
|
+
if !options[:a] && backup_count > 5
|
85
|
+
last_five = backup_times[0..4]
|
86
|
+
remaining_backups = backup_times.drop(5).count
|
87
|
+
|
88
|
+
puts "Showing five latest backup times:".color(:green)
|
89
|
+
last_five.each_with_index { |b, i| puts "[#{i + 1}] #{b}" }
|
90
|
+
puts "#{remaining_backups} more..."
|
91
|
+
else
|
92
|
+
puts "Showing all backup times:".color(:green)
|
93
|
+
backup_times.each_with_index { |b, i| puts "[#{i + 1}] #{b}" }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module BakShell
|
2
|
+
class CLI
|
3
|
+
desc "Remove (specific) backups"
|
4
|
+
arg_name "TARGET"
|
5
|
+
command :purge do |c|
|
6
|
+
c.desc "Number of backups to keep"
|
7
|
+
c.arg_name "count"
|
8
|
+
c.flag :k, :keep, default_value: 0, must_match: /\A[1-9][0-9]*\Z/
|
9
|
+
|
10
|
+
c.action do |global_options, options, args|
|
11
|
+
puts "Purging...".color(:green)
|
12
|
+
|
13
|
+
indexer = Indexer.instance
|
14
|
+
targets = Array.new
|
15
|
+
ids = Array.new
|
16
|
+
|
17
|
+
if args.empty?
|
18
|
+
raise InvalidBackupError.new("Nothing to purge") if indexer.backups.count == 0
|
19
|
+
|
20
|
+
targets = Dir.glob(File.join(BakShell::BACKUP_DIR, "*"))
|
21
|
+
targets.delete(indexer.index_file)
|
22
|
+
|
23
|
+
|
24
|
+
ids = indexer.ids
|
25
|
+
else
|
26
|
+
args.each do |arg|
|
27
|
+
target = File.expand_path(arg)
|
28
|
+
raise InvalidTargetError.new("No such file or directory: #{target}") unless File.exists?(target)
|
29
|
+
|
30
|
+
backup = indexer.backup_with_target(target)
|
31
|
+
|
32
|
+
if backup.nil?
|
33
|
+
puts"No backup found for file or directory: #{target}".color(:yellow)
|
34
|
+
next
|
35
|
+
end
|
36
|
+
|
37
|
+
targets << File.join(BakShell::BACKUP_DIR, backup.id)
|
38
|
+
ids << backup.id
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
keep_count = options[:k].to_i
|
43
|
+
|
44
|
+
if keep_count > 0
|
45
|
+
targets = targets.map { |t| Dir.glob(File.join(t, "*")).reverse.drop(keep_count) }.flatten
|
46
|
+
backup_count = targets.count
|
47
|
+
else
|
48
|
+
backup_count = targets.map { |t| Dir.glob(File.join(t, "*")).count }.inject(:+)
|
49
|
+
indexer.remove(ids)
|
50
|
+
end
|
51
|
+
|
52
|
+
if backup_count > 0
|
53
|
+
targets.each { |t| FileUtils.rm_r(t, force: true) }
|
54
|
+
end
|
55
|
+
|
56
|
+
puts "Purging complete! (#{backup_count} backups removed)".color(:green)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module BakShell
|
2
|
+
class CLI
|
3
|
+
desc "Restore a file or directory"
|
4
|
+
arg_name "TARGET"
|
5
|
+
command :restore do |c|
|
6
|
+
c.desc "Index of backup to restore"
|
7
|
+
c.arg_name "index"
|
8
|
+
c.flag :i, :index, default_value: 1, must_match: /\A[1-9][0-9]*\Z/
|
9
|
+
|
10
|
+
c.action do |global_options, options, args|
|
11
|
+
raise TargetMissingError.new("Target missing") if args.empty?
|
12
|
+
raise TooManyTargetsError.new("Only one target can be specified") if args.count > 1
|
13
|
+
|
14
|
+
target = File.expand_path(args.first)
|
15
|
+
|
16
|
+
raise InvalidTargetError.new("No such file or directory: #{target}") unless File.exists?(target)
|
17
|
+
|
18
|
+
puts "Restoring backup...".color(:green)
|
19
|
+
|
20
|
+
indexer = Indexer.instance
|
21
|
+
backup = indexer.backup_with_target(target)
|
22
|
+
|
23
|
+
raise InvalidBackupError.new("No backup found for file or directory: #{target}") if backup.nil?
|
24
|
+
|
25
|
+
backup_dir = File.join(BakShell::BACKUP_DIR, backup.id)
|
26
|
+
index = options[:i].to_i
|
27
|
+
|
28
|
+
if index == 1
|
29
|
+
backup_to_restore = Dir.glob(File.join(backup_dir, "*.bak-latest-*")).first
|
30
|
+
else
|
31
|
+
backups = Dir.glob(File.join(backup_dir, "*")).reverse
|
32
|
+
|
33
|
+
raise BakShell::InvalidBackupError.new("Invalid backup index: #{index}") if index > backups.count
|
34
|
+
|
35
|
+
backup_to_restore = backups[index - 1]
|
36
|
+
end
|
37
|
+
|
38
|
+
temp_bak = "#{target}.bak-#{Time.now.to_f}"
|
39
|
+
|
40
|
+
FileUtils.mv(target, temp_bak)
|
41
|
+
FileUtils.cp_r(backup_to_restore, target)
|
42
|
+
FileUtils.rm_r(temp_bak, force: true)
|
43
|
+
|
44
|
+
puts "Backup restored!".color(:green)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module BakShell
|
2
|
+
class BaseError < ArgumentError; end
|
3
|
+
class TargetMissingError < BaseError; end
|
4
|
+
class TooManyTargetsError < BaseError; end
|
5
|
+
class InvalidTargetError < BaseError; end
|
6
|
+
class InvalidBackupError < BaseError; end
|
7
|
+
class InvalidOptionError < BaseError; end
|
8
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require "singleton"
|
2
|
+
require "ostruct"
|
3
|
+
require "csv"
|
4
|
+
require "securerandom"
|
5
|
+
|
6
|
+
module BakShell
|
7
|
+
class Indexer
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def backup_with_id(id)
|
11
|
+
return @backup if !@backup.nil? && id == @backup.id
|
12
|
+
|
13
|
+
backup = self.find_by_id(id)
|
14
|
+
|
15
|
+
if backup.nil?
|
16
|
+
@backup = nil
|
17
|
+
else
|
18
|
+
@backup = OpenStruct.new(backup)
|
19
|
+
end
|
20
|
+
|
21
|
+
@backup
|
22
|
+
end
|
23
|
+
|
24
|
+
def backup_with_target(target)
|
25
|
+
return @backup if !@backup.nil? && target == @backup[:target]
|
26
|
+
|
27
|
+
backup = self.find_by_target(target)
|
28
|
+
|
29
|
+
if backup.nil?
|
30
|
+
@backup = nil
|
31
|
+
else
|
32
|
+
@backup = OpenStruct.new(backup)
|
33
|
+
end
|
34
|
+
|
35
|
+
@backup
|
36
|
+
end
|
37
|
+
|
38
|
+
def add(target)
|
39
|
+
if self.targets.include?(target)
|
40
|
+
raise "Attempt to add an existing backup to the index"
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
id = SecureRandom.hex
|
45
|
+
end while self.ids.include?(id)
|
46
|
+
|
47
|
+
CSV.open(self.index_file, "ab") { |f| f << [id, target] }
|
48
|
+
|
49
|
+
self.ids << id
|
50
|
+
self.targets << target
|
51
|
+
self.backups << { id: id, target: target, persistent: false }
|
52
|
+
|
53
|
+
@backup = OpenStruct.new(self.backups.last)
|
54
|
+
end
|
55
|
+
|
56
|
+
def remove(ids)
|
57
|
+
new_index = self.backups.select { |b| !ids.include?(b[:id]) }
|
58
|
+
new_index.map { |b| b.delete(:persistent) }
|
59
|
+
CSV.open(self.index_file, "wb") do |f|
|
60
|
+
new_index.each { |b| f << b.values }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def index_file
|
65
|
+
if @index_file.nil?
|
66
|
+
@index_file = File.join(BakShell::BACKUP_DIR, "baklist.index")
|
67
|
+
FileUtils.touch(@index_file)
|
68
|
+
end
|
69
|
+
|
70
|
+
@index_file
|
71
|
+
end
|
72
|
+
|
73
|
+
def index_file_exists?
|
74
|
+
File.file?(self.index_file)
|
75
|
+
end
|
76
|
+
|
77
|
+
def ids
|
78
|
+
self.load! if @ids.nil?
|
79
|
+
|
80
|
+
@ids
|
81
|
+
end
|
82
|
+
|
83
|
+
def targets
|
84
|
+
self.load! if @targets.nil?
|
85
|
+
|
86
|
+
@targets
|
87
|
+
end
|
88
|
+
|
89
|
+
def backups
|
90
|
+
self.load! if @backups.nil?
|
91
|
+
|
92
|
+
@backups
|
93
|
+
end
|
94
|
+
|
95
|
+
def load!
|
96
|
+
@ids = Array.new
|
97
|
+
@targets = Array.new
|
98
|
+
@backups = Array.new
|
99
|
+
CSV.foreach(self.index_file) do |row|
|
100
|
+
@ids << row.first
|
101
|
+
@targets << row.last
|
102
|
+
@backups << { id: row.first, target: row.last, persistent: true }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
alias_method :reload!, :load!
|
106
|
+
|
107
|
+
def find_by_id(id)
|
108
|
+
self.backups.find { |b| id == b[:id] }
|
109
|
+
end
|
110
|
+
|
111
|
+
def find_by_target(target)
|
112
|
+
self.backups.find { |b| target == b[:target] }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/bak-shell.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Dev shit
|
2
|
+
if ENV["BAK_SHELL_DEV_SHIT"] == "devshit"
|
3
|
+
require "awesome_print" rescue nil
|
4
|
+
end
|
5
|
+
|
6
|
+
# Standard lib shit
|
7
|
+
require "fileutils"
|
8
|
+
|
9
|
+
# Require gems shit
|
10
|
+
require "gli"
|
11
|
+
require "rainbow/ext/string"
|
12
|
+
|
13
|
+
module BakShell
|
14
|
+
# BACKUP_DIR = File.expand_path(File.join(ENV["HOME"], "bak"))
|
15
|
+
BACKUP_DIR = File.expand_path(File.join(ENV["HOME"], "tmp", "bak"))
|
16
|
+
end
|
17
|
+
|
18
|
+
require_relative "./bak-shell/version"
|
19
|
+
require_relative "./bak-shell/exceptions"
|
20
|
+
require_relative "./bak-shell/indexer"
|
21
|
+
require_relative "./bak-shell/cli"
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bak-shell
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Audi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 10.3.2
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 10.3.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: gli
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.11.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.11.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rainbow
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.0.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.0.0
|
69
|
+
description: Backup utility that lets you back up files or directories, and manage
|
70
|
+
those backups from the command line
|
71
|
+
email: robert@audii.me
|
72
|
+
executables:
|
73
|
+
- bak
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- bak-shell.gemspec
|
83
|
+
- bin/bak
|
84
|
+
- lib/bak-shell.rb
|
85
|
+
- lib/bak-shell/cli.rb
|
86
|
+
- lib/bak-shell/commands/backup.rb
|
87
|
+
- lib/bak-shell/commands/info.rb
|
88
|
+
- lib/bak-shell/commands/purge.rb
|
89
|
+
- lib/bak-shell/commands/restore.rb
|
90
|
+
- lib/bak-shell/exceptions.rb
|
91
|
+
- lib/bak-shell/indexer.rb
|
92
|
+
- lib/bak-shell/version.rb
|
93
|
+
homepage: https://github.com/RobertAudi/bak-shell
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.2.2
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Backup utility for the shell
|
118
|
+
test_files: []
|