coral_core 0.2.26 → 0.2.30

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.
Files changed (46) hide show
  1. data/Gemfile +7 -2
  2. data/Gemfile.lock +84 -15
  3. data/VERSION +1 -1
  4. data/coral_core.gemspec +51 -16
  5. data/lib/{coral_core/command.rb → coral/command/shell.rb} +12 -116
  6. data/lib/coral/machine/fog.rb +215 -0
  7. data/lib/coral/network/default.rb +26 -0
  8. data/lib/coral/node/rackspace.rb +23 -0
  9. data/lib/coral_core.rb +249 -134
  10. data/lib/coral_core/config.rb +233 -275
  11. data/lib/coral_core/config/collection.rb +57 -0
  12. data/lib/coral_core/config/options.rb +70 -0
  13. data/lib/coral_core/config/project.rb +225 -0
  14. data/lib/coral_core/core.rb +19 -173
  15. data/lib/coral_core/event/puppet_event.rb +98 -0
  16. data/lib/coral_core/mixin/config_collection.rb +52 -0
  17. data/lib/coral_core/mixin/config_ops.rb +51 -0
  18. data/lib/coral_core/mixin/config_options.rb +38 -0
  19. data/lib/coral_core/mixin/lookup.rb +211 -0
  20. data/lib/coral_core/mixin/macro/object_interface.rb +292 -0
  21. data/lib/coral_core/mixin/macro/plugin_interface.rb +277 -0
  22. data/lib/coral_core/mixin/settings.rb +46 -0
  23. data/lib/coral_core/mixin/sub_config.rb +208 -0
  24. data/lib/coral_core/mod/hash.rb +29 -0
  25. data/lib/{hiera_backend.rb → coral_core/mod/hiera_backend.rb} +0 -0
  26. data/lib/coral_core/plugin.rb +261 -0
  27. data/lib/coral_core/plugin/command.rb +95 -0
  28. data/lib/coral_core/plugin/machine.rb +152 -0
  29. data/lib/coral_core/plugin/network.rb +24 -0
  30. data/lib/coral_core/plugin/node.rb +184 -0
  31. data/lib/coral_core/plugin_base.rb +147 -0
  32. data/lib/coral_core/repository.rb +471 -82
  33. data/lib/coral_core/util/cli.rb +293 -0
  34. data/lib/coral_core/util/data.rb +178 -8
  35. data/lib/coral_core/util/disk.rb +13 -0
  36. data/lib/coral_core/util/git.rb +35 -10
  37. data/lib/coral_core/{interface.rb → util/interface.rb} +31 -21
  38. data/lib/coral_core/util/process.rb +43 -0
  39. data/locales/en.yml +8 -0
  40. data/spec/coral_core/interface_spec.rb +45 -45
  41. metadata +109 -34
  42. data/.gitmodules +0 -12
  43. data/lib/coral_core/memory.rb +0 -226
  44. data/lib/coral_core/util/git/base.rb +0 -65
  45. data/lib/coral_core/util/git/lib.rb +0 -89
  46. data/lib/coral_core/util/git/remote.rb +0 -12
@@ -0,0 +1,147 @@
1
+
2
+ module Coral
3
+ module Plugin
4
+ class Base < Core
5
+ # All Plugin classes should directly or indirectly extend Base
6
+
7
+ def initialize(type, provider, options)
8
+ config = Config.ensure(options)
9
+ name = Util::Data.ensure_value(config.delete(:name), provider)
10
+
11
+ set_meta(config.delete(:meta, Config.new))
12
+
13
+ super(config)
14
+
15
+ self.name = name
16
+
17
+ normalize
18
+ end
19
+
20
+ #---
21
+
22
+ def initialized?(options = {})
23
+ return true
24
+ end
25
+
26
+ #---
27
+
28
+ def method_missing(method, *args, &block)
29
+ return nil
30
+ end
31
+
32
+ #-----------------------------------------------------------------------------
33
+ # Property accessor / modifiers
34
+
35
+ def name
36
+ return get(:name)
37
+ end
38
+
39
+ #---
40
+
41
+ def name=name
42
+ set(:name, string(name))
43
+ end
44
+
45
+ #---
46
+
47
+ def meta
48
+ return @meta
49
+ end
50
+
51
+ #---
52
+
53
+ def set_meta(meta)
54
+ @meta = Config.ensure(meta)
55
+ end
56
+
57
+ #---
58
+
59
+ def plugin_type
60
+ return meta.get(:type)
61
+ end
62
+
63
+ #---
64
+
65
+ def plugin_provider
66
+ return meta.get(:provider)
67
+ end
68
+
69
+ #---
70
+
71
+ def plugin_directory
72
+ return meta.get(:directory)
73
+ end
74
+
75
+ #---
76
+
77
+ def plugin_file
78
+ return meta.get(:file)
79
+ end
80
+
81
+ #---
82
+
83
+ def plugin_instance_name
84
+ return meta.get(:instance_name)
85
+ end
86
+
87
+ #---
88
+
89
+ def plugin_parent=parent
90
+ meta.set(:parent, parent) if parent.is_a?(Coral::Plugin::Base)
91
+ end
92
+
93
+ #---
94
+
95
+ def plugin_parent
96
+ return meta.get(:parent)
97
+ end
98
+
99
+ #-----------------------------------------------------------------------------
100
+ # Plugin operations
101
+
102
+ def normalize
103
+ end
104
+
105
+ #-----------------------------------------------------------------------------
106
+ # Utilities
107
+
108
+ def self.build_info(type, data)
109
+ plugins = []
110
+
111
+ if data.is_a?(Hash)
112
+ data = [ data ]
113
+ end
114
+
115
+ if data.is_a?(Array)
116
+ data.each do |info|
117
+ unless Util::Data.empty?(info)
118
+ info = translate(info)
119
+
120
+ if Util::Data.empty?(info[:provider])
121
+ info[:provider] = Plugin.type_default(type)
122
+ end
123
+
124
+ plugins << info
125
+ end
126
+ end
127
+ end
128
+ return plugins
129
+ end
130
+
131
+ #---
132
+
133
+ def self.translate(data)
134
+ return ( data.is_a?(Hash) ? symbol_map(data) : {} )
135
+ end
136
+
137
+ #---
138
+
139
+ def self.ensure_plugin_collection
140
+ include Mixin::Settings
141
+ include Mixin::SubConfig
142
+
143
+ extend Mixin::Macro::PluginInterface
144
+ end
145
+ end
146
+ end
147
+ end
@@ -1,164 +1,553 @@
1
-
2
1
  module Coral
3
2
  class Repository < Core
4
-
3
+
4
+ @@repositories = {}
5
+
6
+ #---
7
+
8
+ def self.collection
9
+ return @@repositories
10
+ end
11
+
5
12
  #-----------------------------------------------------------------------------
6
13
  # Constructor / Destructor
7
14
 
15
+ def self.init(directory, url, revision, options = {})
16
+ config = Config.ensure(options)
17
+
18
+ return new(config.import({
19
+ :directory => directory,
20
+ :origin => url,
21
+ :revision => revision
22
+ }))
23
+ end
24
+
25
+ #---
26
+
27
+ def self.open(directory, options = {})
28
+ config = Config.ensure(options)
29
+
30
+ directory = Util::Disk.filename(directory)
31
+
32
+ if ! @@repositories.has_key?(directory) || config.get(:reset, false)
33
+ return new(config.import({
34
+ :directory => directory
35
+ }))
36
+ end
37
+ return @@repositories[directory]
38
+ end
39
+
40
+ #-----------------------------------------------------------------------------
41
+
8
42
  def initialize(options = {})
9
43
  config = Config.ensure(options)
10
44
 
11
45
  super(config)
12
46
 
13
- @name = config.get(:name, '')
14
- @directory = config.get(:directory, '')
15
- @submodule = config.get(:submodule, '')
16
- @remote_dir = config.get(:remote_dir, '')
47
+ set_location(config.get(:directory, Dir.pwd))
17
48
 
18
- ensure_git(true)
19
- end
49
+ @cloud = config.get(:object_container, nil)
50
+ @remote_path = config.get(:remote_path, nil)
20
51
 
21
- #-----------------------------------------------------------------------------
22
- # Property accessors / modifiers
52
+ @origin = config.get(:origin, nil)
53
+ @revision = config.get(:revision, nil)
54
+
55
+ set_origin(@origin) unless @origin.nil?
56
+ checkout(@revision) unless @revision.nil?
57
+
58
+ pull if config.get(:pull, false)
59
+ end
60
+
61
+ #---
23
62
 
24
- attr_accessor :name, :remote_dir
25
- attr_reader :directory, :submodule, :git
63
+ def inspect
64
+ "#<#{self.class}: #{directory} : #{@origin} : #{@revision}>"
65
+ end
66
+
67
+ #-----------------------------------------------------------------------------
68
+ # Location information
69
+
70
+ def self.git_dir(path, require_top_level = false)
71
+ path = File.expand_path(path)
72
+ git_dir = File.join(path, '.git')
26
73
 
74
+ if File.exist?(git_dir)
75
+ if File.directory?(git_dir)
76
+ return git_dir
77
+ elsif ! require_top_level
78
+ git_dir = Util::Disk.read(git_dir)
79
+ unless git_dir.nil?
80
+ git_dir = git_dir.gsub(/^gitdir\:\s*/, '').strip
81
+ return git_dir if File.directory?(git_dir)
82
+ end
83
+ end
84
+ elsif File.exist?(path) && (path =~ /\.git$/ && File.exist?(File.join(path, 'HEAD')))
85
+ return path
86
+ end
87
+ return nil
88
+ end
89
+
90
+ #-----------------------------------------------------------------------------
91
+ # URLs
92
+
93
+ def self.url(host, repo, options = {})
94
+ config = Config.ensure(options)
95
+
96
+ user = config.get(:user, 'git')
97
+ auth = config.get(:auth, true)
98
+ return user + (auth ? '@' : '://') + host + (auth ? ':' : '/') + repo
99
+ end
100
+
27
101
  #---
102
+
103
+ def self.edit_url(url, options = {})
104
+ config = Config.ensure(options)
105
+
106
+ if matches = url.strip.match(/^(https?|git)\:\/\/([^\/]+)\/(.+)/)
107
+ host, path = matches.captures
108
+ return url(host, path, config.import({ :auth => true }))
109
+ end
110
+ return url
111
+ end
112
+
113
+ #-----------------------------------------------------------------------------
114
+ # Checks
28
115
 
29
116
  def ensure_git(reset = false)
30
- if reset || ! @git
31
- if @directory.empty?
32
- @git = nil
33
- else
34
- directory = @directory
35
- unless Util::Data.empty?(@submodule)
36
- directory = File.join(@directory, @submodule)
117
+ if reset || ! @git_lib
118
+ @git_lib = nil
119
+ unless directory.empty?
120
+ @git_lib = Git.new(directory)
121
+ end
122
+ end
123
+ return self
124
+ end
125
+ protected :ensure_git
126
+
127
+ #---
128
+
129
+ def can_persist?
130
+ ensure_git
131
+ return true if @git_lib
132
+ return false
133
+ end
134
+
135
+ #---
136
+
137
+ def self.top?(path)
138
+ git_dir = File.join(path, '.git')
139
+ if File.exist?(git_dir)
140
+ return true if File.directory?(git_dir)
141
+ elsif File.exist?(path) && (path =~ /\.git$/ && File.exist?(File.join(path, 'HEAD')))
142
+ return true
143
+ end
144
+ return false
145
+ end
146
+
147
+ #---
148
+
149
+ def top?
150
+ return self.class.top?(directory)
151
+ end
152
+
153
+ #---
154
+
155
+ def self.submodule?(path)
156
+ git_dir = File.join(path, '.git')
157
+ if File.exist?(git_dir)
158
+ unless File.directory?(git_dir)
159
+ git_dir = Util::Disk.read(git_dir)
160
+ unless git_dir.nil?
161
+ git_dir = git_dir.gsub(/^gitdir\:\s*/, '').strip
162
+ return true if File.directory?(git_dir)
37
163
  end
38
- @git = Git.open(directory, {
39
- :log => logger,
40
- })
41
164
  end
42
165
  end
166
+ return false
167
+ end
168
+
169
+ #---
170
+
171
+ def submodule?
172
+ return self.class.submodule?(directory)
173
+ end
174
+
175
+ #-----------------------------------------------------------------------------
176
+ # Property accessors / modifiers
177
+
178
+ attr_reader :directory
179
+
180
+ #---
181
+
182
+ def path
183
+ if parent.nil?
184
+ return directory
185
+ end
186
+ return directory.gsub(parent.directory + File::SEPARATOR, '')
187
+ end
188
+
189
+ #---
190
+
191
+ def git
192
+ return @git_lib.git if can_persist?
193
+ return nil
194
+ end
195
+ protected :git
196
+
197
+ #---
198
+
199
+ def config(name, options = {})
200
+ config = Config.ensure(options) # Just in case we throw a configuration in
201
+ return git.config(config.export, name) if can_persist?
202
+ return nil
203
+ end
204
+
205
+ #---
206
+
207
+ def set_config(name, value, options = {})
208
+ config = Config.ensure(options) # Just in case we throw a configuration in
209
+ git.config(config.export, name, string(value)) if can_persist?
43
210
  return self
44
211
  end
45
212
 
46
213
  #---
214
+
215
+ def delete_config(name, options = {})
216
+ config = Config.ensure(options)
217
+ git.config(config.import({ :remove_section => true }).options, name) if can_persist?
218
+ return self
219
+ end
220
+
221
+ #---
222
+
223
+ def set_location(directory)
224
+ @@repositories.delete(@directory) if @directory
225
+
226
+ if Util::Data.empty?(directory)
227
+ @directory = Dir.pwd
228
+ else
229
+ @directory = Util::Disk.filename(directory)
230
+ end
231
+
232
+ @@repositories[@directory] = self
47
233
 
48
- def set_repository(directory = '', submodule = '')
49
- @directory = string(directory)
50
- @submodule = string(submodule)
51
234
  ensure_git(true)
235
+
236
+ init_parent
237
+ init_remotes
238
+ load_revision
52
239
  return self
53
240
  end
54
-
241
+
55
242
  #-----------------------------------------------------------------------------
243
+ # Basic Git operations
244
+
245
+ attr_reader :parent, :revision
246
+
247
+ #---
248
+
249
+ def init_parent
250
+ @parent = nil
56
251
 
57
- def set_remote(name, hosts, options = {})
252
+ unless top?
253
+ search_dir = directory
254
+
255
+ while File.directory?((search_dir = File.expand_path('..', search_dir)))
256
+ if self.class.git_dir(search_dir)
257
+ @parent = self.class.open(search_dir)
258
+ break;
259
+ end
260
+ end
261
+ end
262
+ return self
263
+ end
264
+ protected :init_parent
265
+
266
+ #---
267
+
268
+ def load_revision
269
+ if can_persist?
270
+ @revision = git.native(:rev_parse, { :abbrev_ref => true }, 'HEAD').strip
271
+
272
+ if @revision.empty?
273
+ @revision = git.native(:rev_parse, {}, 'HEAD').strip
274
+ end
275
+ load_submodules
276
+ end
277
+ return self
278
+ end
279
+ protected :load_revision
280
+
281
+ #---
282
+
283
+ def checkout(revision)
284
+ if can_persist?
285
+ revision = revision.strip
286
+
287
+ git.checkout({}, revision) unless @git_lib.bare
288
+ @revision = revision
289
+ load_submodules
290
+ end
291
+ return self
292
+ end
293
+
294
+ #---
295
+
296
+ def commit(files = '.', options = {})
58
297
  config = Config.ensure(options)
59
298
 
60
299
  if can_persist?
61
- hosts = array(hosts)
300
+ time = Time.new.strftime("%Y-%m-%d %H:%M:%S")
301
+ user = ENV['USER']
302
+ message = config.get(:message, 'Saving state')
62
303
 
63
- delete_remote(name)
64
- return self if hosts.empty?
304
+ user = 'UNKNOWN' unless user && ! user.empty?
305
+
306
+ git.reset({}, 'HEAD') # Clear the index so we get a clean commit
307
+
308
+ files = array(files)
309
+ git.add(files) # Get all added and updated files
310
+ git.add({ :update => true }, files) # Get all deleted files
311
+
312
+ git.commit({
313
+ :m => "#{time} by <#{user}> - #{message}",
314
+ :author => config.get(:author, false),
315
+ :allow_empty => config.get(:allow_empty, false)
316
+ })
65
317
 
66
- if @remote_dir && ! config.get(:repo, false)
67
- config[:repo] = @remote_dir
318
+ if ! parent.nil? && config.get(:propogate, true)
319
+ parent.commit(directory, config.import({
320
+ :message => "Updating submodule #{path} with: #{message}"
321
+ }))
68
322
  end
323
+ end
324
+ return self
325
+ end
326
+
327
+ #-----------------------------------------------------------------------------
328
+ # Submodule operations
329
+
330
+ attr_reader :submodules
331
+
332
+ #---
333
+
334
+ def load_submodules
335
+ @submodules = {}
336
+
337
+ if can_persist?
338
+ # Returns a Hash of { <path:String> => { 'url' => <url:String>, 'id' => <id:String> } }
339
+ # Returns {} if no .gitmodules file was found
340
+ Grit::Submodule.config(@git_lib, revision).each do |path, data|
341
+ repo = self.class.open(File.join(directory, path))
342
+ repo.set_origin(data['url']) # Just a sanity check (might disapear)
343
+
344
+ @submodules[path] = repo
345
+ end
346
+ end
347
+ return self
348
+ end
349
+ protected :load_submodules
350
+
351
+ #---
352
+
353
+ def add_submodule(path, url, revision, options = {})
354
+ if can_persist?
355
+ git.submodule({ :branch => revision }, 'add', url, path)
356
+ commit([ '.gitmodules', path ], { :message => "Adding submodule #{url} to #{path}" })
357
+ load_submodules
358
+ end
359
+ end
360
+
361
+ #---
362
+
363
+ def delete_submodule(path)
364
+ if can_persist?
365
+ submodule_key = "submodule.#{path}"
69
366
 
70
- git.add_remote(name, Git.url(hosts.shift, config[:repo], options))
367
+ delete_config(submodule_key)
368
+ delete_config(submodule_key, { :file => '.gitmodules' })
71
369
 
72
- if ! hosts.empty?
73
- remote = git.remote(name)
74
-
75
- config[:add] = true
370
+ git.rm({ :cached => true }, path)
371
+ FileUtils.rm_rf(File.join(directory, path))
372
+ FileUtils.rm_rf(File.join(git.git_dir, 'modules', path))
76
373
 
77
- hosts.each do |host|
78
- git_url = Git.url(host, config[:repo], config.options)
79
- remote.set_url(git_url, config.options)
80
- end
81
- end
374
+ commit([ '.gitmodules', path ], { :message => "Removing submodule #{url} from #{path}" })
375
+ load_submodules
82
376
  end
377
+ end
378
+
379
+ #---
380
+
381
+ def update_submodules
382
+ git.submodule({ :init => true, :recursive => true }, 'update') if can_persist?
83
383
  return self
84
384
  end
385
+ protected :update_submodules
85
386
 
86
387
  #---
87
388
 
88
- def delete_remote(name)
389
+ def foreach!
89
390
  if can_persist?
90
- remote = git.remote(name)
91
- if remote && remote.url && ! remote.url.empty?
92
- remote.remove
391
+ submodules.each do |path, repo|
392
+ yield(path, repo)
93
393
  end
94
394
  end
95
- return self
395
+ return self
96
396
  end
97
-
397
+
98
398
  #-----------------------------------------------------------------------------
99
- # Git operations
399
+ # Remote operations
100
400
 
101
- def commit(files = '.', options = {})
401
+ attr_reader :origin, :edit
402
+
403
+ #---
404
+
405
+ def init_remotes
406
+ @origin = config('remote.origin.url')
407
+ set_edit(edit_url(@origin)) unless config('remote.edit.url')
408
+ return self
409
+ end
410
+ protected :init_remotes
411
+
412
+ #---
413
+
414
+ def set_origin(url)
415
+ set_remote('origin', url)
416
+ @origin = url
417
+ return self
418
+ end
419
+
420
+ #---
421
+
422
+ def set_edit(url)
423
+ set_remote('edit', url)
424
+ @edit = url
425
+ return self
426
+ end
427
+
428
+ #---
429
+
430
+ def set_remote(name, url)
431
+ delete_remote(name)
432
+ git.remote({}, 'add', name.to_s, url) if can_persist?
433
+ return self
434
+ end
435
+
436
+ #---
437
+
438
+ def add_remote_url(name, url, options = {})
102
439
  config = Config.ensure(options)
103
440
 
104
441
  if can_persist?
105
- time = Time.new.strftime("%Y-%m-%d %H:%M:%S")
106
- user = ENV['USER']
107
- message = config.get(:message, 'Saving state')
442
+ git.remote({
443
+ :add => true,
444
+ :delete => config.get(:delete, false),
445
+ :push => config.get(:push, false)
446
+ }, 'set-url', name.to_s, url)
447
+ end
448
+ return self
449
+ end
450
+
451
+ #---
452
+
453
+ def set_host_remote(name, hosts, path, options = {})
454
+ config = Config.ensure(options)
455
+
456
+ if can_persist?
457
+ hosts = array(hosts)
458
+
459
+ return self if hosts.empty?
108
460
 
109
- config[:author] = config.get(:author, '')
110
- config[:allow_empty] = config.get(:allow_empty, false)
461
+ set_remote(name.to_s, url(hosts.shift, path, config.options))
111
462
 
112
- unless user && ! user.empty?
113
- user = 'UNKNOWN'
463
+ unless hosts.empty?
464
+ hosts.each do |host|
465
+ add_remote_url(name.to_s, url(host, path, config.options), config)
466
+ end
114
467
  end
468
+ end
469
+ return self
470
+ end
471
+
472
+ #---
473
+
474
+ def delete_remote(name)
475
+ if can_persist? && git.list_remotes.include?(name)
476
+ git.remote({}, 'rm', name.to_s)
477
+ end
478
+ return self
479
+ end
480
+
481
+ #-----------------------------------------------------------------------------
482
+ # SSH operations
483
+
484
+ def pull!(remote = :origin, options = {})
485
+ config = Config.ensure(options)
486
+ success = false
115
487
 
116
- array(files).each do |file|
117
- git.add(file) # Get all added and updated files
118
- git.add(file, { :update => true }) # Get all deleted files
488
+ if can_persist?
489
+ success = Command.new({
490
+ :command => :git,
491
+ :data => { 'git-dir=' => git.git_dir },
492
+ :subcommand => {
493
+ :command => :pull,
494
+ :flags => ( config.get(:tags, true) ? :tags : '' ),
495
+ :args => [ remote, config.get(:branch, '') ]
496
+ }
497
+ }).exec!(config) do |line|
498
+ block_given? ? yield(line) : true
499
+ end
500
+
501
+ update_submodules
502
+
503
+ if success && ! parent.nil? && config.get(:propogate, true)
504
+ parent.commit(directory, config.import({
505
+ :message => "Pulling updates for submodule #{path}",
506
+ :allow_empty => true
507
+ }))
119
508
  end
120
-
121
- git.commit("#{time} by <#{user}> - #{message}", config.options)
122
509
  end
123
- return self
510
+ return success
124
511
  end
125
512
 
126
- #-----------------------------------------------------------------------------
513
+ #---
514
+
515
+ def pull(remote = :origin, options = {})
516
+ return pull!(remote, options)
517
+ end
127
518
 
128
- def push!(remote = 'origin', options = {})
519
+ #---
520
+
521
+ def push!(remote = :edit, options = {})
129
522
  config = Config.ensure(options)
523
+ success = false
130
524
 
131
525
  if can_persist?
132
- branch = config.get(:branch, 'master')
133
- tags = config.get(:tags, false)
134
-
135
- return Coral::Command.new({
526
+ success = Command.new({
136
527
  :command => :git,
137
- :data => { 'git-dir=' => git.repo.to_s },
528
+ :data => { 'git-dir=' => git.git_dir },
138
529
  :subcommand => {
139
530
  :command => :push,
140
- :flags => ( tags ? :tags : '' ),
141
- :args => [ remote, branch ]
531
+ :flags => ( config.get(:tags, true) ? :tags : '' ),
532
+ :args => [ remote, config.get(:branch, '') ]
142
533
  }
143
534
  }).exec!(config) do |line|
144
535
  block_given? ? yield(line) : true
145
536
  end
537
+
538
+ if success && config.get(:propogate, true)
539
+ foreach! do |path, repo|
540
+ repo.push(remote, config)
541
+ end
542
+ end
146
543
  end
544
+ return success
147
545
  end
148
546
 
149
547
  #---
150
548
 
151
- def push(remote = 'origin', options = {})
549
+ def push(remote = :edit, options = {})
152
550
  return push!(remote, options)
153
- end
154
-
155
- #-----------------------------------------------------------------------------
156
- # Checks
157
-
158
- def can_persist?
159
- ensure_git
160
- return true if @git
161
- return false
162
551
  end
163
552
  end
164
553
  end