aggkit 0.3.1.8768 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/aggkit/ci.rb CHANGED
@@ -9,75 +9,76 @@ module Aggkit
9
9
 
10
10
  module Ci
11
11
 
12
- def log_action action
12
+ def log_action(action)
13
13
  puts " => #{action}"
14
14
  end
15
15
 
16
- def info message
16
+ def info(message)
17
17
  puts " => #{message}"
18
18
  end
19
19
 
20
- def execute cmd
20
+ def execute(cmd)
21
21
  puts " - executing: #{cmd}"
22
22
  result = `#{cmd}`
23
23
  puts result
24
- if !$?.success?
25
- raise "Execution failed cmd: #{cmd.inspect}"
26
- end
24
+ raise "Execution failed cmd: #{cmd.inspect}" unless $?.success?
25
+
27
26
  result
28
27
  end
29
28
 
30
- #Уровни образов
29
+ # Уровни образов
31
30
  # 1. Без модификатора - не протестирован, отсутсвует в репозитории.
32
31
  # 2. С модификатором dev - протестирован автотестами, ДОЛЖЕН быть в репозитории.
33
32
  # 3. С модификатором stage - протестирован руками, ДОЛЖЕН быть в репозитории(как и его предшественник dev).
34
33
  # 4. С модификатором release - помечен для релиза, ДОЛЖЕН быть в репозитории(как и его предшественник dev и stage).
35
34
 
36
- #Работа должна вестить ТОЛЬКО с workimage
35
+ # Работа должна вестить ТОЛЬКО с workimage
37
36
 
38
37
  # Базовое имя образа
39
38
  # Например docker.rnds.pro/aggredator-mq
40
- def baseimage service
39
+ def baseimage(service)
41
40
  raise "Service name invalid: #{service.inspect}" if service.to_s.length < 2
41
+
42
42
  "docker.rnds.pro/aggredator-#{service}"
43
43
  end
44
44
 
45
-
46
45
  # Текущее имя образа без модификаторов продвижения, но с тегом
47
46
  # Например docker.rnds.pro/aggredator-mq:7e6420b5
48
- def workimage service, tag
47
+ def workimage(service, tag)
49
48
  raise "Service tag invalid: #{tag.inspect}" if tag.to_s.length < 2
49
+
50
50
  "#{baseimage(service)}:#{tag}"
51
51
  end
52
52
 
53
53
  # Образ с модификатором версии
54
54
  # Например docker.rnds.pro/aggredator-mq-dev:7e6420b5
55
- def promotedimage service, stage, tag
55
+ def promotedimage(service, stage, tag)
56
56
  raise "Service stage invalid: #{stage.inspect}" if stage.to_s.length < 2
57
57
  raise "Service tag invalid: #{tag.inspect}" if tag.to_s.length < 2
58
+
58
59
  "#{baseimage(service)}-#{stage}:#{tag}"
59
60
  end
60
61
 
61
62
  # Образ с модификатором протестированной автотестами версии
62
63
  # Например docker.rnds.pro/aggredator-mq-dev:7e6420b5
63
- def devimage service, tag
64
+ def devimage(service, tag)
64
65
  promotedimage(service, 'dev', tag)
65
66
  end
66
67
 
67
68
  # Образ с модификатором стабильной версии
68
69
  # Например docker.rnds.pro/aggredator-mq-stage:7e6420b5
69
- def stageimage service, tag
70
+ def stageimage(service, tag)
70
71
  promotedimage(service, 'stage', tag)
71
72
  end
72
73
 
73
74
  # Образ с модификатором релизной версии
74
- #Например docker.rnds.pro/aggredator-mq-release:7e6420b5
75
- def releaseimage service, tag
75
+ # Например docker.rnds.pro/aggredator-mq-release:7e6420b5
76
+ def releaseimage(service, tag)
76
77
  promotedimage(service, 'release', tag)
77
78
  end
78
79
 
79
- # Протегировать образ тегом
80
- def tagimage! image, newimage, push: false, ignoreerror: false
80
+ # Протегировать образ тегом
81
+ def tagimage!(image, newimage, push: false, ignoreerror: false)
81
82
  log_action "Tag #{image.inspect} as #{newimage.inspect}"
82
83
 
83
84
  if ignoreerror
@@ -86,28 +87,26 @@ module Aggkit
86
87
  execute("docker tag #{image} #{newimage}")
87
88
  end
88
89
 
89
- if push
90
- execute("docker push #{newimage}")
91
- end
90
+ execute("docker push #{newimage}") if push
92
91
 
93
- return newimage
92
+ newimage
94
93
  end
95
94
 
96
- #Удалить образ образ тегом
97
- def rmimage! image
95
+ # Удалить образ образ тегом
96
+ def rmimage!(image)
98
97
  log_action "Remove #{image.inspect}"
99
98
  execute("docker rmi #{image} &> /dev/null || true")
100
99
  end
101
100
 
102
- def label! image, label, value
101
+ def label!(image, label, value)
103
102
  raise "Image label invalid: #{label.inspect}" if label.to_s.length < 2
104
103
  raise "Image label value invalid: #{value.inspect}" if value.to_s.length < 2
105
104
 
106
- execute("echo #{image} | docker build -t #{image} --label=\"#{label.to_s}=#{value.to_s}\" - ")
105
+ execute("echo #{image} | docker build -t #{image} --label=\"#{label}=#{value}\" - ")
107
106
  end
108
107
 
109
108
  # Растегировать все модификатора образа сервиса и возможно удалить его
110
- def untagservice service, tag
109
+ def untagservice(service, tag)
111
110
  if tag != 'latest'
112
111
  rmimage! workimage(service, tag)
113
112
  rmimage! devimage(service, tag)
@@ -117,7 +116,7 @@ module Aggkit
117
116
  end
118
117
 
119
118
  # Продвинуть текущий образ сервиса по уровню - добавить модификатор и залить в репозиторий
120
- def promote! service, tag, stage, fromstage: nil
119
+ def promote!(service, tag, stage, fromstage: nil)
121
120
  fromimage = if fromstage
122
121
  promotedimage(service, fromstage, tag)
123
122
  else
@@ -127,52 +126,50 @@ module Aggkit
127
126
  toimage = promotedimage(service, stage, tag)
128
127
 
129
128
  log_action "Promote #{fromimage} to #{toimage}"
130
- return tagimage! fromimage, toimage, push: true
129
+ tagimage! fromimage, toimage, push: true
131
130
  end
132
131
 
133
132
  # Продвинуть текущий образ по уровню - добавить модификатор dev(протестировано) и залить в репозиторий
134
- def promote_to_dev! service, tag
133
+ def promote_to_dev!(service, tag)
135
134
  promote!(service, tag, 'dev')
136
135
  end
137
136
 
138
137
  # Продвинуть текущий образ по уровню - добавить модификатор stage(стабильно) и залить в репозиторий
139
- def promote_to_stage! service, tag
138
+ def promote_to_stage!(service, tag)
140
139
  promoted = tagimage!(devimage(service, tag), stageimage(service, tag), push: true)
141
140
  # Тегируем этот образ как latest
142
- return tagimage!(promoted, stageimage(service, 'latest'), push: true)
141
+ tagimage!(promoted, stageimage(service, 'latest'), push: true)
143
142
  end
144
143
 
145
144
  # Продвинуть стабильный образ до указанного тега
146
- def promote_to_tag! service, tag, newtag
147
- return tagimage!(devimage(service, tag), stageimage(service, newtag), push: true)
145
+ def promote_to_tag!(service, tag, newtag)
146
+ tagimage!(devimage(service, tag), stageimage(service, newtag), push: true)
148
147
  end
149
148
 
150
149
  # Продвинуть список образов или упасть с ошибкой
151
- def promote_services_to_dev! services, tag
150
+ def promote_services_to_dev!(services, tag)
152
151
  [services].flatten.each do |service|
153
152
  promote_to_dev!(service, tag)
154
153
  end
155
154
  end
156
155
 
157
156
  # Продвинуть список образов или упасть с ошибкой
158
- def promote_services_to_stage! services, tag
157
+ def promote_services_to_stage!(services, tag)
159
158
  [services].flatten.each do |service|
160
159
  promote_to_stage!(service, tag)
161
160
  end
162
161
  end
163
162
 
164
- #Протегировать образы до указанного тега
165
- def promote_services_to_tag! services, tag, newtag
163
+ # Протегировать образы до указанного тега
164
+ def promote_services_to_tag!(services, tag, newtag)
166
165
  [services].flatten.each do |service|
167
166
  promote_to_tag!(service, tag, newtag)
168
167
  end
169
168
  end
170
169
 
171
-
172
-
173
170
  # Попытаться взять все модификаторы образов из репозитория
174
171
  # Скачивает образы и тегирует их как workimage
175
- def pull_images_cache services, tag
172
+ def pull_images_cache(services, tag)
176
173
  log_action "Puling images#{[services].flatten.inspect} from cache...."
177
174
  [services].flatten.each do |service|
178
175
  execute("docker pull #{workimage(service, tag)} 2> /dev/null || true")
@@ -186,15 +183,15 @@ module Aggkit
186
183
  end
187
184
 
188
185
  # Залить образы в репозиторий
189
- def push_images_cache services, tag
186
+ def push_images_cache(services, tag)
190
187
  log_action "Pushing images#{[services].flatten.inspect} to cache...."
191
188
  [services].flatten.each do |service|
192
189
  tagimage! workimage(service, tag), workimage(service, tag), push: true
193
190
  end
194
191
  end
195
192
 
196
- #Растегировать все образы для дальнейшего удаления. Помечает как latest для поддержки кеширования docker
197
- def clean_services services, tag
193
+ # Растегировать все образы для дальнейшего удаления. Помечает как latest для поддержки кеширования docker
194
+ def clean_services(services, tag)
198
195
  [services].flatten.each do |service|
199
196
  tagimage! workimage(service, tag), workimage(service, 'latest'), ignoreerror: true
200
197
  tagimage! devimage(service, tag), workimage(service, 'latest'), ignoreerror: true
@@ -204,17 +201,17 @@ module Aggkit
204
201
  end
205
202
  end
206
203
 
207
- def ssh_deploy services, env, sshserver, tag, stage
208
- with_remote_docker services, env, sshserver, tag, stage do |config, environment|
204
+ def ssh_deploy(services, env, sshserver, tag, stage)
205
+ with_remote_docker services, env, sshserver, tag, stage do |config, _environment|
209
206
  config_file = config['config_file'].to_s
210
207
 
211
208
  execute('docker-compose ps')
212
209
  execute('docker-compose pull')
213
210
 
214
- consul = execute('docker ps | grep consul').split(' ').first.strip
211
+ consul = execute('docker-compose ps | grep consul').split(' ').first.strip
215
212
  puts "CONSULT ID: #{consul}"
216
213
 
217
- if !config_file.empty?
214
+ unless config_file.empty?
218
215
  execute("docker exec -i #{consul} aggwait -t 20 --consul")
219
216
  execute("docker exec -i #{consul} aggconsul --init --override --config - < #{config_file}")
220
217
  end
@@ -223,38 +220,38 @@ module Aggkit
223
220
  end
224
221
  end
225
222
 
226
- def ssh_core_deploy services, env, sshserver, tag, stage
227
- with_remote_docker services, env, sshserver, tag, stage do |config, environment|
223
+ def ssh_core_deploy(services, env, sshserver, tag, stage)
224
+ with_remote_docker services, env, sshserver, tag, stage do |config, _environment|
228
225
  config_file = config['config_file'].to_s
229
226
 
230
227
  execute('docker-compose ps')
231
228
  execute('docker-compose pull')
232
229
  execute('docker-compose up -t 30 -d consul')
233
230
 
234
- consul = execute('docker ps | grep consul').split(' ').first.strip
231
+ consul = execute('docker-compose ps | grep consul').split(' ').first.strip
235
232
  puts "CONSULT ID: #{consul}"
236
233
 
237
- if !config_file.empty?
234
+ unless config_file.empty?
238
235
  execute("docker exec -i #{consul} aggwait -t 20 --consul")
239
236
  execute("docker exec -i #{consul} aggconsul --init --override --config - < #{config_file}")
240
237
  end
241
238
 
242
- #echo " * Consul ready"
243
- #echo " * Updatting database..."
239
+ # echo " * Consul ready"
240
+ # echo " * Updatting database..."
244
241
  execute('docker-compose up -t 30 -d db')
245
242
  sleep 5
246
- #echo " * Db ready"
247
- #echo " * Bootstrapping admin..."
243
+ # echo " * Db ready"
244
+ # echo " * Bootstrapping admin..."
248
245
  execute('docker-compose run --rm -T admin /home/app/docker/bootstrap.rb')
249
246
  sleep 5
250
- #echo " * Bootstrapping complete"
251
- #echo " * Updating all containers..."
247
+ # echo " * Bootstrapping complete"
248
+ # echo " * Updating all containers..."
252
249
  execute('docker-compose up -t 30 -d')
253
- #echo " * Containers ready"
250
+ # echo " * Containers ready"
254
251
  end
255
252
  end
256
253
 
257
- def with_remote_docker services, env, sshserver, tag, stage
254
+ def with_remote_docker(services, env, sshserver, tag, stage)
258
255
  services = [services].flatten
259
256
  log_action "Deploy [#{services.inspect}] tag:#{tag.inspect} stage:#{stage.inspect} to #{sshserver.inspect}"
260
257
  with_ssh sshserver do |sockname|
@@ -265,14 +262,12 @@ module Aggkit
265
262
  envs['CORE_TAG'] = tag
266
263
  envs['CORE_SUFFIX'] = "-#{stage}"
267
264
 
268
- localid = execute("docker info | grep ID").strip
265
+ localid = execute('docker info | grep ID').strip
269
266
 
270
267
  Aggkit::Env.with_env(envs) do |environment|
271
- remoteid = execute("docker info | grep ID").strip
268
+ remoteid = execute('docker info | grep ID').strip
272
269
 
273
- if localid == remoteid
274
- raise "Can't get access to remote Docker"
275
- end
270
+ raise "Can't get access to remote Docker" if localid == remoteid
276
271
 
277
272
  yield(env_config, environment)
278
273
 
@@ -282,12 +277,11 @@ module Aggkit
282
277
  tagimage! deployed, current
283
278
  untagservice(service, tag)
284
279
  end
285
-
286
280
  end
287
281
  end
288
282
  end
289
283
 
290
- def with_ssh server, user: 'root', &block
284
+ def with_ssh(server, user: 'root')
291
285
  file = Tempfile.new
292
286
  @ssh_sockname = file.path
293
287
  file.unlink
@@ -304,3 +298,4 @@ module Aggkit
304
298
  end
305
299
 
306
300
  end
301
+
data/lib/aggkit/env.rb CHANGED
@@ -4,6 +4,7 @@ require 'English'
4
4
  require 'dotenv'
5
5
 
6
6
  module Dotenv
7
+
7
8
  module_function
8
9
 
9
10
  def load_without_export(*filenames)
@@ -13,19 +14,21 @@ module Dotenv
13
14
  end
14
15
  end
15
16
  end
17
+
16
18
  end
17
19
 
18
20
  module Aggkit
19
21
 
20
22
  class Env
23
+
21
24
  attr_accessor :project, :name, :env_root, :environment
22
25
 
23
- def self.list path = Dir.pwd
26
+ def self.list(path = Dir.pwd)
24
27
  root = Project.new(path).project_root
25
28
  Pathfinder.new(root).each_env
26
29
  end
27
30
 
28
- def self.with_env env, &block
31
+ def self.with_env(env)
29
32
  current_env = ENV.to_h
30
33
  env.each_pair do |k, v|
31
34
  ENV[k.to_s] = v.to_s
@@ -41,7 +44,7 @@ module Aggkit
41
44
  end
42
45
  end
43
46
 
44
- def initialize path
47
+ def initialize(path)
45
48
  @project = if File.directory?(File.expand_path(path))
46
49
  @name = File.basename(File.realpath(File.expand_path(path)))
47
50
  Project.new(path)
@@ -50,19 +53,19 @@ module Aggkit
50
53
  Project.new(Dir.pwd)
51
54
  end
52
55
 
53
- if !self.class.list.include?(@name)
56
+ unless self.class.list.include?(@name)
54
57
  raise "There no env #{@name.inspect} in project: #{project.project_root.inspect}"
55
58
  end
56
59
 
57
60
  @env_root = File.join(project_root, 'envs', @name)
58
61
  end
59
62
 
60
- def prepare release: false
63
+ def prepare(release: false)
61
64
  @environment = prepare_environment(release: release)
62
65
  end
63
66
 
64
- def with_env &block
65
- Env.with_env(environment) &block
67
+ def with_env(&block)
68
+ Env.with_env(environment) & block
66
69
  end
67
70
 
68
71
  def project_root
@@ -82,148 +85,161 @@ module Aggkit
82
85
  end
83
86
 
84
87
  def core_dot_file
85
- File.join(core_root, '.env') rescue nil
88
+ begin
89
+ File.join(core_root, '.env')
90
+ rescue StandardError
91
+ nil
92
+ end
86
93
  end
87
94
 
88
95
  def env_files
89
- [core_dot_file, project_dot_file, dot_file].compact.select{|f| File.exists?(f)}.uniq
96
+ [core_dot_file, project_dot_file, dot_file].compact.select{|f| File.exist?(f) }.uniq
90
97
  end
91
98
 
92
99
  def config_file
93
- [File.join(env_root, 'config.yml'), File.join(env_root, 'config.yaml')].compact.select{|f| File.exists?(f)}.uniq.first
100
+ [File.join(env_root, 'config.yml'), File.join(env_root, 'config.yaml')].compact.select{|f| File.exist?(f) }.uniq.first
94
101
  end
95
102
 
96
103
  def show
97
104
  {
98
- name: name,
105
+ name: name,
99
106
  project_root: project_root,
100
- core_root: core_root,
101
- envfiles: env_files,
102
- config_file: config_file,
103
- envs: environment,
107
+ core_root: core_root,
108
+ envfiles: env_files,
109
+ config_file: config_file,
110
+ envs: environment
104
111
  }
105
112
  end
106
113
 
107
- private
108
-
114
+ private
109
115
 
110
- def prepare_environment release: false
111
- base_env = {
112
- 'ENV_ROOT' => env_root,
113
- 'PROJECT_ROOT' => project_root,
114
- 'CORE_ROOT' => core_root,
115
- 'PATH' => check_path_variable(env_root),
116
- }
117
116
 
118
- dot_env = prepare_dot(base_env)
117
+ def prepare_environment(release: false)
118
+ base_env = {
119
+ 'ENV_ROOT' => env_root,
120
+ 'PROJECT_ROOT' => project_root,
121
+ 'CORE_ROOT' => core_root,
122
+ 'PATH' => check_path_variable(env_root)
123
+ }
119
124
 
120
- compose_env = prepare_compose(dot_env.merge(base_env), release: release)
125
+ dot_env = prepare_dot(base_env)
121
126
 
122
- dot_env.merge(base_env).merge(compose_env)
123
- end
127
+ compose_env = prepare_compose(dot_env.merge(base_env), release: release)
124
128
 
125
- def prepare_dot env
126
- Env.with_env(env) do
127
- Dotenv.load_without_export(*env_files)
129
+ dot_env.merge(base_env).merge(compose_env)
128
130
  end
129
- end
130
131
 
131
- def prepare_compose env, release: false
132
- compose_files = if env['COMPOSE_FILE']
133
- env['COMPOSE_FILE']
134
- elsif File.exists?(File.join(env_root, "docker-compose.yml"))
135
- File.join(env_root, "docker-compose.yml")
136
- elsif File.exists?(File.join(env_root, "docker-compose.yaml"))
137
- File.join(env_root, "docker-compose.yaml")
132
+ def prepare_dot(env)
133
+ Env.with_env(env) do
134
+ Dotenv.load_without_export(*env_files)
135
+ end
138
136
  end
139
137
 
140
- if compose_files
141
- result_file = File.join(env_root, "compose-result.yml")
142
- Env.with_env(env.merge('COMPOSE_FILE' => compose_files)) do
143
- if release
144
- `aggmerge #{result_file} --skip-build`
145
- else
146
- `aggmerge #{result_file}`
138
+ def prepare_compose(env, release: false)
139
+ compose_files = if env['COMPOSE_FILE']
140
+ env['COMPOSE_FILE']
141
+ elsif File.exist?(File.join(env_root, 'docker-compose.yml'))
142
+ File.join(env_root, 'docker-compose.yml')
143
+ elsif File.exist?(File.join(env_root, 'docker-compose.yaml'))
144
+ File.join(env_root, 'docker-compose.yaml')
145
+ end
146
+
147
+ if compose_files
148
+ result_file = File.join(env_root, 'compose-result.yml')
149
+ Env.with_env(env.merge('COMPOSE_FILE' => compose_files)) do
150
+ if release
151
+ `aggmerge #{result_file} --skip-build`
152
+ else
153
+ `aggmerge #{result_file}`
154
+ end
147
155
  end
156
+ {
157
+ 'COMPOSE_FILE' => result_file
158
+ }
159
+ else
160
+ {}
148
161
  end
149
- {
150
- 'COMPOSE_FILE' => result_file
151
- }
152
- else
153
- {}
154
162
  end
155
- end
156
163
 
157
- def check_path_variable path
158
- if ENV['PATH'][path]
159
- ENV['PATH']
160
- else
161
- "#{ENV['PATH']}:#{path}"
164
+ def check_path_variable(path)
165
+ if ENV['PATH'][path]
166
+ ENV['PATH']
167
+ else
168
+ "#{ENV['PATH']}:#{path}"
169
+ end
162
170
  end
163
- end
164
171
 
165
- class Pathfinder
166
- attr_accessor :path
172
+ class Pathfinder
167
173
 
168
- def initialize path
169
- @path = File.realpath(File.expand_path(path))
170
- end
171
174
 
172
- def each_parent
173
- current = path.split(File::SEPARATOR)
174
- Enumerator.new do |y|
175
- while !current.empty? do
176
- y << current.join(File::SEPARATOR)
177
- current.pop
175
+ attr_accessor :path
176
+
177
+ def initialize(path)
178
+ @path = File.realpath(File.expand_path(path))
179
+ end
180
+
181
+ def each_parent
182
+ current = path.split(File::SEPARATOR)
183
+ Enumerator.new do |y|
184
+ until current.empty?
185
+ y << current.join(File::SEPARATOR)
186
+ current.pop
187
+ end
178
188
  end
179
189
  end
180
- end
181
190
 
182
- def each_env
183
- Dir.chdir(File.join(path, 'envs')) do
184
- Dir.glob('**/*').select do |f|
185
- File.directory? f
186
- end.select do |f|
187
- File.exists?(File.join(f, '.env')) || File.exists?(File.join(f, 'docker-compose.yml')) || File.exists?(File.join(f, 'docker-compose.yaml'))
188
- end.sort
191
+ def each_env
192
+ Dir.chdir(File.join(path, 'envs')) do
193
+ Dir.glob('**/*').select do |f|
194
+ File.directory? f
195
+ end.select do |f|
196
+ File.exist?(File.join(f, '.env')) || File.exist?(File.join(f, 'docker-compose.yml')) || File.exist?(File.join(f, 'docker-compose.yaml'))
197
+ end.sort
198
+ end
189
199
  end
190
- end
191
- end
192
200
 
193
- class Project
194
- attr_accessor :env_root
195
201
 
196
- def initialize path
197
- raise "env path #{path} is not directory!" if !File.directory?(File.expand_path(path))
198
- @env_root = File.realpath(File.expand_path(path))
199
202
  end
200
203
 
201
- def project_root
202
- @project_root = Pathfinder.new(env_root).each_parent.find do |path|
203
- root?(path)
204
- end || (raise "Can't find project root")
205
- end
204
+ class Project
205
+
206
206
 
207
- def core_root
208
- @core_root = Pathfinder.new(project_root).each_parent.find do |path|
209
- core?(path)
207
+ attr_accessor :env_root
208
+
209
+ def initialize(path)
210
+ raise "env path #{path} is not directory!" unless File.directory?(File.expand_path(path))
211
+
212
+ @env_root = File.realpath(File.expand_path(path))
210
213
  end
211
- end
212
214
 
213
- def root? path
214
- return File.realpath(File.expand_path(path)) if File.directory?("#{path}/.git") ||
215
- File.exists?("#{path}/base-service.yml") ||
216
- File.exists?("#{path}/common-services.yml") ||
217
- File.directory?("#{path}/envs")
218
- end
215
+ def project_root
216
+ @project_root = Pathfinder.new(env_root).each_parent.find do |path|
217
+ root?(path)
218
+ end || (raise "Can't find project root")
219
+ end
219
220
 
220
- def core? path
221
- return File.realpath(File.expand_path(path)) if File.exists?("#{path}/.core")
222
- end
221
+ def core_root
222
+ @core_root = Pathfinder.new(project_root).each_parent.find do |path|
223
+ core?(path)
224
+ end
225
+ end
223
226
 
224
- end
227
+ def root?(path)
228
+ return File.realpath(File.expand_path(path)) if File.directory?("#{path}/.git") ||
229
+ File.exist?("#{path}/base-service.yml") ||
230
+ File.exist?("#{path}/common-services.yml") ||
231
+ File.directory?("#{path}/envs")
232
+ end
233
+
234
+ def core?(path)
235
+ return File.realpath(File.expand_path(path)) if File.exist?("#{path}/.core")
236
+ end
237
+
238
+
239
+ end
225
240
 
226
241
 
227
242
  end
228
243
 
229
244
  end
245
+