nucleon 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/Gemfile +4 -8
  2. data/Gemfile.lock +0 -28
  3. data/README.rdoc +13 -5
  4. data/Rakefile +9 -1
  5. data/VERSION +1 -1
  6. data/bin/nucleon +55 -0
  7. data/lib/core/codes.rb +107 -0
  8. data/lib/core/config/collection.rb +57 -0
  9. data/lib/core/config/options.rb +70 -0
  10. data/lib/core/config.rb +342 -0
  11. data/lib/core/core.rb +54 -0
  12. data/lib/core/errors.rb +84 -0
  13. data/lib/core/facade.rb +283 -0
  14. data/lib/core/gems.rb +80 -0
  15. data/lib/core/manager.rb +594 -0
  16. data/lib/core/mixin/action/commit.rb +58 -0
  17. data/lib/core/mixin/action/project.rb +53 -0
  18. data/lib/core/mixin/action/push.rb +52 -0
  19. data/lib/core/mixin/config/collection.rb +53 -0
  20. data/lib/core/mixin/config/options.rb +39 -0
  21. data/lib/core/mixin/macro/object_interface.rb +361 -0
  22. data/lib/core/mixin/macro/plugin_interface.rb +380 -0
  23. data/lib/core/mixin/settings.rb +46 -0
  24. data/lib/core/mixin/sub_config.rb +148 -0
  25. data/lib/core/mod/hash.rb +29 -0
  26. data/lib/core/plugin/action.rb +371 -0
  27. data/lib/core/plugin/base.rb +313 -0
  28. data/lib/core/plugin/command.rb +98 -0
  29. data/lib/core/plugin/event.rb +53 -0
  30. data/lib/core/plugin/extension.rb +12 -0
  31. data/lib/core/plugin/project.rb +890 -0
  32. data/lib/core/plugin/template.rb +80 -0
  33. data/lib/core/plugin/translator.rb +38 -0
  34. data/lib/core/util/cli.rb +353 -0
  35. data/lib/core/util/console.rb +237 -0
  36. data/lib/core/util/data.rb +404 -0
  37. data/lib/core/util/disk.rb +114 -0
  38. data/lib/core/util/git.rb +43 -0
  39. data/lib/core/util/liquid.rb +17 -0
  40. data/lib/core/util/logger.rb +147 -0
  41. data/lib/core/util/package.rb +93 -0
  42. data/lib/core/util/shell.rb +239 -0
  43. data/lib/nucleon/action/add.rb +69 -0
  44. data/lib/nucleon/action/create.rb +52 -0
  45. data/lib/nucleon/action/extract.rb +49 -0
  46. data/lib/nucleon/action/remove.rb +51 -0
  47. data/lib/nucleon/action/save.rb +53 -0
  48. data/lib/nucleon/action/update.rb +37 -0
  49. data/lib/nucleon/command/bash.rb +146 -0
  50. data/lib/nucleon/event/regex.rb +52 -0
  51. data/lib/nucleon/project/git.rb +465 -0
  52. data/lib/nucleon/project/github.rb +108 -0
  53. data/lib/nucleon/template/json.rb +16 -0
  54. data/lib/nucleon/template/wrapper.rb +16 -0
  55. data/lib/nucleon/template/yaml.rb +16 -0
  56. data/lib/nucleon/translator/json.rb +27 -0
  57. data/lib/nucleon/translator/yaml.rb +27 -0
  58. data/lib/nucleon.rb +18 -15
  59. data/locales/en.yml +3 -132
  60. data/nucleon.gemspec +66 -27
  61. data/spec/core/util/console_spec.rb +489 -0
  62. metadata +109 -96
@@ -0,0 +1,594 @@
1
+
2
+ module Nucleon
3
+ class Manager
4
+
5
+ include Celluloid
6
+
7
+ #-----------------------------------------------------------------------------
8
+
9
+ @@supervisors = {}
10
+
11
+ #-----------------------------------------------------------------------------
12
+ # Plugin manager interface
13
+
14
+ def self.init_manager(name)
15
+ name = name.to_sym
16
+
17
+ Manager.supervise_as name
18
+ @@supervisors[name] = Celluloid::Actor[name]
19
+ end
20
+
21
+ #---
22
+
23
+ def self.connection(name = :core)
24
+ name = name.to_sym
25
+
26
+ init_manager(name) unless @@supervisors.has_key?(name)
27
+
28
+ begin
29
+ @@supervisors[name].test_connection
30
+ rescue Celluloid::DeadActorError
31
+ retry
32
+ end
33
+ @@supervisors[name]
34
+ end
35
+
36
+ #---
37
+
38
+ def initialize
39
+ @logger = Nucleon.logger
40
+
41
+ @namespaces = {}
42
+ @types = {}
43
+ @load_info = {}
44
+ @plugins = {}
45
+ end
46
+
47
+ #-----------------------------------------------------------------------------
48
+ # Property accessor / modifiers
49
+
50
+ attr_reader :logger
51
+
52
+ #---
53
+
54
+ def namespaces
55
+ @namespaces.keys
56
+ end
57
+
58
+ def define_namespace(*namespaces)
59
+ namespaces.each do |namespace|
60
+ @namespaces[namespace.to_sym] = true
61
+ end
62
+ end
63
+
64
+ #---
65
+
66
+ def types
67
+ @types.keys
68
+ end
69
+
70
+ #---
71
+
72
+ def type_default(type)
73
+ @types[type.to_sym]
74
+ end
75
+
76
+ #---
77
+
78
+ def loaded_plugins(type = nil, provider = nil)
79
+ results = {}
80
+ type = type.to_sym if type
81
+ provider = provider.to_sym if provider
82
+
83
+ if type && @load_info.has_key?(type)
84
+ if provider && @load_info.has_key?(provider)
85
+ results = @load_info[type][provider]
86
+ else
87
+ results = @load_info[type]
88
+ end
89
+ elsif ! type
90
+ results = @load_info
91
+ end
92
+ results
93
+ end
94
+
95
+ #---
96
+
97
+ def plugins(type = nil, provider = nil)
98
+ results = {}
99
+ type = type.to_sym if type
100
+ provider = provider.to_sym if provider
101
+
102
+ if type && @plugins.has_key?(type)
103
+ if provider && ! @plugins[type].keys.empty?
104
+ @plugins[type].each do |instance_name, plugin|
105
+ plugin = @plugins[type][instance_name]
106
+ results[instance_name] = plugin if plugin.plugin_provider == provider
107
+ end
108
+ else
109
+ results = @plugins[type]
110
+ end
111
+ elsif ! type
112
+ results = @plugins
113
+ end
114
+ results
115
+ end
116
+
117
+ #-----------------------------------------------------------------------------
118
+ # Operations
119
+
120
+ def test_connection
121
+ true
122
+ end
123
+
124
+ #---
125
+
126
+ def reload
127
+ current_time = Time.now
128
+ Celluloid.logger = logger
129
+
130
+ logger.info("Initializing the Nucleon plugin system at #{current_time}")
131
+
132
+ define_namespace :nucleon
133
+
134
+ define_type :extension => nil, # Core
135
+ :action => :update, # Core
136
+ :project => :git, # Core
137
+ :command => :bash, # Core
138
+ :event => :regex, # Utility
139
+ :template => :json, # Utility
140
+ :translator => :json # Utility
141
+
142
+ load_plugins(true)
143
+ logger.info("Finished initializing Nucleon plugin system at #{Time.now}")
144
+ end
145
+
146
+ #---
147
+
148
+ def define_type(type_info)
149
+ if type_info.is_a?(Hash)
150
+ logger.info("Defining plugin types at #{Time.now}")
151
+
152
+ type_info.each do |type, default_provider|
153
+ logger.debug("Mapping plugin type #{type} to default provider #{default_provider}")
154
+ @types[type.to_sym] = default_provider
155
+ end
156
+ else
157
+ logger.warn("Defined types must be specified as a hash to be registered properly")
158
+ end
159
+ end
160
+
161
+ #---
162
+
163
+ def load_plugins(reset_gems = false)
164
+ # Register core plugins
165
+ logger.info("Initializing core plugins at #{Time.now}")
166
+ register(File.join(File.dirname(__FILE__), '..'))
167
+
168
+ # Register external Gem defined plugins
169
+ Gems.register(reset_gems)
170
+
171
+ # Register any other extension plugins
172
+ exec(:register_plugins)
173
+
174
+ # Autoload all registered plugins
175
+ autoload
176
+ end
177
+
178
+ #---
179
+
180
+ def register(base_path, &code)
181
+ namespaces.each do |namespace|
182
+ namespace_path = File.join(base_path, namespace.to_s)
183
+
184
+ if File.directory?(namespace_path)
185
+ register_namespace(namespace, namespace_path, &code)
186
+ end
187
+ end
188
+ end
189
+
190
+ #---
191
+
192
+ def register_namespace(namespace, base_path, &code)
193
+ if File.directory?(base_path)
194
+ logger.info("Loading files from #{base_path} at #{Time.now}")
195
+
196
+ Dir.glob(File.join(base_path, '*.rb')).each do |file|
197
+ logger.debug("Loading file: #{file}")
198
+ require file
199
+ end
200
+
201
+ logger.info("Loading directories from #{base_path} at #{Time.now}")
202
+ Dir.entries(base_path).each do |path|
203
+ unless path.match(/^\.\.?$/)
204
+ register_type(namespace, base_path, path, &code) if types.include?(path.to_sym)
205
+ end
206
+ end
207
+ end
208
+ end
209
+ protected :register_namespace
210
+
211
+ #---
212
+
213
+ def register_type(namespace, base_path, plugin_type, &code)
214
+ base_directory = File.join(base_path, plugin_type.to_s)
215
+
216
+ if File.directory?(base_directory)
217
+ logger.info("Registering #{base_directory} at #{Time.now}")
218
+
219
+ Dir.glob(File.join(base_directory, '*.rb')).each do |file|
220
+ add_build_info(namespace, plugin_type, file)
221
+ end
222
+ end
223
+ end
224
+ protected :register_type
225
+
226
+ #---
227
+
228
+ def add_build_info(namespace, type, file, &code)
229
+ type = type.to_sym
230
+
231
+ @load_info[type] = {} unless @load_info.has_key?(type)
232
+
233
+ components = file.split(File::SEPARATOR)
234
+ provider = components.pop.sub(/\.rb/, '').to_sym
235
+ directory = components.join(File::SEPARATOR)
236
+
237
+ logger.info("Loading nucleon #{type} plugin #{provider} at #{Time.now}")
238
+
239
+ unless @load_info[type].has_key?(provider)
240
+ data = {
241
+ :namespace => namespace,
242
+ :type => type,
243
+ :provider => provider,
244
+ :directory => directory,
245
+ :file => file
246
+ }
247
+ code.call(data) if code
248
+
249
+ logger.debug("Plugin #{type} loaded: #{data.inspect}")
250
+ @load_info[type][provider] = data
251
+ end
252
+ end
253
+ protected :add_build_info
254
+
255
+ #---
256
+
257
+ def autoload
258
+ logger.info("Autoloading registered plugins at #{Time.now}")
259
+
260
+ @load_info.keys.each do |type|
261
+ logger.debug("Autoloading type: #{type}")
262
+
263
+ @load_info[type].each do |provider, plugin|
264
+ logger.debug("Autoloading provider #{provider} at #{plugin[:directory]}")
265
+
266
+ nucleon_require(plugin[:directory], provider)
267
+
268
+ @load_info[type][provider][:class] = provider_class(plugin[:namespace], type, provider)
269
+ logger.debug("Updated #{type} #{provider} load info: #{@load_info[type][provider].inspect}")
270
+
271
+ # Make sure extensions are listening from the time they are loaded
272
+ load(:extension, provider, { :name => provider }) if type == :extension # Create a persistent instance
273
+ end
274
+ end
275
+ end
276
+
277
+ #---
278
+
279
+ def load(type, provider = nil, options = {})
280
+ config = Config.ensure(options)
281
+ name = config.get(:name, nil)
282
+
283
+ logger.info("Fetching plugin #{type} provider #{provider} at #{Time.now}")
284
+ logger.debug("Plugin options: #{config.export.inspect}")
285
+
286
+ default_provider = type_default(type)
287
+
288
+ if options.is_a?(Hash) || options.is_a?(Nucleon::Config)
289
+ config = Config.ensure(translate_type(type, options))
290
+ provider = config.get(:provider, provider)
291
+ options = config.export
292
+ end
293
+ provider = default_provider unless provider
294
+
295
+ if name
296
+ logger.debug("Looking up existing instance of #{name}")
297
+
298
+ existing_instance = get(type, name)
299
+ logger.info("Using existing instance of #{type}, #{name}") if existing_instance
300
+ end
301
+
302
+ return existing_instance if existing_instance
303
+ create(type, provider, config.export)
304
+ end
305
+
306
+ #---
307
+
308
+ def load_multiple(type, data, build_hash = false, keep_array = false)
309
+ logger.info("Fetching multiple plugins of #{type} at #{Time.now}")
310
+
311
+ group = ( build_hash ? {} : [] )
312
+ klass = base_plugin_class(type)
313
+ data = klass.build_info(type, data) if klass.respond_to?(:build_info)
314
+
315
+ logger.debug("Translated plugin data: #{data.inspect}")
316
+
317
+ data.each do |options|
318
+ if plugin = load(type, options[:provider], options)
319
+ if build_hash
320
+ group[plugin.plugin_name] = plugin
321
+ else
322
+ group << plugin
323
+ end
324
+ end
325
+ end
326
+ return group.shift if ! build_hash && group.length == 1 && ! keep_array
327
+ group
328
+ end
329
+
330
+ #---
331
+
332
+ def create(type, provider, options = {})
333
+ type = type.to_sym
334
+ provider = provider.to_sym
335
+
336
+ unless @types.has_key?(type)
337
+ logger.warn("Plugin type #{type} creation requested but it has not been registered yet")
338
+ return nil
339
+ end
340
+
341
+ info = @load_info[type][provider] if Util::Data.exists?(@load_info, [ type, provider ])
342
+
343
+ if info
344
+ logger.debug("Plugin information for #{provider} #{type} found. Data: #{info.inspect}")
345
+
346
+ instance_name = "#{provider}_" + Nucleon.sha1(options)
347
+ options = translate(info[:namespace], type, provider, options)
348
+
349
+ @plugins[type] = {} unless @plugins.has_key?(type)
350
+
351
+ unless instance_name && @plugins[type].has_key?(instance_name)
352
+ info[:instance_name] = instance_name
353
+ options[:meta] = Config.new(info).import(Util::Data.hash(options[:meta]))
354
+
355
+ logger.info("Creating new plugin #{provider} #{type} with #{options.inspect}")
356
+
357
+ plugin = info[:class].new(type, provider, options)
358
+
359
+ @plugins[type][instance_name] = plugin
360
+ end
361
+ return @plugins[type][instance_name]
362
+ end
363
+
364
+ logger.warn("Plugin information cannot be found for plugin #{type} #{provider}")
365
+ nil
366
+ end
367
+
368
+ #---
369
+
370
+ def get(type, name)
371
+ logger.info("Fetching plugin #{type} #{name}")
372
+
373
+ if @plugins.has_key?(type)
374
+ @plugins[type].each do |instance_name, plugin|
375
+ if plugin.plugin_name.to_s == name.to_s
376
+ logger.debug("Plugin #{type} #{name} found")
377
+ return plugin
378
+ end
379
+ end
380
+ end
381
+ logger.debug("Plugin #{type} #{name} not found")
382
+ nil
383
+ end
384
+
385
+ #---
386
+
387
+ def remove(plugin)
388
+ if plugin && plugin.respond_to?(:plugin_type) && @plugins.has_key?(plugin.plugin_type)
389
+ logger.debug("Removing #{plugin.plugin_type} #{plugin.plugin_name}")
390
+ @plugins[plugin.plugin_type].delete(plugin.plugin_instance_name)
391
+ plugin.terminate if plugin.respond_to?(:terminate)
392
+ else
393
+ logger.warn("Cannot remove plugin: #{plugin.inspect}")
394
+ end
395
+ end
396
+
397
+ #-----------------------------------------------------------------------------
398
+ # Extension hook execution
399
+
400
+ def exec(method, options = {})
401
+ results = nil
402
+
403
+ if Nucleon.log_level == :hook # To save processing on rendering
404
+ logger.hook("Executing extension hook { #{method} } at #{Time.now} with:\n#{PP.pp(options, '')}\n")
405
+ end
406
+
407
+ extensions = plugins(:extension)
408
+
409
+ extensions.each do |name, plugin|
410
+ provider = plugin.plugin_provider
411
+ result = nil
412
+
413
+ logger.debug("Checking extension #{provider}")
414
+
415
+ if plugin.respond_to?(method)
416
+ results = {} if results.nil?
417
+
418
+ result = plugin.send(method, options)
419
+ logger.info("Completed hook #{method} at #{Time.now} with: #{result.inspect}")
420
+
421
+ if block_given?
422
+ results[provider] = yield(:process, result)
423
+ logger.debug("Processed extension result into: #{results[provider].inspect}")
424
+ end
425
+
426
+ if results[provider].nil?
427
+ logger.debug("Setting extension result to: #{result.inspect}")
428
+ results[provider] = result
429
+ end
430
+ end
431
+ end
432
+
433
+ if ! results.nil? && block_given?
434
+ results = yield(:reduce, results)
435
+ logger.debug("Reducing extension results to: #{results.inspect}")
436
+ else
437
+ logger.debug("Final extension results: #{results.inspect}")
438
+ end
439
+ results
440
+ end
441
+
442
+ #---
443
+
444
+ def config(type, options = {})
445
+ config = Config.ensure(options)
446
+
447
+ logger.debug("Generating #{type} extended configuration from: #{config.export.inspect}")
448
+
449
+ exec("#{type}_config", Config.new(config.export)) do |op, data|
450
+ if op == :reduce
451
+ data.each do |provider, result|
452
+ config.defaults(result)
453
+ end
454
+ nil
455
+ else
456
+ hash(data)
457
+ end
458
+ end
459
+ config.delete(:extension_type)
460
+
461
+ logger.debug("Final extended configuration: #{config.export.inspect}")
462
+ config
463
+ end
464
+
465
+ #---
466
+
467
+ def check(method, options = {})
468
+ config = Config.ensure(options)
469
+
470
+ logger.debug("Checking extension #{method} given: #{config.export.inspect}")
471
+
472
+ success = exec(method, config.import({ :extension_type => :check })) do |op, data|
473
+ if op == :reduce
474
+ ! data.values.include?(false)
475
+ else
476
+ data ? true : false
477
+ end
478
+ end
479
+
480
+ success = success.nil? || success ? true : false
481
+
482
+ logger.debug("Extension #{method} check result: #{success.inspect}")
483
+ success
484
+ end
485
+
486
+ #---
487
+
488
+ def value(method, value, options = {})
489
+ config = Config.ensure(options)
490
+
491
+ logger.debug("Setting extension #{method} value given: #{value.inspect}")
492
+
493
+ exec(method, config.import({ :value => value, :extension_type => :value })) do |op, data|
494
+ if op == :process
495
+ value = data unless data.nil?
496
+ end
497
+ end
498
+
499
+ logger.debug("Extension #{method} retrieved value: #{value.inspect}")
500
+ value
501
+ end
502
+
503
+ #---
504
+
505
+ def collect(method, options = {})
506
+ config = Config.ensure(options)
507
+ values = []
508
+
509
+ logger.debug("Collecting extension #{method} values")
510
+
511
+ exec(method, config.import({ :extension_type => :collect })) do |op, data|
512
+ if op == :process
513
+ values << data unless data.nil?
514
+ end
515
+ end
516
+
517
+ logger.debug("Extension #{method} collected values: #{values.inspect}")
518
+ values
519
+ end
520
+
521
+ #-----------------------------------------------------------------------------
522
+ # Utilities
523
+
524
+ def translate_type(type, options)
525
+ klass = base_plugin_class(type)
526
+ logger.debug("Executing option translation for: #{klass.inspect}")
527
+
528
+ options = klass.send(:translate, options) if klass.respond_to?(method)
529
+ options
530
+ end
531
+
532
+ #---
533
+
534
+ def translate(namespace, type, provider, options)
535
+ klass = provider_class(namespace, type, provider)
536
+ logger.debug("Executing option translation for: #{klass.inspect}")
537
+
538
+ options = klass.send(:translate, options) if klass.respond_to?(method)
539
+ options
540
+ end
541
+
542
+ #---
543
+
544
+ def class_name(name, separator = '::', want_array = FALSE)
545
+ components = []
546
+
547
+ case name
548
+ when String, Symbol
549
+ components = name.to_s.split(separator)
550
+ when Array
551
+ components = name
552
+ end
553
+
554
+ components.collect! do |value|
555
+ value = value.to_s.strip
556
+ value[0] = value.capitalize[0] if value =~ /^[a-z]/
557
+ value
558
+ end
559
+
560
+ if want_array
561
+ return components
562
+ end
563
+ components.join(separator)
564
+ end
565
+
566
+ #---
567
+
568
+ def class_const(name, separator = '::')
569
+ components = class_name(name, separator, TRUE)
570
+ constant = Object
571
+
572
+ components.each do |component|
573
+ constant = constant.const_defined?(component) ?
574
+ constant.const_get(component) :
575
+ constant.const_missing(component)
576
+ end
577
+ constant
578
+ end
579
+
580
+ #---
581
+
582
+ def base_plugin_class(type)
583
+ class_const([ :nucleon, :plugin, type ])
584
+ end
585
+ protected :base_plugin_class
586
+
587
+ #---
588
+
589
+ def provider_class(namespace, type, provider)
590
+ class_const([ namespace, type, provider ])
591
+ end
592
+ protected :provider_class
593
+ end
594
+ end
@@ -0,0 +1,58 @@
1
+
2
+ module Nucleon
3
+ module Mixin
4
+ module Action
5
+ module Commit
6
+
7
+ #-----------------------------------------------------------------------------
8
+ # Options
9
+
10
+ def commit_options(parser, optional = true)
11
+ if optional
12
+ parser.option_bool(:commit, false,
13
+ '--commit',
14
+ 'nucleon.core.mixins.commit.options.commit'
15
+ )
16
+ else
17
+ parser.options[:commit] = true
18
+ end
19
+
20
+ parser.option_bool(:allow_empty, false,
21
+ '--empty',
22
+ 'nucleon.core.mixins.commit.options.empty'
23
+ )
24
+ parser.option_bool(:propogate, false,
25
+ '--propogate',
26
+ 'nucleon.core.mixins.commit.options.propogate'
27
+ )
28
+ parser.option_str(:message, '',
29
+ '--message COMMIT_MESSAGE',
30
+ 'nucleon.core.mixins.commit.options.message'
31
+ )
32
+ parser.option_str(:author, nil,
33
+ '--author COMMIT_AUTHOR',
34
+ 'nucleon.core.mixins.commit.options.author'
35
+ )
36
+ end
37
+
38
+ #-----------------------------------------------------------------------------
39
+ # Operations
40
+
41
+ def commit(project, files = '.')
42
+ success = true
43
+
44
+ if project && settings[:commit]
45
+ success = project.commit(files, extended_config(:commit, {
46
+ :allow_empty => settings[:allow_empty],
47
+ :message => settings[:message],
48
+ :author => settings[:author],
49
+ :propogate => settings[:propogate]
50
+ }))
51
+ end
52
+ success
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,53 @@
1
+
2
+ module Nucleon
3
+ module Mixin
4
+ module Action
5
+ module Project
6
+
7
+ #-----------------------------------------------------------------------------
8
+ # Options
9
+
10
+ def project_options(parser, ref_override = false, rev_override = false)
11
+ parser.option_str(:project_provider, 'git',
12
+ '--proj-provider PROVIDER',
13
+ 'nucleon.core.mixins.project.options.provider'
14
+ )
15
+ if ref_override
16
+ parser.option_str(:reference, nil,
17
+ '--reference PROJECT_REF',
18
+ 'nucleon.core.mixins.project.options.reference'
19
+ )
20
+ end
21
+ if rev_override
22
+ parser.option_str(:revision, nil,
23
+ '--revision PROJECT_REV',
24
+ 'nucleon.core.mixins.project.options.revision'
25
+ )
26
+ end
27
+ end
28
+
29
+ #-----------------------------------------------------------------------------
30
+ # Operations
31
+
32
+ def project_load(root_dir, update = false)
33
+
34
+ # 1. Set a default project provider (reference can override)
35
+ # 2. Get project from root directory
36
+ # 3. Initialize project if not yet initialized
37
+ # 4. Set remote if needed
38
+ # 5. Checkout revision if needed
39
+ # 6. Pull down updates if requested
40
+
41
+ return Nucleon.project(extended_config(:project, {
42
+ :provider => settings[:project_provider],
43
+ :directory => root_dir,
44
+ :url => settings[:reference],
45
+ :revision => settings[:revision],
46
+ :pull => update
47
+ }))
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+