mclone 0.2.1 → 0.3.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 +4 -4
- data/CHANGES.md +16 -8
- data/README.md +409 -409
- data/bin/mclone +1 -157
- data/lib/mclone/cli.rb +157 -0
- data/lib/mclone.rb +752 -745
- metadata +4 -3
data/bin/mclone
CHANGED
|
@@ -1,159 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
require 'clamp'
|
|
6
|
-
require 'mclone'
|
|
7
|
-
|
|
8
|
-
include Mclone
|
|
9
|
-
|
|
10
|
-
begin
|
|
11
|
-
|
|
12
|
-
Clamp do
|
|
13
|
-
|
|
14
|
-
using Refinements
|
|
15
|
-
|
|
16
|
-
self.default_subcommand = 'info'
|
|
17
|
-
|
|
18
|
-
option ['-f', '--force'], :flag, 'Insist on potentially dangerous actions', default: false
|
|
19
|
-
option ['-n', '--dry-run'], :flag, 'Simulation mode with no on-disk modifications', default: false
|
|
20
|
-
option ['-v', '--verbose'], :flag, 'Verbose operation', default: false
|
|
21
|
-
|
|
22
|
-
option ['-V', '--version'], :flag, 'Show version' do
|
|
23
|
-
puts VERSION
|
|
24
|
-
exit(true)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def session
|
|
28
|
-
session = Mclone::Session.new
|
|
29
|
-
session.force = force?
|
|
30
|
-
session.simulate = dry_run?
|
|
31
|
-
session.verbose = verbose?
|
|
32
|
-
session.restore_volumes!
|
|
33
|
-
session
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def resolve_mode(mode)
|
|
37
|
-
case (m = Task::MODES.resolve(mode)).size
|
|
38
|
-
when 0 then raise(Task::Error, %(no modes matching pattern "#{mode}"))
|
|
39
|
-
when 1 then m.first
|
|
40
|
-
else raise(Task::Error, %(ambiguous mode pattern "#{mode}"))
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
subcommand 'info', 'Output information on volumes & tasks' do
|
|
45
|
-
def execute
|
|
46
|
-
s = session
|
|
47
|
-
$stdout.puts "# Mclone version #{Mclone::VERSION}"
|
|
48
|
-
$stdout.puts
|
|
49
|
-
$stdout.puts '## Volumes'
|
|
50
|
-
$stdout.puts
|
|
51
|
-
s.volumes.each { |volume| $stdout.puts "* [#{volume.id}] :: (#{volume.root})" }
|
|
52
|
-
stales = []
|
|
53
|
-
intacts = []
|
|
54
|
-
intact_tasks = s.intact_tasks
|
|
55
|
-
s.tasks.each do |task|
|
|
56
|
-
ts = (t = intact_tasks[task]).nil? ? "<#{task.id}>" : "[#{task.id}]"
|
|
57
|
-
svs = s.volumes.volume(task.source_id).nil? ? "<#{task.source_id}>" : "[#{task.source_id}]"
|
|
58
|
-
dvs = s.volumes.volume(task.destination_id).nil? ? "<#{task.destination_id}>" : "[#{task.destination_id}]"
|
|
59
|
-
crypter_mode = task.crypter_mode.nil? ? nil : "#{task.crypter_mode}+"
|
|
60
|
-
xs = ["* #{ts} :: #{crypter_mode}#{task.mode} #{svs}(#{task.source_root}) -> #{dvs}(#{task.destination_root})"]
|
|
61
|
-
xs << "include #{task.include}" unless task.include.nil? || task.include.empty?
|
|
62
|
-
xs << "exclude #{task.exclude}" unless task.exclude.nil? || task.exclude.empty?
|
|
63
|
-
(t.nil? ? stales : intacts) << xs.join(' :: ')
|
|
64
|
-
end
|
|
65
|
-
unless intacts.empty?
|
|
66
|
-
$stdout.puts
|
|
67
|
-
$stdout.puts '## Intact tasks'
|
|
68
|
-
$stdout.puts
|
|
69
|
-
intacts.each { |x| $stdout.puts x }
|
|
70
|
-
end
|
|
71
|
-
unless stales.empty?
|
|
72
|
-
$stdout.puts
|
|
73
|
-
$stdout.puts '## Stale tasks'
|
|
74
|
-
$stdout.puts
|
|
75
|
-
stales.each { |x| $stdout.puts x }
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
subcommand 'volume', 'Volume operations' do
|
|
81
|
-
|
|
82
|
-
subcommand ['new', 'create'], 'Create new volume' do
|
|
83
|
-
parameter 'DIRECTORY', 'Directory to become a Mclone volume'
|
|
84
|
-
def execute
|
|
85
|
-
session.format_volume!(directory)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
subcommand 'delete', 'Delete existing volume' do
|
|
90
|
-
parameter 'VOLUME', 'Volume ID pattern'
|
|
91
|
-
def execute
|
|
92
|
-
session.delete_volume!(volume).commit!
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
subcommand 'task', 'Task operations' do
|
|
99
|
-
|
|
100
|
-
def self.set_task_opts
|
|
101
|
-
modes = Task::MODES.collect(&:to_s).join(' | ')
|
|
102
|
-
option ['-m', '--mode'], 'MODE', "Operation mode (#{modes})", default: Task::MODES.first.to_s
|
|
103
|
-
option ['-i', '--include'], 'PATTERN', 'Include paths pattern'
|
|
104
|
-
option ['-x', '--exclude'], 'PATTERN', 'Exclude paths pattern'
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
subcommand ['new', 'create'], 'Create new SOURCE -> DESTINATION task' do
|
|
108
|
-
set_task_opts
|
|
109
|
-
option ['-d', '--decrypt'], :flag, 'Decrypt source'
|
|
110
|
-
option ['-e', '--encrypt'], :flag, 'Encrypt destination'
|
|
111
|
-
option ['-p', '--password'], 'PASSWORD', 'Plain text password'
|
|
112
|
-
option ['-t', '--token'], 'TOKEN', 'Rclone crypt token (obscured password)'
|
|
113
|
-
parameter 'SOURCE', 'Source path'
|
|
114
|
-
parameter 'DESTINATION', 'Destination path'
|
|
115
|
-
def execute
|
|
116
|
-
crypter_mode = nil
|
|
117
|
-
signal_usage_error 'choose either encryption or decryption mode, not both' if decrypt? && encrypt?
|
|
118
|
-
signal_usage_error 'specify either plain text password or Rclone crypt token, not both' if !password.nil? && !token.nil?
|
|
119
|
-
crypter_mode = :encrypt if encrypt?
|
|
120
|
-
crypter_mode = :decrypt if decrypt?
|
|
121
|
-
session.create_task!(
|
|
122
|
-
resolve_mode(mode),
|
|
123
|
-
source,
|
|
124
|
-
destination,
|
|
125
|
-
include: include, exclude: exclude, crypter_mode: crypter_mode, crypter_password: password, crypter_token: token
|
|
126
|
-
).commit!
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
subcommand 'modify', 'Modify existing task' do
|
|
131
|
-
set_task_opts
|
|
132
|
-
parameter 'TASK', 'Task ID pattern'
|
|
133
|
-
def execute
|
|
134
|
-
session.modify_task!(task, mode: resolve_mode(mode), include: include, exclude: exclude).commit!
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
subcommand 'delete', 'Delete existing task' do
|
|
139
|
-
parameter 'TASK', 'Task ID pattern'
|
|
140
|
-
def execute
|
|
141
|
-
session.delete_task!(task).commit!
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
subcommand 'process', 'Process specified tasks' do
|
|
146
|
-
parameter '[TASK] ...', 'Task ID pattern(s)', attribute_name: :tasks
|
|
147
|
-
def execute
|
|
148
|
-
session.process_tasks!(*tasks)
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
rescue Mclone::Error
|
|
157
|
-
$stderr.puts "ERROR: #{$!.message}"
|
|
158
|
-
exit(false)
|
|
159
|
-
end
|
|
3
|
+
require 'mclone/cli'
|
data/lib/mclone/cli.rb
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'clamp'
|
|
4
|
+
require 'mclone'
|
|
5
|
+
|
|
6
|
+
include Mclone
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
|
|
10
|
+
Clamp do
|
|
11
|
+
|
|
12
|
+
using Refinements
|
|
13
|
+
|
|
14
|
+
self.default_subcommand = 'info'
|
|
15
|
+
|
|
16
|
+
option ['-f', '--force'], :flag, 'Insist on potentially dangerous actions', default: false
|
|
17
|
+
option ['-n', '--dry-run'], :flag, 'Simulation mode with no on-disk modifications', default: false
|
|
18
|
+
option ['-v', '--verbose'], :flag, 'Verbose operation', default: false
|
|
19
|
+
|
|
20
|
+
option ['-V', '--version'], :flag, 'Show version' do
|
|
21
|
+
puts VERSION
|
|
22
|
+
exit(true)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def session
|
|
26
|
+
session = Mclone::Session.new
|
|
27
|
+
session.force = force?
|
|
28
|
+
session.simulate = dry_run?
|
|
29
|
+
session.verbose = verbose?
|
|
30
|
+
session.restore_volumes!
|
|
31
|
+
session
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def resolve_mode(mode)
|
|
35
|
+
case (m = Task::MODES.resolve(mode)).size
|
|
36
|
+
when 0 then raise(Task::Error, %(no modes matching pattern "#{mode}"))
|
|
37
|
+
when 1 then m.first
|
|
38
|
+
else raise(Task::Error, %(ambiguous mode pattern "#{mode}"))
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
subcommand 'info', 'Output information on volumes & tasks' do
|
|
43
|
+
def execute
|
|
44
|
+
s = session
|
|
45
|
+
$stdout.puts "# Mclone version #{Mclone::VERSION}"
|
|
46
|
+
$stdout.puts
|
|
47
|
+
$stdout.puts '## Volumes'
|
|
48
|
+
$stdout.puts
|
|
49
|
+
s.volumes.each { |volume| $stdout.puts "* [#{volume.id}] :: (#{volume.root})" }
|
|
50
|
+
stales = []
|
|
51
|
+
intacts = []
|
|
52
|
+
intact_tasks = s.intact_tasks
|
|
53
|
+
s.tasks.each do |task|
|
|
54
|
+
ts = (t = intact_tasks[task]).nil? ? "<#{task.id}>" : "[#{task.id}]"
|
|
55
|
+
svs = s.volumes.volume(task.source_id).nil? ? "<#{task.source_id}>" : "[#{task.source_id}]"
|
|
56
|
+
dvs = s.volumes.volume(task.destination_id).nil? ? "<#{task.destination_id}>" : "[#{task.destination_id}]"
|
|
57
|
+
crypter_mode = task.crypter_mode.nil? ? nil : "#{task.crypter_mode}+"
|
|
58
|
+
xs = ["* #{ts} :: #{crypter_mode}#{task.mode} #{svs}(#{task.source_root}) -> #{dvs}(#{task.destination_root})"]
|
|
59
|
+
xs << "include #{task.include}" unless task.include.nil? || task.include.empty?
|
|
60
|
+
xs << "exclude #{task.exclude}" unless task.exclude.nil? || task.exclude.empty?
|
|
61
|
+
(t.nil? ? stales : intacts) << xs.join(' :: ')
|
|
62
|
+
end
|
|
63
|
+
unless intacts.empty?
|
|
64
|
+
$stdout.puts
|
|
65
|
+
$stdout.puts '## Intact tasks'
|
|
66
|
+
$stdout.puts
|
|
67
|
+
intacts.each { |x| $stdout.puts x }
|
|
68
|
+
end
|
|
69
|
+
unless stales.empty?
|
|
70
|
+
$stdout.puts
|
|
71
|
+
$stdout.puts '## Stale tasks'
|
|
72
|
+
$stdout.puts
|
|
73
|
+
stales.each { |x| $stdout.puts x }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
subcommand 'volume', 'Volume operations' do
|
|
79
|
+
|
|
80
|
+
subcommand ['new', 'create'], 'Create new volume' do
|
|
81
|
+
parameter 'DIRECTORY', 'Directory to become a Mclone volume'
|
|
82
|
+
def execute
|
|
83
|
+
session.format_volume!(directory)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
subcommand 'delete', 'Delete existing volume' do
|
|
88
|
+
parameter 'VOLUME', 'Volume ID pattern'
|
|
89
|
+
def execute
|
|
90
|
+
session.delete_volume!(volume).commit!
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
subcommand 'task', 'Task operations' do
|
|
97
|
+
|
|
98
|
+
def self.set_task_opts
|
|
99
|
+
modes = Task::MODES.collect(&:to_s).join(' | ')
|
|
100
|
+
option ['-m', '--mode'], 'MODE', "Operation mode (#{modes})", default: Task::MODES.first.to_s
|
|
101
|
+
option ['-i', '--include'], 'PATTERN', 'Include paths pattern'
|
|
102
|
+
option ['-x', '--exclude'], 'PATTERN', 'Exclude paths pattern'
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
subcommand ['new', 'create'], 'Create new SOURCE -> DESTINATION task' do
|
|
106
|
+
set_task_opts
|
|
107
|
+
option ['-d', '--decrypt'], :flag, 'Decrypt source'
|
|
108
|
+
option ['-e', '--encrypt'], :flag, 'Encrypt destination'
|
|
109
|
+
option ['-p', '--password'], 'PASSWORD', 'Plain text password'
|
|
110
|
+
option ['-t', '--token'], 'TOKEN', 'Rclone crypt token (obscured password)'
|
|
111
|
+
parameter 'SOURCE', 'Source path'
|
|
112
|
+
parameter 'DESTINATION', 'Destination path'
|
|
113
|
+
def execute
|
|
114
|
+
crypter_mode = nil
|
|
115
|
+
signal_usage_error 'choose either encryption or decryption mode, not both' if decrypt? && encrypt?
|
|
116
|
+
signal_usage_error 'specify either plain text password or Rclone crypt token, not both' if !password.nil? && !token.nil?
|
|
117
|
+
crypter_mode = :encrypt if encrypt?
|
|
118
|
+
crypter_mode = :decrypt if decrypt?
|
|
119
|
+
session.create_task!(
|
|
120
|
+
resolve_mode(mode),
|
|
121
|
+
source,
|
|
122
|
+
destination,
|
|
123
|
+
include: include, exclude: exclude, crypter_mode: crypter_mode, crypter_password: password, crypter_token: token
|
|
124
|
+
).commit!
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
subcommand 'modify', 'Modify existing task' do
|
|
129
|
+
set_task_opts
|
|
130
|
+
parameter 'TASK', 'Task ID pattern'
|
|
131
|
+
def execute
|
|
132
|
+
session.modify_task!(task, mode: resolve_mode(mode), include: include, exclude: exclude).commit!
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
subcommand 'delete', 'Delete existing task' do
|
|
137
|
+
parameter 'TASK', 'Task ID pattern'
|
|
138
|
+
def execute
|
|
139
|
+
session.delete_task!(task).commit!
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
subcommand 'process', 'Process specified tasks' do
|
|
144
|
+
parameter '[TASK] ...', 'Task ID pattern(s)', attribute_name: :tasks
|
|
145
|
+
def execute
|
|
146
|
+
session.process_tasks!(*tasks)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
rescue Mclone::Error
|
|
155
|
+
$stderr.puts "ERROR: #{$!.message}"
|
|
156
|
+
exit(false)
|
|
157
|
+
end
|