ruby-livesync 1.0.0.beta1 → 1.0.0.beta2
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/config/sample.rb +25 -0
- data/lib/ruby-livesync/live_sync/daemon.rb +3 -4
- data/lib/ruby-livesync/live_sync/dsl.rb +9 -6
- data/lib/ruby-livesync/live_sync/log.rb +24 -12
- data/lib/ruby-livesync/live_sync/rsync.rb +15 -8
- data/lib/ruby-livesync/live_sync/ssh.rb +0 -1
- data/lib/ruby-livesync/live_sync/sync.rb +16 -7
- data/lib/ruby-livesync/live_sync/version.rb +1 -1
- data/lib/ruby-livesync/live_sync/watcher.rb +21 -7
- data/livesync.service +13 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d19adaa07cc52746ac91f99dc954b5b85c3d1150b41ffb4e789c453efe404ae
|
4
|
+
data.tar.gz: bf4db87f65060754f52063a41dfc27094027fd8165e4b722f051e6af069dd6cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4caca9a0bb7a879d9133d0aadf74768f2252866f6b5729eb45935ee5d975bb7029a77a40d2e9ada50026ee196bf2ca1a54e5aa4169ca0e87892670a8dc818587
|
7
|
+
data.tar.gz: 50b8a7101b60a5f4b29906f38b92a1cfc1671ebac8cd7aba1fe78ae879cda7d4f2a441ec248e68a7201959f356e12dc0e5583c8cee6e9ea234ba8290b0385e7a
|
data/config/sample.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# name of the sync
|
2
|
+
# if it is an existing path then `source` is set this value
|
3
|
+
sync '4tb' do
|
4
|
+
enabled = false
|
5
|
+
|
6
|
+
# fork to user below, usually associated with private keys
|
7
|
+
user = :root
|
8
|
+
|
9
|
+
delay = 5
|
10
|
+
|
11
|
+
source = '/mnt/4tb/'
|
12
|
+
target = 'root@bhavapower:/mnt/extensor/4tb'
|
13
|
+
|
14
|
+
rsync.opts = '-ax --partial' # default
|
15
|
+
|
16
|
+
# possible values are: true, false, :initial, :watched
|
17
|
+
delete = true
|
18
|
+
|
19
|
+
excludes = [
|
20
|
+
'.snapshots',
|
21
|
+
]
|
22
|
+
|
23
|
+
log.info 'starting'
|
24
|
+
end
|
25
|
+
|
@@ -14,7 +14,7 @@ module LiveSync
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def start
|
17
|
-
Process.setpgrp
|
17
|
+
Process.setpgrp rescue nil # not allowed in systemd
|
18
18
|
Process.setproctitle 'livesync'
|
19
19
|
instance_eval File.read(config), config
|
20
20
|
run
|
@@ -33,11 +33,10 @@ module LiveSync
|
|
33
33
|
@syncs.each do |user, syncs|
|
34
34
|
User.wrap user do
|
35
35
|
syncs.each do |s|
|
36
|
-
s.
|
37
|
-
s.guard
|
36
|
+
s.guard if s.start
|
38
37
|
rescue => e
|
39
38
|
msg = e.message
|
40
|
-
msg += "\n#{e.backtrace.join "\n"}" if Log.debug
|
39
|
+
msg += "\n#{e.backtrace.join "\n"}" if Log.debug?
|
41
40
|
Log.fatal msg
|
42
41
|
end
|
43
42
|
Process.waitall
|
@@ -10,13 +10,16 @@ module LiveSync
|
|
10
10
|
class_attribute :attrs
|
11
11
|
self.attrs = []
|
12
12
|
|
13
|
-
def self.dsl attr, default: nil, type: nil, &block
|
13
|
+
def self.dsl attr, default: nil, enum: nil, type: nil, &block
|
14
14
|
self.attrs << attr
|
15
|
-
define_method attr do |
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
define_method attr do |sv=nil|
|
16
|
+
(v = instance_variable_get("@#{attr}"); return(if v.nil? then default else v end)) if sv.nil?
|
17
|
+
|
18
|
+
raise "#{ctx}/#{attr}: incorrect type" if type and !sv.is_a? type
|
19
|
+
raise "#{ctx}/#{attr}: value not one of following #{enum}" if enum and !sv.in? enum
|
20
|
+
|
21
|
+
instance_variable_set "@#{attr}", sv
|
22
|
+
instance_exec sv, &block if block
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
@@ -1,41 +1,53 @@
|
|
1
1
|
module LiveSync
|
2
2
|
class Log
|
3
3
|
|
4
|
-
class_attribute :ctx
|
5
4
|
class_attribute :global
|
6
5
|
self.global = self.new
|
7
6
|
class << self
|
8
7
|
delegate_missing_to :global
|
9
8
|
end
|
10
9
|
|
11
|
-
class_attribute :
|
12
|
-
|
10
|
+
class_attribute :ctx
|
11
|
+
|
12
|
+
LEVELS = {
|
13
|
+
debug: 0,
|
14
|
+
verbose: 1,
|
15
|
+
info: 2,
|
16
|
+
warning: 3,
|
17
|
+
error: 4,
|
18
|
+
fatal: 5,
|
19
|
+
}
|
20
|
+
class_attribute :level
|
21
|
+
self.level = if !!ENV['DEBUG'] then :debug else ENV['LEVEL']&.to_sym || :info end
|
22
|
+
def self.debug?; level == :debug; end
|
13
23
|
|
14
24
|
def initialize ctx=nil
|
15
25
|
self.ctx = ctx
|
16
26
|
end
|
17
27
|
|
18
|
-
def
|
19
|
-
|
20
|
-
puts "DEBUG: #{parse msg}"
|
28
|
+
def log? level
|
29
|
+
LEVELS[level] >= LEVELS[self.level]
|
21
30
|
end
|
22
31
|
|
32
|
+
def debug msg
|
33
|
+
puts "DEBUG: #{parse msg}" if log? :debug
|
34
|
+
end
|
23
35
|
def info msg
|
24
|
-
puts "INFO: #{parse msg}"
|
36
|
+
puts "INFO: #{parse msg}" if log? :info
|
25
37
|
end
|
26
38
|
|
27
39
|
def warning msg
|
28
|
-
STDERR.puts "WARNING: #{parse msg}"
|
40
|
+
STDERR.puts "WARNING: #{parse msg}" if log? :warning
|
29
41
|
end
|
30
|
-
|
31
42
|
def error msg
|
32
|
-
STDERR.puts "ERROR: #{parse msg}"
|
43
|
+
STDERR.puts "ERROR: #{parse msg}" if log? :error
|
33
44
|
end
|
34
|
-
|
35
45
|
def fatal msg
|
36
|
-
STDERR.puts "FATAL: #{parse msg}"
|
46
|
+
STDERR.puts "FATAL: #{parse msg}" if log? :fatal
|
37
47
|
end
|
38
48
|
|
49
|
+
protected
|
50
|
+
|
39
51
|
def parse msg
|
40
52
|
msg = "#{ctx}: #{msg}" if ctx
|
41
53
|
msg
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module LiveSync
|
2
2
|
class Rsync
|
3
3
|
|
4
|
-
attr_reader :sync
|
4
|
+
attr_reader :sync
|
5
|
+
attr_accessor :opts
|
5
6
|
|
6
7
|
def initialize sync
|
7
8
|
@sync = sync
|
@@ -13,11 +14,15 @@ module LiveSync
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def initial
|
16
|
-
|
17
|
+
args = []
|
18
|
+
args << '--delete' if sync.delete.in? [true, :initial]
|
19
|
+
run :initial, *args, loglevel: :info
|
17
20
|
end
|
18
21
|
|
19
|
-
def
|
20
|
-
|
22
|
+
def partial paths
|
23
|
+
args = ['--files-from=-']
|
24
|
+
args << '--delete-missing-args' if sync.delete.in? [true, :watched]
|
25
|
+
run :partial, *args do |stdin, stdout, stderr|
|
21
26
|
stdin.write paths.join "\n"
|
22
27
|
stdin.close
|
23
28
|
end
|
@@ -25,15 +30,17 @@ module LiveSync
|
|
25
30
|
|
26
31
|
protected
|
27
32
|
|
28
|
-
def run type, args
|
29
|
-
cmd = "rsync -e '#{rsh}' #{opts} #{sync.source} #{sync.target} #{args}"
|
30
|
-
sync.
|
33
|
+
def run type, *args, loglevel: :debug
|
34
|
+
cmd = "rsync -e '#{rsh}' #{opts} #{sync.source} #{sync.target} #{args.join ' '}"
|
35
|
+
sync.excludes.each{ |e| cmd << " --exclude='#{e}'" }
|
36
|
+
|
37
|
+
sync.log.send loglevel, "#{type}: starting with cmd: #{cmd}"
|
31
38
|
stdin, stdout, stderr, @wait_thr = Open3.popen3 cmd
|
32
39
|
yield stdin, stdout, stderr if block_given?
|
33
40
|
Thread.new do
|
34
41
|
Process.wait @wait_thr.pid rescue Errno::ECHILD; nil
|
35
42
|
@wait_thr = nil
|
36
|
-
sync.log.
|
43
|
+
sync.log.send loglevel, "#{type}: finished"
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
@@ -14,6 +14,7 @@ module LiveSync
|
|
14
14
|
fill_name name
|
15
15
|
source name if File.exist? name
|
16
16
|
@to_sync = Set.new
|
17
|
+
@rsync = Rsync.new self
|
17
18
|
end
|
18
19
|
|
19
20
|
def fill_name name
|
@@ -22,6 +23,8 @@ module LiveSync
|
|
22
23
|
@log = Log.new ctx
|
23
24
|
end
|
24
25
|
|
26
|
+
dsl :enabled, default: true
|
27
|
+
|
25
28
|
dsl :user, default: :root
|
26
29
|
dsl :source do |source|
|
27
30
|
raise "#{ctx}: source not found" unless File.exist? source
|
@@ -32,24 +35,30 @@ module LiveSync
|
|
32
35
|
dsl :target do |target|
|
33
36
|
@userhost, @target_path = target.split(':')
|
34
37
|
raise "#{ctx}: missing target path" unless @target_path
|
35
|
-
@
|
38
|
+
source File.join(@source, '') if File.basename(@source) == File.basename(@target_path)
|
36
39
|
end
|
37
40
|
|
38
41
|
dsl :delay, default: 5, type: Integer
|
39
42
|
|
40
|
-
|
43
|
+
dsl :delete, default: false, enum: [true,false] + %i[initial watched]
|
44
|
+
|
45
|
+
dsl :excludes, default: []
|
46
|
+
|
47
|
+
def start
|
48
|
+
return log.warning('skipping disabled sync') && false unless enabled
|
41
49
|
raise "#{ctx}: missing target" unless @target
|
42
|
-
@ssh
|
43
|
-
|
50
|
+
@ssh = Ssh.connect @userhost
|
51
|
+
sleep 1 and log.warning 'waiting for ssh' while !@ssh.available?
|
52
|
+
true
|
44
53
|
end
|
45
54
|
|
46
55
|
def guard
|
47
56
|
fork do
|
48
57
|
Process.setproctitle "livesync: sync #{ctx}"
|
49
|
-
@watcher = Watcher.new
|
58
|
+
@watcher = Watcher.new self
|
50
59
|
@scheduler = Rufus::Scheduler.new
|
51
60
|
|
52
|
-
@watcher.dir_rwatch source,
|
61
|
+
@watcher.dir_rwatch source, &method(:track)
|
53
62
|
@rsync.initial
|
54
63
|
schedule
|
55
64
|
sleep 1.day while true
|
@@ -68,7 +77,7 @@ module LiveSync
|
|
68
77
|
return if @rsync.running?
|
69
78
|
@watcher.process # calls #track
|
70
79
|
return if @to_sync.blank?
|
71
|
-
@rsync.
|
80
|
+
@rsync.partial @to_sync
|
72
81
|
@to_sync.clear
|
73
82
|
ensure
|
74
83
|
schedule
|
@@ -4,13 +4,16 @@ module LiveSync
|
|
4
4
|
attr_reader :notifier
|
5
5
|
delegate_missing_to :notifier
|
6
6
|
|
7
|
-
|
7
|
+
attr_reader :sync
|
8
|
+
|
9
|
+
def initialize sync=nil
|
10
|
+
@sync = sync
|
8
11
|
@notifier = INotify::Notifier.new
|
9
12
|
end
|
10
13
|
|
11
14
|
def watch path, *modes
|
12
|
-
Log.
|
13
|
-
modes
|
15
|
+
Log.debug "#{path}: watching for #{modes.join ','}"
|
16
|
+
modes = %i[all_events] if modes.blank?
|
14
17
|
|
15
18
|
notifier.watch path, *modes do |event|
|
16
19
|
yield event
|
@@ -20,15 +23,26 @@ module LiveSync
|
|
20
23
|
|
21
24
|
def dir_rwatch path, *modes
|
22
25
|
raise "#{path}: not a directory" unless File.directory? path
|
23
|
-
modes
|
26
|
+
modes = %i[create modify] if modes.blank?
|
27
|
+
modes << :delete if sync&.delete&.in? [true, :watched]
|
28
|
+
|
29
|
+
excs = sync&.excludes.map{ |e| Regexp.new e } || []
|
30
|
+
tgts = [path] + Dir.glob("#{path}/{.**,**}/")
|
31
|
+
rtgts = tgts.map{ |t| Pathname.new(t).relative_path_from(path).to_s }
|
32
|
+
excs.each do |e|
|
33
|
+
next unless mt = rtgts.find{ |rt| e.match rt }
|
34
|
+
Log.debug "watcher: skipping #{path}/#{mt} with subdirs"
|
35
|
+
rtgts.delete mt
|
36
|
+
rtgts.delete_if{ |rt| rt.start_with? mt } if File.directory? "#{path}/#{mt}"
|
37
|
+
end
|
24
38
|
|
25
|
-
|
26
|
-
|
39
|
+
rtgts.each do |rt|
|
40
|
+
t = "#{path}/#{rt}"
|
27
41
|
notifier.watch t, *modes do |event|
|
28
42
|
yield event
|
29
43
|
end
|
30
44
|
rescue => e
|
31
|
-
Log.warning "#{t}: skipping due to #{e.class}: #{e.message}"
|
45
|
+
Log.warning "watcher: #{t}: skipping due to #{e.class}: #{e.message}"
|
32
46
|
end
|
33
47
|
self
|
34
48
|
end
|
data/livesync.service
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-livesync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Braulio Oliveira
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -75,6 +75,7 @@ extensions: []
|
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
77
|
- bin/livesync
|
78
|
+
- config/sample.rb
|
78
79
|
- lib/ruby-livesync.rb
|
79
80
|
- lib/ruby-livesync/live_sync.rb
|
80
81
|
- lib/ruby-livesync/live_sync/daemon.rb
|
@@ -86,6 +87,7 @@ files:
|
|
86
87
|
- lib/ruby-livesync/live_sync/user.rb
|
87
88
|
- lib/ruby-livesync/live_sync/version.rb
|
88
89
|
- lib/ruby-livesync/live_sync/watcher.rb
|
90
|
+
- livesync.service
|
89
91
|
homepage: https://github.com/brauliobo/ruby-livesync
|
90
92
|
licenses:
|
91
93
|
- GPLv3
|