docker-sync 0.1.5 → 0.2.0.beta5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 25a78699ee82484750b206b8a01c8dc9d6321a3c
4
- data.tar.gz: a03a06fd88f5b1f396e5cdd51423a3608b4761c2
3
+ metadata.gz: 94abd2200815b1114719e58149b90ecf713ff5ee
4
+ data.tar.gz: f3e8d3f36eaf2046aa0f664670659aba57265e19
5
5
  SHA512:
6
- metadata.gz: b83b63b54356843e661d5f29aa819d53a0fbf2661f6e51bd5513530ed5c3a01683afd8f4abac2f332d3685bebcce93cda2817feddcc9c7a019370162fa26af19
7
- data.tar.gz: bfd090b4b29ce2be0b875ba66ab31764c92e9a6e70fd104d7282e40b6b4c0c806bba17cc66df4994f869127a96f8ed70afe0f73e486b69b4c83100387aa87efe
6
+ metadata.gz: dc26e58c0a5641cd7aadb5fd6af54a07cfb7b6e3760d0f71782aae7d83273ceadfd4cbc6b8f58e396aaeda64d53f8d951c30c02a172d610fb1a9178425b3aabc
7
+ data.tar.gz: bad573788249515c63475299b2058b38240589ae27fce69e1ce9396729d22a6db5ea4da957dbee6932a08aae2c22b2572af088e464fb09538be8b479a7f1ec51
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.5
1
+ 0.2.0.beta5
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
3
+ file = File.expand_path(File.dirname(file))
4
+ $LOAD_PATH.unshift File.expand_path('../lib', file)
5
+
6
+ require 'thor'
7
+ require 'docker-sync/update_check'
8
+
9
+ thor = File.expand_path('../tasks/daemon', file)
10
+ Dir.glob(File.join(thor, '/**/*.thor')).each { |taskfile|
11
+ load taskfile
12
+ }
13
+ thor = File.expand_path('../tasks/sync', file)
14
+ Dir.glob(File.join(thor, '/**/*.thor')).each { |taskfile|
15
+ load taskfile
16
+ }
17
+
18
+ Daemon.start
@@ -3,20 +3,9 @@ file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
3
3
  file = File.expand_path(File.dirname(file))
4
4
  $LOAD_PATH.unshift File.expand_path('../lib', file)
5
5
 
6
-
7
6
  require 'thor'
8
7
  require 'docker-sync/update_check'
9
8
 
10
- # TODO: remove this later, not longer needed
11
- #begin
12
- # gem 'docker-compose'
13
- #rescue Gem::LoadError
14
- # puts "docker-compose gem is missing, installing 'gem install docker-compose'"
15
- # system('gem install docker-compose')
16
- # puts 'docker-compose ruby gem installed, please start docker-sync-stack again'
17
- # exit 0
18
- #end
19
-
20
9
  thor = File.expand_path('../tasks/stack', file)
21
10
  Dir.glob(File.join(thor, '/**/*.thor')).each { |taskfile|
22
11
  # noinspection RubyResolve
@@ -10,22 +10,15 @@ class ComposeManager
10
10
  ### production docker-compose.yml
11
11
  compose_files = [File.expand_path('docker-compose.yml')]
12
12
  if @global_options.key?('compose-file-path')
13
- path = File.expand_path(@global_options['compose-file-path'])
14
- unless File.exist?(path)
15
- raise("Your referenced docker-compose file in docker-sync.yml was not found at #{@global_options['compose-file-path']}")
16
- end
17
- compose_files = [path] # replace
13
+ compose_files = [] # replace
14
+ apply_path_settings(compose_files, @global_options['compose-file-path'], 'compose-file-path')
18
15
  end
19
16
 
20
17
  ### development docker-compose-dev.yml
21
18
  if @global_options.key?('compose-dev-file-path')
22
19
  # explicit path given
23
- path = File.expand_path(@global_options['compose-dev-file-path'])
24
- unless File.exist?(path)
25
- raise("Your referenced docker-compose-dev file in docker-sync.yml was not found at #{@global_options['compose-dev-file-path']}")
26
- end
20
+ apply_path_settings(compose_files, @global_options['compose-dev-file-path'], 'compose-dev-file-path')
27
21
  say_status 'ok',"Found explicit docker-compose-dev.yml and using it from #{@global_options['compose-dev-file-path']}", :green
28
- compose_files.push path # add
29
22
  else
30
23
  # try to find docker-compose-dev.yml
31
24
  e = compose_files.to_enum
@@ -44,9 +37,9 @@ class ComposeManager
44
37
  say_status 'ok','starting compose',:green
45
38
  options = Hash.new
46
39
  if @global_options['compose-force-build']
47
- options['build'] = true
40
+ options[:build] = true
48
41
  end
49
- @compose_session.up(options)
42
+ @compose_session.up(**options)
50
43
  end
51
44
 
52
45
  def stop
@@ -56,4 +49,28 @@ class ComposeManager
56
49
  def clean
57
50
  @compose_session.down
58
51
  end
52
+
53
+ protected
54
+ def apply_path_settings(compose_files, settings, error_key)
55
+ if settings.kind_of?(Array)
56
+ apply_filepaths(compose_files, settings, error_key)
57
+ else
58
+ apply_filepath(compose_files, settings, error_key)
59
+ end
60
+ end
61
+
62
+ private
63
+ def apply_filepath(array, filepath, error_key)
64
+ path = File.expand_path(filepath)
65
+ unless File.exist?(path)
66
+ raise("Your referenced docker-compose file in docker-sync.yml was not found at #{error_key}")
67
+ end
68
+ array.push path
69
+ end
70
+
71
+ def apply_filepaths(array, filepath_array, error_key)
72
+ filepath_array.each do |filepath|
73
+ apply_filepath(array, filepath, error_key)
74
+ end
75
+ end
59
76
  end
@@ -1,9 +1,16 @@
1
1
  require 'pp'
2
2
  require 'pathname'
3
3
  require 'yaml'
4
- # this has basically completely reused from Thor::runner.rb - thank you!
4
+ require 'dotenv'
5
+ require 'docker-sync/config_template'
5
6
 
7
+ # this has basically completely reused from Thor::runner.rb - thank you!
6
8
  module DockerSyncConfig
9
+
10
+ def initialize(options)
11
+ Dotenv.load
12
+ end
13
+
7
14
  def self.global_config_location
8
15
  return File.expand_path('~/.docker-sync-global.yml')
9
16
  end
@@ -19,7 +26,7 @@ module DockerSyncConfig
19
26
  # noinspection RubyStringKeysInHashInspection
20
27
  defaults = {'update_check'=>true, 'update_last_check' => date.iso8601(9), 'update_enforce' => true}
21
28
  if File.exist?(global_config_path)
22
- config = YAML.load_file(global_config_path)
29
+ config = ConfigTemplate::interpolate_config_file(global_config_path)
23
30
  config = defaults.merge(config)
24
31
  return config
25
32
  else
@@ -32,13 +39,32 @@ module DockerSyncConfig
32
39
  File.open(global_config_path, 'w') {|f| f.write config.to_yaml }
33
40
  end
34
41
 
42
+
43
+ def self.project_required_config_version
44
+ return '2'
45
+ end
46
+
47
+ def self.project_ensure_configuration_version_compatibility(config)
48
+ return false unless config.key?('version')
49
+ return false if config['version'].to_s != project_required_config_version.to_s
50
+ return true
51
+ end
52
+
35
53
  def self.project_config_path
36
54
  files = project_config_find
37
55
  if files.length > 0
38
- return files.pop
56
+ path = files.pop
39
57
  else
40
58
  raise('No docker-sync.yml configuration found in your path ( traversing up ) Did you define it for your project?')
41
59
  end
60
+
61
+ begin
62
+ config = YAML.load_file(path)
63
+ raise "Version of docker-sync.yml does not match the reqiured one" unless project_ensure_configuration_version_compatibility(config)
64
+ rescue Exception => e
65
+ raise "You docker-sync.yml file does either not include a version: \"#{project_required_config_version}\" setting or your setting (#{config['version']}) does not match the required version (#{project_required_config_version}). (Add this if you migrated from docker-sync 0.1.x)"
66
+ end
67
+ return path
42
68
  end
43
69
 
44
70
  # this has been ruthlessly stolen from Thor/runner.rb - please do not hunt me for that :)
@@ -0,0 +1,14 @@
1
+ require "yaml"
2
+ require 'dotenv'
3
+
4
+ module ConfigTemplate
5
+ def self.interpolate_config_file(config_path)
6
+ env_hash = {}
7
+ ENV.each {|k,v| env_hash[k.to_sym] = v }
8
+ # assuming the checks that file exist have already been performed
9
+ config_string = File.read(config_path)
10
+ config_string.gsub!('${', '%{')
11
+ config_string = config_string % env_hash
12
+ return YAML.load(config_string)
13
+ end
14
+ end
@@ -4,7 +4,9 @@ module Preconditions
4
4
  def self.check_all_preconditions
5
5
  docker_available
6
6
  docker_running
7
- fswatch_available
7
+ unison_available
8
+ unox_available
9
+ macfsevents_available
8
10
  end
9
11
 
10
12
  def self.docker_available
@@ -48,21 +50,52 @@ module Preconditions
48
50
  if (find_executable0 'unison-fsmonitor').nil?
49
51
  cmd1 = 'curl "https://raw.githubusercontent.com/hnsl/unox/master/unox.py" -o "/usr/local/bin/unison-fsmonitor" \
50
52
  && chmod +x /usr/local/bin/unison-fsmonitor'
51
- cmd2 = 'sudo easy_install pip && sudo pip install macfsevents'
52
- Thor::Shell::Basic.new.say_status 'warning', 'Could not find unison-fsmonitor (for file watching) binary in $PATH. Please install unox before you continue, see https://github.com/hnsl/unox.', :yellow
53
- if Thor::Shell::Basic.new.yes?('Shall I install unison-fsmonitor for you? ')
53
+
54
+ Thor::Shell::Basic.new.say_status 'warning', 'Could not find unison-fsmonitor (for file watching) binary in $PATH. We try to install unox now (for manual instracutions see https://github.com/hnsl/unox.)', :red
55
+ if Thor::Shell::Basic.new.yes?('Shall I install unison-fsmonitor for you? (y/N)')
54
56
  system cmd1
55
- if $?.exitstatus > 0
56
- raise('Failed to install unison-fsmonitor, please file an issue with the output of the error')
57
- end
58
- Thor::Shell::Basic.new.say_status 'ok','install macfsevents using pip (This will ask for sudo, since we use the system python)', :yellow
57
+ else
58
+ raise("Please install it, see https://github.com/hnsl/unox, or simply run :\n #{cmd1} && #{cmd2}")
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ def self.macfsevents_available
65
+ `python -c 'import fsevents'`
66
+ unless $?.success?
67
+ Thor::Shell::Basic.new.say_status 'warning','Could not find macfsevents. Will try to install it using pip', :red
68
+ sudo = false
69
+ if find_executable0('python') == '/usr/bin/python'
70
+ Thor::Shell::Basic.new.say_status 'ok','You seem to use the system python, we will need sudo below'
71
+ sudo = true
72
+ cmd2 = 'sudo easy_install pip && sudo pip install macfsevents'
73
+ else
74
+ Thor::Shell::Basic.new.say_status 'ok','You seem to have a custom python, using non-sudo commands'
75
+ sudo = false
76
+ cmd2 = 'easy_install pip && pip install macfsevents'
77
+ end
78
+ if sudo
79
+ question = 'I will ask you for you root password to install macfsevent by running (This will ask for sudo, since we use the system python)'
80
+ else
81
+ question = 'I will now install macfsevents for you by running'
82
+ end
83
+
84
+ Thor::Shell::Basic.new.say_status 'info', "#{question}: `#{cmd2}\n\n"
85
+ if Thor::Shell::Basic.new.yes?('Shall i continue? (y/N)')
59
86
  system cmd2
60
87
  if $?.exitstatus > 0
61
88
  raise('Failed to install macfsevents, please file an issue with the output of the error')
62
89
  end
90
+ `python -c 'import fsevents'`
91
+ unless $?.success?
92
+ raise('Somehow could not successfully install macfsevents even though i treed. Please report this issue')
93
+ end
63
94
  else
64
- raise("Please install it, see https://github.com/hnsl/unox, or simply run :\n #{cmd1} && #{cmd2}")
95
+ raise('Please install macfsevents manually, see https://github.com/EugenMayer/docker-sync/wiki/1.-Installation')
65
96
  end
66
97
  end
98
+
99
+
67
100
  end
68
101
  end
@@ -4,6 +4,8 @@ require 'docker-sync/sync_process'
4
4
  # noinspection RubyResolve
5
5
  require 'docker-sync/execution'
6
6
  require 'yaml'
7
+ require 'dotenv'
8
+ require 'docker-sync/config_template'
7
9
 
8
10
  module Docker_sync
9
11
  class SyncManager
@@ -14,6 +16,8 @@ module Docker_sync
14
16
  @config_path
15
17
 
16
18
  def initialize(options)
19
+ Dotenv.load
20
+
17
21
  @sync_processes = []
18
22
  @config_syncs = []
19
23
  @config_global = []
@@ -26,7 +30,8 @@ module Docker_sync
26
30
  raise "Config could not be loaded from #{@config_path} - it does not exist"
27
31
  end
28
32
 
29
- config = YAML.load_file(@config_path)
33
+ config = ConfigTemplate::interpolate_config_file(@config_path)
34
+
30
35
  validate_config(config)
31
36
  @config_global = config['options'] || {}
32
37
  @config_syncs = config['syncs']
@@ -44,9 +49,14 @@ module Docker_sync
44
49
  def upgrade_syncs_config
45
50
  @config_syncs.each do |name, config|
46
51
  @config_syncs[name]['config_path'] = @config_path
47
- # expand the sync source to remove ~ and similar expressions in the path
48
- @config_syncs[name]['src'] = File.expand_path(@config_syncs[name]['src'], File.dirname(@config_path))
49
- @config_syncs[name]['cli_mode'] = @config_global['cli_mode'] || 'auto'
52
+
53
+ # [nbr] convert the sync src from relative to absolute path
54
+ # preserve '/' as it may be significant to the sync cmd
55
+ absolute_path = File.expand_path(@config_syncs[name]['src'])
56
+ absolute_path << "/" if @config_syncs[name]['src'].end_with?("/")
57
+ @config_syncs[name]['src'] = absolute_path
58
+
59
+ @config_syncs[name]['cli_mode'] = @config_global['cli_mode'] || 'auto'
50
60
 
51
61
  # set the global verbose setting, if the sync-endpoint does not define a own one
52
62
  unless config.key?('verbose')
@@ -80,13 +90,14 @@ module Docker_sync
80
90
 
81
91
  def validate_sync_config(name, sync_config)
82
92
  config_mandatory = %w[src dest]
83
- config_mandatory.push('sync_host_port') unless sync_config['sync_strategy'] == 'unison' #TODO: Implement autodisovery for other strategies
93
+ config_mandatory.push('sync_host_port') if sync_config['sync_strategy'] == 'rsync' #TODO: Implement autodisovery for other strategies
84
94
  config_mandatory.each do |key|
85
- raise ("#{name} does not have #{key} condiguration value set - this is mandatory") unless sync_config.key?(key)
95
+ raise ("#{name} does not have #{key} configuration value set - this is mandatory") unless sync_config.key?(key)
86
96
  end
87
97
  end
88
98
 
89
99
  def init_sync_processes(sync_name = nil)
100
+ return if @sync_processes.size != 0
90
101
  if sync_name.nil?
91
102
  @config_syncs.each { |name, sync_configuration|
92
103
  @sync_processes.push(create_sync(name, sync_configuration))
@@ -114,6 +125,13 @@ module Docker_sync
114
125
  }
115
126
  end
116
127
 
128
+ def start_container(sync_name = nil)
129
+ init_sync_processes(sync_name)
130
+ @sync_processes.each { |sync_process|
131
+ sync_process.start_container
132
+ }
133
+ end
134
+
117
135
  def run(sync_name = nil)
118
136
  init_sync_processes(sync_name)
119
137
 
@@ -1,7 +1,6 @@
1
1
  require 'thor/shell'
2
2
  # noinspection RubyResolve
3
3
  require 'docker-sync/sync_strategy/rsync'
4
- require 'docker-sync/sync_strategy/unison-onesided'
5
4
  require 'docker-sync/sync_strategy/unison'
6
5
  # noinspection RubyResolve
7
6
  require 'docker-sync/watch_strategy/fswatch'
@@ -36,15 +35,11 @@ module Docker_Sync
36
35
  case @options['sync_strategy']
37
36
  when 'rsync'
38
37
  @sync_strategy = Docker_Sync::SyncStrategy::Rsync.new(@sync_name, @options)
39
- when 'unison-onesided'
40
- @sync_strategy = Docker_Sync::SyncStrategy::Unison_Onesided.new(@sync_name, @options)
41
- when 'unison'
42
- @sync_strategy = Docker_Sync::SyncStrategy::Unison.new(@sync_name, @options)
43
38
  else
44
- @sync_strategy = Docker_Sync::SyncStrategy::Rsync.new(@sync_name, @options)
39
+ @sync_strategy = Docker_Sync::SyncStrategy::Unison.new(@sync_name, @options)
45
40
  end
46
41
  else
47
- @sync_strategy = Docker_Sync::SyncStrategy::Rsync.new(@sync_name, @options)
42
+ @sync_strategy = Docker_Sync::SyncStrategy::Unison.new(@sync_name, @options)
48
43
  end
49
44
  end
50
45
 
@@ -55,16 +50,14 @@ module Docker_Sync
55
50
  @watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
56
51
  when 'disable','dummy'
57
52
  @watch_strategy = Docker_Sync::WatchStrategy::Dummy.new(@sync_name, @options)
58
- when 'unison'
59
- @watch_strategy = Docker_Sync::WatchStrategy::Unison.new(@sync_name, @options)
60
53
  else
61
- @watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
54
+ @watch_strategy = Docker_Sync::WatchStrategy::Unison.new(@sync_name, @options)
62
55
  end
63
56
  else
64
- if @options['sync_strategy'] == 'unison'
65
- @watch_strategy = Docker_Sync::WatchStrategy::Unison.new(@sync_name, @options)
66
- else
57
+ if @options['sync_strategy'] == 'rsync'
67
58
  @watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
59
+ else
60
+ @watch_strategy = Docker_Sync::WatchStrategy::Unison.new(@sync_name, @options)
68
61
  end
69
62
  end
70
63
  end
@@ -93,6 +86,10 @@ module Docker_Sync
93
86
  @sync_strategy.sync
94
87
  end
95
88
 
89
+ def start_container
90
+ @sync_strategy.start_container
91
+ end
92
+
96
93
  def watch
97
94
  @watch_strategy.run
98
95
  end
@@ -67,7 +67,7 @@ module Docker_Sync
67
67
  # in the config - see start_container
68
68
  #args.push("--usermap='*:#{@options['sync_user']}'") if @options.key?('sync_user')
69
69
  #args.push("--groupmap='*:#{@options['sync_group']}'") if @options.key?('sync_group')
70
- args.push("#{@options['src']}/") # add a trailing slash
70
+ args.push("'#{@options['src']}'")
71
71
  args.push("rsync://#{@options['sync_host_ip']}:#{@options['sync_host_port']}/volume")
72
72
  return args
73
73
  end
@@ -96,7 +96,7 @@ module Docker_Sync
96
96
  def expand_ignore_strings
97
97
  expanded_ignore_strings = []
98
98
 
99
- exclude_type = 'Path'
99
+ exclude_type = 'Name'
100
100
  unless @options['sync_excludes_type'].nil?
101
101
  exclude_type = @options['sync_excludes_type']
102
102
  end
@@ -121,16 +121,25 @@ module Docker_Sync
121
121
  args.push(@options['src'])
122
122
  args.push('-auto')
123
123
  args.push('-batch')
124
+ args.push(sync_prefer) if @options.key?('sync_prefer')
124
125
  args.push(@options['sync_args']) if @options.key?('sync_args')
125
126
  sync_host_port = get_host_port(get_container_name, UNISON_CONTAINER_PORT)
126
127
  args.push("socket://#{@options['sync_host_ip']}:#{sync_host_port}")
127
128
 
128
129
  if @options.key?('sync_group') || @options.key?('sync_groupid')
129
- raise('Unison does not support sync_user, sync_group, sync_groupid - please use rsync if you need that')
130
+ raise('Unison does not support sync_group, sync_groupid - please use rsync if you need that')
130
131
  end
131
132
  return args
132
133
  end
133
134
 
135
+ def sync_prefer
136
+ case @options['sync_prefer']
137
+ when 'src' then "-prefer #{@options['src']}"
138
+ when 'dest' then "-prefer #{@options['dest']}"
139
+ else "-prefer #{@options['sync_prefer']}"
140
+ end
141
+ end
142
+
134
143
  def start_container
135
144
  say_status 'ok', 'Starting unison', :white
136
145
  container_name = get_container_name
@@ -173,14 +182,25 @@ module Docker_Sync
173
182
  say_status 'command', cmd, :white if @options['verbose']
174
183
  `#{cmd}` || raise('Start failed')
175
184
  say_status 'ok', "starting initial sync of #{container_name}", :white if @options['verbose']
176
- # this sleep is needed since the container could be not started
177
- sleep 5 # TODO: replace with unison -testserver
185
+ # wait until container is started, then sync:
186
+ sync_host_port = get_host_port(get_container_name, UNISON_CONTAINER_PORT)
187
+ cmd = "unison -testserver #{@options['dest']} \"socket://#{@options['sync_host_ip']}:#{sync_host_port}\""
188
+ say_status 'command', cmd, :white if @options['verbose']
189
+ attempt = 0
190
+ loop do
191
+ stdout, stderr, exit_status = Open3.capture3(cmd)
192
+ break if exit_status == 0
193
+ attempt += 1
194
+ break if attempt > 5
195
+ sleep 1
196
+ end
178
197
  sync
179
198
  say_status 'success', 'Unison server started', :green
180
199
  end
181
200
 
182
201
  def get_host_port(container_name, container_port)
183
- cmd = 'docker inspect --format=" {{ .NetworkSettings.Ports }} " ' + container_name + ' | /usr/bin/sed -E "s/.*map\[' + container_port + '[^ ]+ ([0-9]*)[^0-9].*/\1/"'
202
+ File.exist?('/usr/bin/sed') ? sed = '/usr/bin/sed' : sed = `which sed`.chomp # use macOS native sed in /usr/bin/sed first, fallback to sed in $PATH if it's not there
203
+ cmd = 'docker inspect --format=" {{ .NetworkSettings.Ports }} " ' + container_name + " | #{sed} " + '-E "s/.*map\[' + container_port + '[^ ]+ ([0-9]*)[^0-9].*/\1/"'
184
204
  say_status 'command', cmd, :white if @options['verbose']
185
205
  stdout, stderr, exit_status = Open3.capture3(cmd)
186
206
  if not exit_status.success?
@@ -24,7 +24,6 @@ class UpdateChecker
24
24
  # do not check the image if its the first run - since this it will be downloaded anyway
25
25
  unless DockerSyncConfig::is_first_run
26
26
  check_rsync_image
27
- check_unison_onesided_image
28
27
  check_unison_image
29
28
 
30
29
  # stop if there was an update
@@ -73,18 +72,6 @@ class UpdateChecker
73
72
 
74
73
  end
75
74
 
76
- def check_unison_onesided_image
77
- say_status 'ok','Checking if a newer unison:onesided image is available'
78
-
79
- if system("docker pull eugenmayer/unison:onesided | grep 'Downloaded newer image for'")
80
- say_status 'ok', 'Downloaded newer image for unison:onesided', :green
81
- @newer_image_found = true
82
- else
83
- say_status 'ok', 'No newer image found - current image is up to date.'
84
- end
85
-
86
- end
87
-
88
75
  def get_current_version
89
76
  path = File.expand_path('../../../', __FILE__)
90
77
  return File.read("#{path}/VERSION")
@@ -101,12 +88,13 @@ class UpdateChecker
101
88
  # update the timestamp
102
89
  now = DateTime.now
103
90
  @config['update_last_check'] = now.iso8601(9)
91
+
104
92
  DockerSyncConfig::global_config_save(@config)
105
93
 
106
94
  check = docker_sync_update_check
107
95
  if check.update_available
108
96
  say_status 'warning',"There is an update (#{check.latest_version}) available (current version #{check.current_version}). Please update before you continue",:yellow
109
- if yes?("Shall i update docker-sync to #{check.latest_version} for you?")
97
+ if yes?("Shall I update docker-sync to #{check.latest_version} for you?")
110
98
  system('gem update docker-sync')
111
99
  say_status 'success','Successfully updated, please restart docker-sync and check the changelog at https://github.com/EugenMayer/docker-sync/wiki/5.-Changelog',:green
112
100
  exit 0
@@ -23,7 +23,7 @@ class UpgradeChecker
23
23
  def should_run
24
24
  # get the update_status which is the version of the update hook which has been run already
25
25
  upgrade_status = last_upgraded_version
26
- if upgrade_status == '' || Gem::Version.new(upgrade_status) < Gem::Version.new(get_current_version) # thats how we compare the version
26
+ if upgrade_status == '' || Gem::Version.new(upgrade_status) < Gem::Version.new(UpgradeChecker.get_current_version) # thats how we compare the version
27
27
  return true
28
28
  end
29
29
 
@@ -31,14 +31,14 @@ class UpgradeChecker
31
31
  end
32
32
 
33
33
 
34
- def get_current_version
34
+ def self.get_current_version
35
35
  path = File.expand_path('../../../', __FILE__)
36
36
  return File.read("#{path}/VERSION")
37
37
  end
38
38
 
39
39
  def docker_sync_update_check
40
40
  gem_name = 'docker-sync'
41
- current_version = get_current_version
41
+ current_version = UpgradeChecker.get_current_version
42
42
  checker = GemUpdateChecker::Client.new(gem_name, current_version)
43
43
  return checker
44
44
  end
@@ -47,13 +47,21 @@ class UpgradeChecker
47
47
  # this is the upgrade hook for the unison-unox introduction / rename of unison
48
48
  if Gem::Version.new(last_upgraded_version) < Gem::Version.new('0.1.0')
49
49
  Thor::Shell::Basic.new.say_status 'warning', 'Please be aware that with the strategy "unison" is now called unison-onesided and you might need to migrate. See https://github.com/EugenMayer/docker-sync/wiki/Migration-Guide for more informations', :red
50
- unless Thor::Shell::Basic.new.yes?('Shall we continue?')
50
+ unless Thor::Shell::Basic.new.yes?('Shall we continue? (y/N)')
51
51
  exit 1
52
52
  end
53
53
  end
54
54
 
55
+ if Gem::Version.new(last_upgraded_version) < Gem::Version.new('0.2.0')
56
+ Thor::Shell::Basic.new.say_status 'warning', "A lot changed with 0.2.x! Unison is the default sync, unison-onesided has been REMOVED. If you have been using rsync, have been using unison excludes or you are not sure, please read the upgrade guide or your setup will go lala! : \n\n_Please_ read :): https://github.com/EugenMayer/docker-sync/wiki/1.2-Upgrade-Guide\n\n", :red
57
+ unless Thor::Shell::Basic.new.yes?('Shall we continue - DID you read it? (y/N)')
58
+ exit 1
59
+ end
60
+ end
61
+
62
+
55
63
  # update the upgrade_status
56
- @config['upgrade_status'] = "#{get_current_version}"
64
+ @config['upgrade_status'] = "#{UpgradeChecker.get_current_version}"
57
65
  DockerSyncConfig::global_config_save(@config)
58
66
  end
59
67
  end
@@ -54,7 +54,7 @@ module Docker_Sync
54
54
  args.push('-orIE')
55
55
  args.push(@events_to_watch.map { |pattern| "--event #{pattern}" })
56
56
  args.push(@options['watch_args']) if @options.key?('watch_args')
57
- args.push(@options['src'])
57
+ args.push("'#{@options['src']}'")
58
58
  sync_command = get_sync_cli_call
59
59
  args.push(" | xargs -I -n1 #{sync_command} -n #{@sync_name} --config='#{@options['config_path']}'")
60
60
  end
@@ -28,7 +28,7 @@ module Docker_Sync
28
28
  end
29
29
 
30
30
  def stop
31
- Process.kill "TERM", @watch_fork
31
+ Process.kill 'TERM', @watch_fork
32
32
  Process.wait @watch_fork
33
33
  end
34
34
 
@@ -0,0 +1,50 @@
1
+ require 'docker-sync/sync_manager'
2
+ require 'docker-sync/config'
3
+ require 'docker-sync/preconditions'
4
+ require 'docker-sync/update_check'
5
+ require 'docker-sync/upgrade_check'
6
+ require 'daemons'
7
+ require 'fileutils'
8
+
9
+ class Daemon < Thor
10
+ class_option :dir, :aliases => '--dir', :default => './.docker-sync', :type => :string, :desc => 'Path to PID and OUTPUT file Directory'
11
+ class_option :logd, :aliases => '--logd', :default => true, :type => :boolean, :desc => 'To log OUPUT to file on Daemon or not'
12
+ class_option :app_name, :aliases => '--name', :default => 'daemon', :type => :string, :desc => 'App name used in PID and OUTPUT file name for Daemon'
13
+ class_option :logd, :aliases => '--logd', :default => true, :type => :boolean, :desc => 'To log OUPUT to file on Daemon or not'
14
+ class_option :config, :aliases => '-c',:default => nil, :type => :string, :desc => 'Path of the docker_sync config'
15
+ class_option :sync_name, :aliases => '-n',:type => :string, :desc => 'If given, only this sync configuration will be references/started/synced'
16
+ class_option :version, :aliases => '-v',:type => :boolean, :default => false, :desc => 'prints out the version of docker-sync and exits'
17
+
18
+ desc 'start', 'Start docker-sync daemon'
19
+ def start
20
+ opt = options.dup
21
+ opt.merge!(:daemon => true)
22
+ sync = Sync.new([], opt)
23
+ sync.start
24
+ end
25
+
26
+ desc 'stop', 'Stop docker-sync daemon'
27
+ def stop
28
+ opt = options.dup
29
+ opt.merge!(:daemon => true)
30
+ sync = Sync.new([], opt)
31
+ sync.stop
32
+ end
33
+
34
+ desc 'clean', 'Clean docker-sync daemon'
35
+ def clean
36
+ opt = options.dup
37
+ opt.merge!(:daemon => true)
38
+ sync = Sync.new([], opt)
39
+ sync.clean
40
+ end
41
+
42
+ desc 'log', 'Prints last 100 lines of daemon log. Only for use with docker-sync started in background.'
43
+ method_option :lines, :aliases => '--lines', :default => 100, :type => :numeric, :desc => 'Specify number of lines to tail'
44
+ method_option :follow, :aliases => '-f', :default => false, :type => :boolean, :desc => 'Specify if the logs should be streamed'
45
+ def log
46
+ opt = options.dup
47
+ sync = Sync.new([], opt)
48
+ sync.log
49
+ end
50
+ end
@@ -8,10 +8,16 @@ require 'docker-sync/compose'
8
8
  class Stack < Thor
9
9
  class_option :config, :aliases => '-c', :default => nil, :type => :string, :desc => 'Path of the docker_sync config'
10
10
  class_option :sync_name, :aliases => '-n', :type => :string, :desc => 'If given, only this sync configuration will be references/started/synced'
11
+ class_option :version, :aliases => '-v',:type => :boolean, :default => false, :desc => 'prints out the version of docker-sync and exits'
11
12
 
12
13
  desc 'start', 'Start sync services, watcher and then your docker-compose defined stack'
13
14
 
14
15
  def start
16
+ if options[:version]
17
+ puts UpgradeChecker.get_current_version
18
+ exit(0)
19
+ end
20
+
15
21
  # do run update check in the start command only
16
22
  updates = UpdateChecker.new
17
23
  updates.run
@@ -37,6 +43,8 @@ class Stack < Thor
37
43
  end
38
44
  end
39
45
 
46
+ say_status 'note:', 'You can also run docker-sync in the background with docker-sync --daemon'
47
+
40
48
  @sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
41
49
  @sync_manager.run(options[:sync_name])
42
50
  global_options = @sync_manager.global_options
@@ -63,6 +71,11 @@ class Stack < Thor
63
71
  desc 'clean', 'compose down your app stack, stop and clean up all sync endpoints'
64
72
 
65
73
  def clean
74
+ if options[:version]
75
+ puts UpgradeChecker.get_current_version
76
+ exit(0)
77
+ end
78
+
66
79
  begin
67
80
  Preconditions::check_all_preconditions
68
81
  rescue Exception => e
@@ -3,108 +3,128 @@ require 'docker-sync/config'
3
3
  require 'docker-sync/preconditions'
4
4
  require 'docker-sync/update_check'
5
5
  require 'docker-sync/upgrade_check'
6
+ require 'daemons'
7
+ require 'fileutils'
6
8
 
7
9
  class Sync < Thor
8
10
 
9
11
  class_option :config, :aliases => '-c',:default => nil, :type => :string, :desc => 'Path of the docker_sync config'
10
12
  class_option :sync_name, :aliases => '-n',:type => :string, :desc => 'If given, only this sync configuration will be references/started/synced'
13
+ class_option :version, :aliases => '-v',:type => :boolean, :default => false, :desc => 'prints out the version of docker-sync and exits'
11
14
 
12
15
  desc 'start', 'Start all sync configurations in this project'
16
+ method_option :daemon, :aliases => '-d', :default => false, :type => :boolean, :desc => 'Run in the background'
17
+ method_option :app_name, :aliases => '--name', :default => 'daemon', :type => :string, :desc => 'App name used in PID and OUTPUT file name for Daemon'
18
+ method_option :dir, :aliases => '--dir', :default => './.docker-sync', :type => :string, :desc => 'Path to PID and OUTPUT file Directory'
19
+ method_option :logd, :aliases => '--logd', :default => true, :type => :boolean, :desc => 'To log OUPUT to file on Daemon or not'
13
20
  def start
14
- # do run update check in the start command only
15
- updates = UpdateChecker.new
16
- updates.run
17
- upgrades = UpgradeChecker.new
18
- upgrades.run
19
- begin
20
- Preconditions::check_all_preconditions
21
- rescue Exception => e
22
- say_status 'error', e.message, :red
23
- exit 1
21
+ if options[:version]
22
+ puts UpgradeChecker.get_current_version
23
+ exit(0)
24
24
  end
25
+ # do run update check in the start command only
26
+ UpdateChecker.new().run
27
+ UpgradeChecker.new().run
28
+
29
+ config_path = config_preconditions # Preconditions and Define config_path from shared method
30
+ @sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
25
31
 
26
- if options[:config]
27
- config_path = options[:config]
32
+ start_dir = Dir.pwd # Set start_dir variable to be equal to pre-daemonized folder, since daemonizing will change dir to '/'
33
+ if options['daemon']
34
+ daemonize
28
35
  else
29
- begin
30
- config_path = DockerSyncConfig::project_config_path
31
- rescue Exception => e
32
- say_status 'error', e.message, :red
33
- return
34
- end
36
+ say_status 'note:', 'You can also run docker-sync in the background with --daemon', :white
37
+ end
38
+
39
+ Dir.chdir(start_dir) do # We want run these in pre-daemonized folder/directory since provided config_path might not be full_path
40
+ @sync_manager.run(options[:sync_name])
41
+ @sync_manager.join_threads
35
42
  end
36
- @sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
37
- @sync_manager.run(options[:sync_name])
38
- @sync_manager.join_threads
39
43
  end
40
44
 
41
- desc 'sync', 'sync - do not start a watcher'
42
- def sync
45
+ desc 'stop', 'Stop docker-sync daemon'
46
+ method_option :app_name, :aliases => '--name', :default => 'daemon', :type => :string, :desc => 'App name used in PID and OUTPUT file name for Daemon'
47
+ method_option :dir, :aliases => '--dir', :default => './.docker-sync', :type => :string, :desc => 'Path to PID and OUTPUT file Directory'
48
+ def stop
49
+ if options[:version]
50
+ puts UpgradeChecker.get_current_version
51
+ exit(0)
52
+ end
53
+
54
+ config_path = config_preconditions
55
+ sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
56
+
43
57
  begin
44
- Preconditions::check_all_preconditions
45
- rescue Exception => e
46
- say_status 'error', e.message, :red
47
- exit 1
58
+ pid = File.read("#{options['dir']}/#{options['app_name']}.pid") # Read PID from PIDFILE created by Daemons
59
+ Process.kill(:INT, -(Process.getpgid(pid.to_i))) # Send INT signal to group PID, which means INT will be sent to all sub-processes and Threads spawned by docker-sync
60
+ say_status 'shutdown', 'Background dsync has been stopped'
61
+ rescue Errno::ESRCH, Errno::ENOENT => e
62
+ say_status 'error', e.message, :red # Rescue incase PIDFILE does not exist or there is no process with such PID
63
+ say_status(
64
+ 'error', 'Check if your PIDFILE and process with such PID exists', :red
65
+ )
48
66
  end
67
+ end
49
68
 
50
- if options[:config]
51
- config_path = options[:config]
52
- else
53
- begin
54
- config_path = DockerSyncConfig::project_config_path
55
- rescue Exception => e
56
- say_status 'error', e.message, :red
57
- return
58
- end
69
+ desc 'sync', 'just sync - do not start a watcher though'
70
+ def sync
71
+ if options[:version]
72
+ puts UpgradeChecker.get_current_version
73
+ exit(0)
59
74
  end
75
+
76
+ config_path = config_preconditions # Preconditions and Define config_path from shared method
77
+
60
78
  @sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
61
79
  @sync_manager.sync(options[:sync_name])
62
80
  end
63
81
 
64
82
  desc 'clean', 'Stop and clean up all sync endpoints'
65
83
  def clean
66
- begin
67
- Preconditions::check_all_preconditions
68
- rescue Exception => e
69
- say_status 'error', e.message, :red
70
- exit 1
84
+ if options[:version]
85
+ puts UpgradeChecker.get_current_version
86
+ exit(0)
71
87
  end
72
88
 
73
- if options[:config]
74
- config_path = options[:config]
75
- else
76
- begin
77
- config_path = DockerSyncConfig::project_config_path
78
- rescue Exception => e
79
- say_status 'error', e.message, :red
80
- return
81
- end
89
+ config_path = config_preconditions # Preconditions and Define config_path from shared method
90
+
91
+ # Look for any background syncs and stop them if we see them
92
+ dir = './.docker-sync'
93
+ files = Dir[File.join(dir, '*.pid')]
94
+ files.each do |pid_file|
95
+ pid = File.read(pid_file).to_i
96
+ Process.kill(:INT, -(Process.getpgid(pid)))
97
+ say_status 'shutdown', 'Background dsync has been stopped'
82
98
  end
99
+ # Remove the .docker-sync directory
100
+ FileUtils.rm_r dir if File.directory?(dir)
101
+
83
102
  @sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
84
103
  @sync_manager.clean(options[:sync_name])
85
104
  say_status 'success', 'Finished cleanup. Removed stopped, removed sync container and removed their volumes', :green
86
105
  end
87
106
 
107
+ desc 'log', 'Prints last 100 lines of daemon log. Only for use with docker-sync started in background.'
108
+ method_option :lines, :aliases => '--lines', :default => 100, :type => :numeric, :desc => 'Specify number of lines to tail'
109
+ method_option :follow, :aliases => '-f', :default => false, :type => :boolean, :desc => 'Specify if the logs should be streamed'
110
+ def log
111
+ if options[:version]
112
+ puts UpgradeChecker.get_current_version
113
+ exit(0)
114
+ end
115
+
116
+ print_daemon_logs
117
+ end
118
+
88
119
  desc 'list', 'List all sync-points of the project configuration path'
89
120
  method_option :verbose, :default => false, :type => :boolean, :desc => 'Verbose output'
90
121
  def list
91
- begin
92
- Preconditions::check_all_preconditions
93
- rescue Exception => e
94
- say_status 'error', e.message, :red
95
- exit 1
122
+ if options[:version]
123
+ puts UpgradeChecker.get_current_version
124
+ exit(0)
96
125
  end
97
126
 
98
- if options[:config]
99
- config_path = options[:config]
100
- else
101
- begin
102
- config_path = DockerSyncConfig::project_config_path
103
- rescue Exception => e
104
- say_status 'error', e.message, :red
105
- return
106
- end
107
- end
127
+ config_path = config_preconditions # Preconditions and Define config_path from shared method
108
128
 
109
129
  say_status 'ok',"Found configuration at #{config_path}"
110
130
  @sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
@@ -114,4 +134,71 @@ class Sync < Thor
114
134
  print_table(config) if options['verbose']
115
135
  end
116
136
  end
137
+
138
+ no_tasks do
139
+ def config_preconditions # Moved shared preconditions block into separate method to have less/cleaner code
140
+ begin
141
+ Preconditions::check_all_preconditions
142
+ rescue Exception => e
143
+ say_status 'error', e.message, :red
144
+ exit 1
145
+ end
146
+
147
+ return options[:config] if options[:config]
148
+
149
+ begin
150
+ DockerSyncConfig::project_config_path
151
+ rescue Exception => e
152
+ say_status 'error', e.message, :red
153
+ exit 1
154
+ end
155
+ end
156
+
157
+ def daemonize
158
+ # Create the directory for the logs/pid if it doesn't already exist:
159
+ FileUtils.mkpath(options['dir'])
160
+
161
+ # Check to see if we're already running:
162
+ if daemon_running?
163
+ say_status 'error', "docker-sync already started for this configuration", :red
164
+ exit 1
165
+ end
166
+
167
+ # If we're daemonizing, run a sync first to ensure the containers exist so that a docker-compose up won't fail:
168
+ @sync_manager.start_container(options[:sync_name])
169
+ # the existing strategies' start_container will also sync, but just in case a strategy doesn't, sync now:
170
+ @sync_manager.sync(options[:sync_name])
171
+
172
+ dopts = {
173
+ app_name: options['app_name'],
174
+ dir_mode: :normal,
175
+ dir: options['dir'],
176
+ log_output: options['logd'],
177
+ output_logfilename: "#{options['app_name']}.log"
178
+ } # List of options accepted by Daemonize, can be customized pretty nicely with provided CLI options
179
+
180
+ say_status 'success', 'Starting Docker-Sync in the background', :green
181
+ Daemons.daemonize(dopts)
182
+ end
183
+
184
+ def print_daemon_logs
185
+ unless daemon_running?
186
+ say_status 'error', "docker-sync is not running in daemon mode for this configuration", :red
187
+ exit 1
188
+ end
189
+
190
+ log_file = File.join(options['dir'], "#{options['app_name']}.log")
191
+ begin
192
+ system("tail #{options['follow'] ? '-f ' : ''}-n #{options['lines']} #{log_file}")
193
+ rescue Interrupt
194
+ nil
195
+ end
196
+ end
197
+
198
+ def daemon_running?
199
+ pid_file = Daemons::PidFile.find_files(options['dir'], options['app_name']).first || ''
200
+
201
+ File.file?(pid_file) && Daemons::Pid.running?(File.read(pid_file).to_i)
202
+ end
203
+ end
117
204
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docker-sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0.beta5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eugen Mayer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-10 00:00:00.000000000 Z
11
+ date: 2017-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -84,33 +84,76 @@ dependencies:
84
84
  - - '='
85
85
  - !ruby/object:Gem::Version
86
86
  version: 1.6.3
87
+ - !ruby/object:Gem::Dependency
88
+ name: dotenv
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: '2.1'
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 2.1.1
97
+ type: :runtime
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.1'
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 2.1.1
107
+ - !ruby/object:Gem::Dependency
108
+ name: daemons
109
+ requirement: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '1.2'
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 1.2.3
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '1.2'
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: 1.2.3
87
127
  description: Sync your code live to docker-containers without losing any performance
88
128
  on OSX
89
129
  email: eugen.mayer@kontextwork.de
90
130
  executables:
91
131
  - docker-sync
92
132
  - docker-sync-stack
133
+ - docker-sync-daemon
93
134
  extensions: []
94
135
  extra_rdoc_files: []
95
136
  files:
96
137
  - Thorfile
97
138
  - VERSION
98
139
  - bin/docker-sync
140
+ - bin/docker-sync-daemon
99
141
  - bin/docker-sync-stack
100
142
  - lib/docker-sync/compose.rb
101
143
  - lib/docker-sync/config.rb
144
+ - lib/docker-sync/config_template.rb
102
145
  - lib/docker-sync/execution.rb
103
146
  - lib/docker-sync/preconditions.rb
104
147
  - lib/docker-sync/sync_manager.rb
105
148
  - lib/docker-sync/sync_process.rb
106
149
  - lib/docker-sync/sync_strategy/rsync.rb
107
- - lib/docker-sync/sync_strategy/unison-onesided.rb
108
150
  - lib/docker-sync/sync_strategy/unison.rb
109
151
  - lib/docker-sync/update_check.rb
110
152
  - lib/docker-sync/upgrade_check.rb
111
153
  - lib/docker-sync/watch_strategy/dummy.rb
112
154
  - lib/docker-sync/watch_strategy/fswatch.rb
113
155
  - lib/docker-sync/watch_strategy/unison.rb
156
+ - tasks/daemon/daemon.thor
114
157
  - tasks/stack/stack.thor
115
158
  - tasks/sync/sync.thor
116
159
  homepage: https://github.com/EugenMayer/docker_sync
@@ -128,9 +171,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
171
  version: '2.0'
129
172
  required_rubygems_version: !ruby/object:Gem::Requirement
130
173
  requirements:
131
- - - ">="
174
+ - - ">"
132
175
  - !ruby/object:Gem::Version
133
- version: '0'
176
+ version: 1.3.1
134
177
  requirements: []
135
178
  rubyforge_project:
136
179
  rubygems_version: 2.4.5.1
@@ -1,137 +0,0 @@
1
- require 'thor/shell'
2
- require 'docker-sync/preconditions'
3
- require 'open3'
4
- require 'terminal-notifier'
5
-
6
- module Docker_Sync
7
- module SyncStrategy
8
- class Unison_Onesided
9
- include Thor::Shell
10
-
11
- @options
12
- @sync_name
13
- @watch_thread
14
- UNISON_CONTAINER_PORT = '5000'
15
- def initialize(sync_name, options)
16
- @sync_name = sync_name
17
- @options = options
18
- # if a custom image is set, apply it
19
- if @options.key?('image')
20
- @docker_image = @options['image']
21
- else
22
- @docker_image = 'eugenmayer/unison:onesided'
23
- end
24
- begin
25
- Preconditions::unison_available
26
- rescue Exception => e
27
- say_status 'error', "#{@sync_name} has been configured to sync with unison, but no unison available", :red
28
- say_status 'error', e.message, :red
29
- exit 1
30
- end
31
- end
32
-
33
- def run
34
- start_container
35
- sync
36
- end
37
-
38
- def sync
39
- args = sync_options
40
- cmd = 'unison ' + args.join(' ')
41
-
42
- say_status 'command', cmd, :white if @options['verbose']
43
-
44
- stdout, stderr, exit_status = Open3.capture3(cmd)
45
- if not exit_status.success?
46
- say_status 'error', "Error starting sync, exit code #{$?.exitstatus}", :red
47
- say_status 'message', stderr
48
- else
49
- TerminalNotifier.notify(
50
- "Synced #{@options['src']}", :title => @sync_name
51
- ) if @options['notify_terminal']
52
- say_status 'ok', "Synced #{@options['src']}", :white
53
- if @options['verbose']
54
- say_status 'output', stdout
55
- end
56
- end
57
- end
58
-
59
- def sync_options
60
- args = []
61
-
62
- unless @options['sync_excludes'].nil?
63
- args = @options['sync_excludes'].map { |pattern| "-ignore='Path #{pattern}'" } + args
64
- end
65
- args.push(@options['src'])
66
- args.push('-auto')
67
- args.push('-batch')
68
- args.push(@options['sync_args']) if @options.key?('sync_args')
69
- args.push("socket://#{@options['sync_host_ip']}:#{@options['sync_host_port']}/")
70
- args.push('-debug all') if @options['verbose']
71
- if @options.key?('sync_user') || @options.key?('sync_group') || @options.key?('sync_groupid') || @options.key?('sync_userid')
72
- raise('Unison does not support sync_user, sync_group, sync_groupid or sync_userid - please use rsync if you need that')
73
- end
74
- return args
75
- end
76
-
77
- def start_container
78
- say_status 'ok', 'Starting unison', :white
79
- container_name = get_container_name
80
- volume_name = get_volume_name
81
- running = `docker ps --filter 'status=running' --filter 'name=#{container_name}' --format "{{.Names}}" | grep '^#{container_name}$'`
82
- if running == ''
83
- say_status 'ok', "#{container_name} container not running", :white if @options['verbose']
84
- exists = `docker ps --filter "status=exited" --filter "name=#{container_name}" --format "{{.Names}}" | grep '^#{container_name}$'`
85
- if exists == ''
86
- say_status 'ok', "creating #{container_name} container", :white if @options['verbose']
87
- cmd = "docker run -p '#{@options['sync_host_port']}:#{UNISON_CONTAINER_PORT}' -v #{volume_name}:#{@options['dest']} -e UNISON_DIR=#{@options['dest']} -e TZ=${TZ-`readlink /etc/localtime | sed -e 's,/usr/share/zoneinfo/,,'`} --name #{container_name} -d #{@docker_image}"
88
- else
89
- say_status 'ok', "starting #{container_name} container", :white if @options['verbose']
90
- cmd = "docker start #{container_name}"
91
- end
92
- say_status 'command', cmd, :white if @options['verbose']
93
- `#{cmd}` || raise('Start failed')
94
- else
95
- say_status 'ok', "#{container_name} container still running", :blue
96
- end
97
- say_status 'ok', "starting initial sync of #{container_name}", :white if @options['verbose']
98
- # this sleep is needed since the container could be not started
99
- sleep 1
100
- sync
101
- say_status 'success', 'Unison server started', :green
102
- end
103
-
104
- def get_container_name
105
- return "#{@sync_name}"
106
- end
107
-
108
- def get_volume_name
109
- return @sync_name
110
- end
111
-
112
- def stop_container
113
- `docker stop #{get_container_name}`
114
- end
115
-
116
- def reset_container
117
- stop_container
118
- `docker rm #{get_container_name}`
119
- `docker volume rm #{get_volume_name}`
120
- end
121
-
122
- def clean
123
- reset_container
124
- end
125
-
126
- def stop
127
- say_status 'ok', "Stopping sync container #{get_container_name}"
128
- begin
129
- stop_container
130
- rescue Exception => e
131
- say_status 'error', "Stopping failed of #{get_container_name}:", :red
132
- puts e.message
133
- end
134
- end
135
- end
136
- end
137
- end