docker-sync 0.2.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2c634ad99d5e88d69c686388e3346c96b3d2c30f
4
- data.tar.gz: d87d180c0ca6bbe42c7b29fd8d3d90ee203edfb0
3
+ metadata.gz: b1fcfca4a756b6eaaaa9a278bdea971d33e06117
4
+ data.tar.gz: aa304239c78e91cdabe5bbb59435d24bbcd9f485
5
5
  SHA512:
6
- metadata.gz: 0dbce940c38568427f7c2a845ed380caec3f41ccb6f5a3beb4d0ef16cf1913b5dc5ee4a2586785423529c242100710b09a4311d26dd436ed964e9820d86f85c9
7
- data.tar.gz: ffa6b76d564ee17c3fa1a34e56a37138de831996cab2da60765aeb635b62150accd41579377e5dbaf9789005f71845b8c0242c5034f93ba5acc27b01c2c41c2a
6
+ metadata.gz: 6d50f3fafeb4b8cbd59c76e5735391f5becf785dbbc09ab01d8cd596d1d14cc9b62f1ecaad22c5e60296ebc61417675b3220bdd7de6f84bad3c3b283866545c8
7
+ data.tar.gz: 90303f06c9d494dea39ad881ecfdf3880960c62d8b6868c1f3783941a6804f738de17ba91a82b63e6de02152cca579eeb287439d12b91ba31f63e8fd8278ae18
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.3
1
+ 0.3.0
@@ -0,0 +1,4 @@
1
+ require 'docker-sync/config/config_locator'
2
+ require 'docker-sync/config/global_config'
3
+ require 'docker-sync/config/project_config'
4
+ require 'docker-sync/preconditions/strategy'
@@ -0,0 +1,80 @@
1
+ require 'pathname'
2
+
3
+ module DockerSync
4
+ # helps us loading our config files, GlobalConfig and ProjectConfig
5
+ module ConfigLocator
6
+ ERROR_MISSING_PROJECT_CONFIG =
7
+ 'No docker-sync.yml configuration found in your path ( traversing up ) '\
8
+ 'Did you define it for your project?'.freeze
9
+
10
+ class << self
11
+ attr_accessor :global_config_path
12
+ # @return [String] The path to the global config location
13
+ def current_global_config_path
14
+ path = global_config_path
15
+ path = File.expand_path('~/.docker-sync-global.yml') if path.nil?
16
+ path
17
+ end
18
+
19
+ # @return [String] the path to the project configuration found
20
+ def lookup_project_config_path
21
+ files = project_config_find
22
+
23
+ raise ERROR_MISSING_PROJECT_CONFIG if files.empty?
24
+
25
+ files.pop
26
+ end
27
+
28
+ private
29
+
30
+
31
+ # this has been ruthlessly stolen from Thor/runner.rb - please do not hunt me for that :)
32
+ # returns a list of file paths matching the docker-sync.yml file. The return the first one we find while traversing
33
+ # the folder tree up
34
+ # @return [Array]
35
+ def project_config_find(skip_lookup = false)
36
+ # Finds docker-sync.yml by traversing from your current directory down to the root
37
+ # directory of your system. If at any time we find a docker-sync.yml file, we stop.
38
+ #
39
+ #
40
+ # ==== Example
41
+ #
42
+ # If we start at /Users/wycats/dev/thor ...
43
+ #
44
+ # 1. /Users/wycats/dev/thor
45
+ # 2. /Users/wycats/dev
46
+ # 3. /Users/wycats <-- we find a docker-sync.yml here, so we stop
47
+ #
48
+ # Suppose we start at c:\Documents and Settings\james\dev\docker-sync ...
49
+ #
50
+ # 1. c:\Documents and Settings\james\dev\docker-sync.yml
51
+ # 2. c:\Documents and Settings\james\dev
52
+ # 3. c:\Documents and Settings\james
53
+ # 4. c:\Documents and Settings
54
+ # 5. c:\ <-- no docker-sync.yml found!
55
+ #
56
+ docker_sync_files = []
57
+
58
+ unless skip_lookup
59
+ Pathname.pwd.ascend do |path|
60
+ docker_sync_files = globs_for_project_config(path).map { |g| Dir[g] }.flatten
61
+ break unless docker_sync_files.empty?
62
+ end
63
+ end
64
+
65
+ docker_sync_files
66
+ end
67
+
68
+ # Where to look for docker-sync.yml files.
69
+ #
70
+ def globs_for_project_config(path)
71
+ path = escape_globs(path)
72
+ ["#{path}/docker-sync.yml"]
73
+ end
74
+
75
+ def escape_globs(path)
76
+ path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&')
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,56 @@
1
+ require 'yaml'
2
+ require 'dotenv'
3
+
4
+ module DockerSync
5
+ module ConfigSerializer
6
+ class << self
7
+ # @param [String] config_path path to the yaml configuration to load
8
+ # @return [Object] returns a Yaml hashmap, expaneded by ENV vars
9
+ def default_deserializer_file(config_path)
10
+ config_string = File.read(config_path)
11
+ default_deserializer_string(config_string)
12
+ end
13
+
14
+ # @param [String] config_string the configuration string inf yaml format
15
+ # @return [Object] a yaml hashmap
16
+ def default_deserializer_string(config_string)
17
+ deserialize_config( expand_env_variables(config_string) )
18
+ end
19
+
20
+ private
21
+
22
+ # Replaces our tokens, in this case all ENV variables we defined. Find those in the string an replace
23
+ # them with then values from our ENV, including the dotenv file
24
+ # @param [String] config_string
25
+ # @return [String]
26
+ def expand_env_variables(config_string)
27
+ load_dotenv
28
+
29
+ env_hash = {}
30
+ ENV.each {|k,v| env_hash[k.to_sym] = v }
31
+ config_string.gsub!('${', '%{')
32
+ config_string % env_hash
33
+ end
34
+
35
+
36
+ # deserializes the configuration string, right now as a yaml formatted string
37
+ # @param [String] config_string
38
+ # @return [Object]
39
+ def deserialize_config(config_string)
40
+ # noinspection RubyResolve
41
+ YAML.load(config_string)
42
+ end
43
+
44
+ # Loads the dotenv file but also lets us overide the source not being .env but anything you put
45
+ # into the ENV variable DOCKER_SYNC_ENV_FILE
46
+ # @return [Object]
47
+ def load_dotenv
48
+ # TODO: ensure we do this once only
49
+ env_file = ENV.fetch('DOCKER_SYNC_ENV_FILE', '.env')
50
+
51
+ # noinspection RubyResolve
52
+ Dotenv.load(env_file)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,55 @@
1
+ require 'singleton'
2
+ require 'forwardable'
3
+
4
+ require 'docker-sync/config/config_locator'
5
+ require 'docker-sync/config/config_serializer'
6
+
7
+ module DockerSync
8
+ class GlobalConfig
9
+ extend Forwardable
10
+ include Singleton
11
+
12
+ # noinspection RubyStringKeysInHashInspection
13
+ DEFAULTS = {
14
+ 'update_check' => true,
15
+ 'update_last_check' => DateTime.new(2001, 1, 1).iso8601(9),
16
+ 'update_enforce' => true,
17
+ 'upgrade_status' => '',
18
+ }.freeze
19
+
20
+ attr_reader :config
21
+ private :config
22
+
23
+ def_delegators :@config, :[], :to_h
24
+
25
+ def self.load; instance end
26
+
27
+ def initialize
28
+ load_global_config
29
+ end
30
+
31
+ def load_global_config
32
+ @config_path = DockerSync::ConfigLocator.current_global_config_path
33
+ if File.exist?(@config_path)
34
+ @config = DockerSync::ConfigSerializer.default_deserializer_file(@config_path)
35
+ end
36
+
37
+ unless @config
38
+ @config = DEFAULTS.dup
39
+ @first_run = true
40
+ end
41
+ end
42
+
43
+ def first_run?
44
+ @first_run
45
+ end
46
+
47
+ # @param [Object] updates
48
+ # Updates and saves the configuration back to the file
49
+ def update!(updates)
50
+ @config.merge!(updates)
51
+
52
+ File.open(@config_path, 'w') {|f| f.write @config.to_yaml }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,123 @@
1
+ require 'singleton'
2
+ require 'docker-sync/config/config_locator'
3
+ require 'docker-sync/config/config_serializer'
4
+
5
+ module DockerSync
6
+ class ProjectConfig
7
+ extend Forwardable
8
+
9
+ REQUIRED_CONFIG_VERSION = '2'.freeze
10
+
11
+ ERROR_MISSING_CONFIG_VERSION =
12
+ "Your docker-sync.yml file does not include a version: \"#{REQUIRED_CONFIG_VERSION}\""\
13
+ '(Add this if you migrated from docker-sync 0.1.x)'.freeze
14
+
15
+ ERROR_MISMATCH_CONFIG_VERSION =
16
+ 'Your docker-sync.yml file does not match the required version '\
17
+ "(#{REQUIRED_CONFIG_VERSION}).".freeze
18
+
19
+ ERROR_MISSING_SYNCS = 'no syncs defined'.freeze
20
+
21
+ attr_reader :config, :config_path
22
+ private :config
23
+
24
+ def_delegators :@config, :[], :to_h
25
+
26
+ def initialize(config_path: nil, config_string: nil)
27
+ if config_string.nil?
28
+ config_path = DockerSync::ConfigLocator.lookup_project_config_path if config_path.nil?
29
+ load_project_config(config_path)
30
+ else
31
+ @config = DockerSync::ConfigSerializer.default_deserializer_string(config_string)
32
+ @config_path = nil
33
+ end
34
+
35
+ validate_config!
36
+ normalize_config!
37
+ end
38
+
39
+ def load_project_config(config_path = nil)
40
+ @config_path = config_path
41
+ return unless File.exist?(@config_path)
42
+ @config = DockerSync::ConfigSerializer.default_deserializer_file(@config_path)
43
+ end
44
+
45
+ def unison_required?
46
+ config['syncs'].any? { |name, sync_config|
47
+ sync_config['sync_strategy'] == 'unison' || sync_config['watch_strategy'] == 'unison'
48
+ }
49
+ end
50
+
51
+ def rsync_required?
52
+ config['syncs'].any? { |name, sync_config|
53
+ sync_config['sync_strategy'] == 'rsync'
54
+ }
55
+ end
56
+
57
+
58
+ private
59
+
60
+ def validate_config!
61
+ raise error_missing_given_config if config.nil?
62
+ raise ERROR_MISSING_CONFIG_VERSION unless config.key?('version')
63
+ raise ERROR_MISMATCH_CONFIG_VERSION unless config['version'].to_s == REQUIRED_CONFIG_VERSION
64
+ raise ERROR_MISSING_SYNCS unless config.key?('syncs')
65
+
66
+ validate_syncs_config!
67
+ end
68
+
69
+ def validate_syncs_config!
70
+ config['syncs'].each do |name, sync_config|
71
+ validate_sync_config(name, sync_config)
72
+ end
73
+ end
74
+
75
+ def validate_sync_config(name, sync_config)
76
+ config_mandatory = %w[src]
77
+ #TODO: Implement autodisovery for other strategies
78
+ config_mandatory.push('sync_host_port') if sync_config['sync_strategy'] == 'rsync'
79
+ config_mandatory.each do |key|
80
+ raise ("#{name} does not have #{key} configuration value set - this is mandatory") unless sync_config.key?(key)
81
+ end
82
+ end
83
+
84
+ def error_missing_given_config
85
+ "Config could not be loaded from #{config_path} - it does not exist"
86
+ end
87
+
88
+ def normalize_config!
89
+ config['syncs'].each do |name, sync_config|
90
+ config['syncs'][name] = normalize_sync_config(sync_config)
91
+ end
92
+ end
93
+
94
+ def normalize_sync_config(sync_config)
95
+ {
96
+ 'sync_strategy' => sync_strategy_for(sync_config),
97
+ 'watch_strategy' => watch_strategy_for(sync_config)
98
+ }.merge(sync_config)
99
+ end
100
+
101
+ def sync_strategy_for(sync_config)
102
+ case sync_config['sync_strategy']
103
+ when 'rsync' then 'rsync'
104
+ else 'unison'
105
+ end
106
+ end
107
+
108
+ def watch_strategy_for(sync_config)
109
+ if sync_config.key?('watch_strategy')
110
+ case sync_config['watch_strategy']
111
+ when 'fswatch' then 'fswatch'
112
+ when 'disable','dummy' then 'dummy'
113
+ else 'unison'
114
+ end
115
+ elsif sync_config['sync_strategy'] == 'rsync'
116
+ 'fswatch'
117
+ else
118
+ 'unison'
119
+ end
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,24 @@
1
+ module DockerSync
2
+ module Preconditions
3
+ class Linux
4
+ def check_all_preconditions(config)
5
+ end
6
+
7
+ def docker_available
8
+ end
9
+
10
+ def docker_running
11
+ end
12
+
13
+ def fswatch_available
14
+ end
15
+
16
+ def rsync_available
17
+ end
18
+
19
+ def unison_available
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,148 @@
1
+ require 'mkmf'
2
+ module DockerSync
3
+ module Preconditions
4
+ class Osx
5
+ def check_all_preconditions(config)
6
+ return unless should_run_precondition?
7
+
8
+ docker_available
9
+ docker_running
10
+
11
+ if config.unison_required?
12
+ unison_available
13
+ end
14
+
15
+ if config.rsync_required?
16
+ rsync_available
17
+ fswatch_available
18
+ end
19
+ end
20
+
21
+ def docker_available
22
+ if (find_executable0 'docker').nil?
23
+ raise('Could not find docker binary in path. Please install it, e.g. using "brew install docker" or install docker-for-mac')
24
+ end
25
+ end
26
+
27
+ def docker_running
28
+ `docker ps`
29
+ if $?.exitstatus > 0
30
+ raise('No docker daemon seems to be running. Did you start your docker-for-mac / docker-machine?')
31
+ end
32
+ end
33
+
34
+ def rsync_available
35
+ if should_run_precondition?
36
+ if (find_executable0 'rsync').nil?
37
+ raise('Could not find rsync binary in path. Please install it, e.g. using "brew install rsync"')
38
+ end
39
+ end
40
+ end
41
+
42
+ def unison_available
43
+ if should_run_precondition?
44
+ if (find_executable0 'unison').nil?
45
+ cmd1 = 'brew install unison"'
46
+
47
+ Thor::Shell::Basic.new.say_status 'warning', 'Could not find unison binary in $PATH. Trying to install now', :red
48
+ if Thor::Shell::Basic.new.yes?('I will install unison using brew for you? (y/N)')
49
+ system cmd1
50
+ else
51
+ raise('Please install it yourself using: brew install unison')
52
+ end
53
+ end
54
+
55
+ unox_available
56
+ end
57
+ end
58
+
59
+ def fswatch_available
60
+ if should_run_precondition?
61
+ if (find_executable0 'fswatch').nil?
62
+ cmd1 = 'brew install fswatch"'
63
+
64
+ Thor::Shell::Basic.new.say_status 'warning', 'No fswatch available. Install it by "brew install fswatch Trying to install now', :red
65
+ if Thor::Shell::Basic.new.yes?('I will install fswatch using brew for you? (y/N)')
66
+ system cmd1
67
+ else
68
+ raise('Please install it yourself using: brew install fswatch')
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ private
76
+
77
+ def should_run_precondition?(silent = false)
78
+ unless has_brew?
79
+ Thor::Shell::Basic.new.say_status 'inf', 'Not running any precondition checks since you have no brew and that is unsupported. Is all up to you know.', :white unless silent
80
+ return false
81
+ end
82
+ return true
83
+ end
84
+
85
+ def has_brew?
86
+ return find_executable0 'brew'
87
+ end
88
+
89
+
90
+ def unox_available
91
+ if should_run_precondition?
92
+ `brew list unox`
93
+ if $?.exitstatus > 0
94
+ unless (find_executable0 'unison-fsmonitor').nil?
95
+ # unox installed, but not using brew, we do not allow that anymore
96
+ Thor::Shell::Basic.new.say_status 'error', 'You install unison-fsmonitor (unox) not using brew. Please uninstall it and run docker-sync again, so we can install it for you', :red
97
+ exit 1
98
+ end
99
+ cmd1 = 'brew tap eugenmayer/dockersync && brew install eugenmayer/dockersync/unox'
100
+
101
+ Thor::Shell::Basic.new.say_status 'warning', 'Could not find unison-fsmonitor (unox) binary in $PATH. Trying to install now', :red
102
+ if Thor::Shell::Basic.new.yes?('I will install unox through brew for you? (y/N)')
103
+ system cmd1
104
+ else
105
+ raise('Please install it yourself using: brew tap eugenmayer/dockersync && brew install unox')
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ def install_pip(package, test = nil)
112
+ test ? `python -c 'import #{test}'` : `python -c 'import #{package}'`
113
+
114
+ unless $?.success?
115
+ Thor::Shell::Basic.new.say_status 'warning', "Could not find #{package}. Will try to install it using pip", :red
116
+ if find_executable0('python') == '/usr/bin/python'
117
+ Thor::Shell::Basic.new.say_status 'ok', 'You seem to use the system python, we will need sudo below'
118
+ sudo = true
119
+ cmd2 = "sudo easy_install pip && sudo pip install #{package}"
120
+ else
121
+ Thor::Shell::Basic.new.say_status 'ok', 'You seem to have a custom python, using non-sudo commands'
122
+ sudo = false
123
+ cmd2 = "easy_install pip && pip install #{package}"
124
+ end
125
+ if sudo
126
+ question = "I will ask you for you root password to install #{package} by running (This will ask for sudo, since we use the system python)"
127
+ else
128
+ question = "I will now install #{package} for you by running"
129
+ end
130
+
131
+ Thor::Shell::Basic.new.say_status 'info', "#{question}: `#{cmd2}\n\n"
132
+ if Thor::Shell::Basic.new.yes?('Shall I continue? (y/N)')
133
+ system cmd2
134
+ if $?.exitstatus > 0
135
+ raise("Failed to install #{package}, please file an issue with the output of the error")
136
+ end
137
+ test ? `python -c 'import #{test}'` : `python -c 'import #{package}'`
138
+ unless $?.success?
139
+ raise("Somehow I could not successfully install #{package} even though I tried. Please report this issue.")
140
+ end
141
+ else
142
+ raise("Please install #{package} manually, see https://github.com/EugenMayer/docker-sync/wiki/1.-Installation")
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end