metaverse 0.2.rc1 → 0.2.rc2

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: 728ccb915c07d528cb4aa047d5cbfe124921733a
4
- data.tar.gz: 8b29aa1a599b73554f73730be8a66905de367af9
3
+ metadata.gz: 708f891e0a48058bf390887594a82e99c05bd13e
4
+ data.tar.gz: 116dce5ac37a3fc4652faea5ed7978bbd89762cb
5
5
  SHA512:
6
- metadata.gz: eb818e909547acce5e08080770f4675148a9397770fc437fa61fe3edb35db96f6cdae82f2705317ee7dd9b688da6894fca09f46de840c852f0a14078c69eb72d
7
- data.tar.gz: 0ded1e37a022419ddc622f0e0497350ed4c06c38d5e8fcd3fe176c13b2e9d1fa006c89a2b04f0cfbe3cfdc603f1c81fdcc83ff5abbec12655e53ab9ce019b5d1
6
+ metadata.gz: 403699eebdc051b8b59fe60bd93184f19bdc84f548b664e6daec0f273d799d13010628c051d80671062491994ed0aebafbea39bd8c440eaf4eacf2aa5860bc02
7
+ data.tar.gz: b96456e8d007bc460d197c64f8829c3f03f87b93490aab0b87e6cc137363a97857ffed6d49d15e67b9115d4afc32d5bdadd5808308c1cf35cbf3995808e47298
data/README.md CHANGED
@@ -5,6 +5,17 @@ Metaverse multirepo management revolves around two main axes of functionality:
5
5
  * Workflow related functionality
6
6
  * State Management
7
7
 
8
+ # Installation
9
+
10
+ Metaverse requires `rugged` which in turns relies on a bundled `libgit2` which
11
+ is a Ruby native extension.
12
+
13
+ ### OSX
14
+
15
+ You need Cmake and pkg-config. If you have brew you can install by running :
16
+
17
+ `brew install cmake pkg-config`
18
+
8
19
  # 1 - Workflow
9
20
  ## Utilities
10
21
  ### Checkout
@@ -31,6 +42,12 @@ Pulls the changes from the main remote in every repo - similar to `git pull orig
31
42
 
32
43
  Displays the branch at which every repo is.
33
44
 
45
+ ### Exec
46
+ `meta exec COMMAND`
47
+
48
+ Runs the specified shell command in every repo's working directory. Also passes
49
+ the ENV used to invoke it to the shell command.
50
+
34
51
  ## Features
35
52
  ### New
36
53
  `meta feature new FEATURE_NAME`
@@ -3,14 +3,21 @@ require 'colorize'
3
3
  require 'yaml'
4
4
  require 'metaverse/repo'
5
5
  require 'metaverse/iterator'
6
+ require 'metaverse/errors'
7
+ require 'transacted'
6
8
 
7
9
  module Metaverse
8
10
  class Base
9
11
  def initialize path
10
12
  @logger = Logger.new STDOUT
13
+ @base_path = path
11
14
  read_config "#{path}/.meta.yml"
12
- @repos_paths = Metaverse::Iterator.new path, @ignored_repos
13
- @repos_paths.build
15
+ @repos_paths = Metaverse::Iterator.new path, @ignored_repos, @repos_paths
16
+ if @repos_paths.empty?
17
+ @repos_paths.build
18
+ save_config "#{path}/.meta.yml"
19
+ puts "Repos cache built.".green
20
+ end
14
21
  @repos = @repos_paths.map {|repo| Metaverse::Repo.new repo}
15
22
  end
16
23
 
@@ -20,7 +27,27 @@ module Metaverse
20
27
 
21
28
 
22
29
  def checkout name
23
- @repos.each { |repo| repo.checkout name } if check_dirtiness
30
+ action_options = -> (repo) {
31
+ {
32
+ up: -> { repo.checkout name },
33
+ down: -> { repo.checkout repo.pop_previous_branch }
34
+ }
35
+ }
36
+
37
+ if check_dirtiness
38
+ actions = @repos.map {|repo|
39
+ puts "\n # #{repo.name}".blue
40
+ Transacted::Action.new action_options.call(repo)
41
+ }
42
+
43
+ checkout_transaction = Transacted::Transaction.new actions
44
+ case checkout_transaction.execute
45
+ when :execution_success then puts "Checkout successful".green
46
+ when :rollback_success then puts "An error prevented checking out the system. Rolled back successfully".yellow
47
+ when :rollback_failure then puts "An error was encountered during the checkout. In addition, an error happened while trying to rollback the system. Please fix the state of your system manually.".red
48
+ end
49
+ end
50
+
24
51
  end
25
52
 
26
53
  def create_state prefix, state
@@ -28,24 +55,46 @@ module Metaverse
28
55
  puts "\n # #{repo.name}".blue
29
56
  repo.create_state prefix, state
30
57
  repo.checkout "#{prefix}/#{state}" if not prefix == "snapshot"
31
- }
58
+ } if check_dirtiness
32
59
  end
33
60
 
34
61
 
35
- def load_state prefix, state, remote = nil
36
- @repos.each { |repo|
37
- puts "\n # #{repo.name}".blue
38
- repo.load_state prefix, state, remote
62
+ def load_state prefix, state, remote = nil, should_create_branch = false
63
+ action_options = -> (repo) {
64
+ {
65
+ up: -> {
66
+ puts "\n # #{repo.name}".blue
67
+ repo.load_state prefix, state, remote, should_create_branch
68
+ },
69
+ down: -> {
70
+ puts "\n # Rolling back in #{repo.name}".blue
71
+ puts repo.checkout repo.pop_previous_branch
72
+ }
73
+ }
39
74
  }
75
+
76
+ if check_dirtiness
77
+
78
+ actions = @repos.map { |repo|
79
+ Transacted::Action.new action_options.call repo
80
+ }
81
+
82
+ load_state_transaction = Transacted::Transaction.new actions
83
+ case load_state_transaction.execute
84
+ when :execution_success then puts "Loading state successful".green
85
+ when :rollback_success then puts "An error prevented loading the state of the system. Rolled back successfully".yellow
86
+ when :rollback_failure then puts "An error was encountered during the loading of the state. In addition, an error happened while trying to rollback the system. Please fix the state of your system manually.".red
87
+ end
88
+ end
40
89
  end
41
90
 
42
91
 
43
- def send_state prefix, state, remote
92
+ def send_state prefix, state, remote, should_clean = false
44
93
  @repos.each { |repo|
45
94
  puts "\n # #{repo.name}".blue
46
95
  is_branch = repo.current_branch.match /refs\/heads\/(.*)/
47
- repo.send_state prefix, state, remote, !!is_branch
48
- }
96
+ repo.send_state prefix, state, remote, !!is_branch, should_clean
97
+ } if check_dirtiness
49
98
  end
50
99
 
51
100
 
@@ -53,7 +102,7 @@ module Metaverse
53
102
  @repos.each { |repo|
54
103
  puts "\n # #{repo.name}".blue
55
104
  repo.update remote
56
- }
105
+ } if check_dirtiness
57
106
  end
58
107
 
59
108
 
@@ -84,14 +133,52 @@ module Metaverse
84
133
  end
85
134
 
86
135
  def read_config path
136
+ Errors::config_not_found! if not File.exist? path
137
+
87
138
  config = YAML.load File.open(path)
139
+ @repos_paths = config['repos'] || []
88
140
  @origin_remote = config['remotes']['main']
89
141
  @own_remote = config['remotes']['own']
90
142
  @ignored_repos = config['ignore'] || []
91
143
  end
92
144
 
145
+ def save_config path
146
+ config = {
147
+ 'repos' => @repos_paths.map{ |repo| repo },
148
+ 'remotes'=> {
149
+ 'main' => @origin_remote,
150
+ 'own' => @own_remote
151
+ },
152
+ 'ignore' => @ignored_repos
153
+ }
154
+ File.open(path, 'w') {|f| f.write config.to_yaml }
155
+ end
156
+
157
+ def clear_repos
158
+ @repos_paths = []
159
+ save_config "#{@base_path}/.meta.yml"
160
+ puts "Repos cache cleared.".green
161
+ end
162
+
93
163
  def pull
94
- @repos.each { |repo| repo.pull @origin_remote }
164
+ @repos.each { |repo|
165
+ puts "\n # #{repo.name}".blue
166
+ repo.pull @origin_remote
167
+ } if check_dirtiness
168
+ end
169
+
170
+ def add_remote name, base_url
171
+ @repos.each { |repo|
172
+ puts "\n # #{repo.name}".blue
173
+ repo.add_remote name, "#{base_url}/#{repo.name}"
174
+ }
175
+ end
176
+
177
+ def exec env, command
178
+ @repos.each { |repo|
179
+ puts "\n # #{repo.name}".blue
180
+ repo.exec env, command
181
+ }
95
182
  end
96
183
  end
97
184
  end
data/lib/metaverse/cli.rb CHANGED
@@ -8,6 +8,12 @@ module Metaverse
8
8
  @meta = Base.new Dir.pwd
9
9
  end
10
10
 
11
+ desc "reset", "Empties the repos cache"
12
+ def reset
13
+ init
14
+ @meta.clear_repos
15
+ end
16
+
11
17
  desc "branches", "Displays the branch in which each repo is"
12
18
  def branches
13
19
  init
@@ -25,34 +31,40 @@ module Metaverse
25
31
  end
26
32
 
27
33
  desc "feature [COMMAND] [FEATURE_NAME]", "Creates a new feature branch or loads a previous one"
34
+ option :branch, type: :boolean, aliases: '-b'
28
35
  def feature command, name, remote = nil
29
36
  init
30
37
  case command
31
38
  when 'new' then @meta.create_state 'feature', name
32
- when 'load' then @meta.load_state 'feature', name, remote
39
+ when 'load' then @meta.load_state 'feature', name, remote, options[:branch]
33
40
  when 'send' then @meta.send_state 'feature', name, remote
41
+ when 'close' then @meta.send_state 'feature', name, remote, true
34
42
  end
35
43
  end
36
44
 
37
45
 
38
46
  desc "release [COMMAND] [FEATURE_NAME]", "Creates a new release branch or loads a previous one"
47
+ option :branch, type: :boolean, aliases: '-b'
39
48
  def release command, name, remote = nil
40
49
  init
41
50
  case command
42
51
  when 'new' then @meta.create_state 'release', name
43
- when 'load' then @meta.load_state 'release', name, remote
52
+ when 'load' then @meta.load_state 'release', name, remote, options[:branch]
44
53
  when 'send' then @meta.send_state 'release', name, remote
54
+ when 'close' then @meta.send_state 'release', name, remote, true
45
55
  end
46
56
  end
47
57
 
48
58
 
49
59
  desc "bugfix [COMMAND] [FEATURE_NAME]", "Creates a new bugfix branch or loads a previous one"
60
+ option :branch, type: :boolean, aliases: '-b'
50
61
  def bugfix command, name, remote = nil
51
62
  init
52
63
  case command
53
64
  when 'new' then @meta.create_state 'bugfix', name
54
- when 'load' then @meta.load_state 'bugfix', name, remote
65
+ when 'load' then @meta.load_state 'bugfix', name, remote, options[:branch]
55
66
  when 'send' then @meta.send_state 'bugfix', name, remote
67
+ when 'close' then @meta.send_state 'bugfix', name, remote, true
56
68
  end
57
69
  end
58
70
 
@@ -94,5 +106,19 @@ module Metaverse
94
106
  init
95
107
  @meta.pull
96
108
  end
109
+
110
+ desc "remote [COMMAND]", "manages repos remotes such as adding new ones"
111
+ def remote command, name, url
112
+ init
113
+ case command
114
+ when 'add' then @meta.add_remote name, url
115
+ end
116
+ end
117
+
118
+ desc "exec [COMMAND]", "runs the specified shell command in every repo"
119
+ def exec *command
120
+ init
121
+ @meta.exec ENV, command.join(' ')
122
+ end
97
123
  end
98
124
  end
@@ -0,0 +1,21 @@
1
+ require 'colorize'
2
+
3
+ module Metaverse
4
+ class Errors
5
+ def self.config_not_found!
6
+ abort ".meta.yml file was not found in the current working directory. Exiting ...".red
7
+ end
8
+
9
+ def self.ref_not_found ref
10
+ return Exception.new "Reference '#{ref}' does not exist.".red
11
+ end
12
+
13
+ def self.remote_not_found remote
14
+ puts "Remote '#{remote}' does not exist. Skipping ...".red
15
+ end
16
+
17
+ def self.remote_exists remote
18
+ puts "Remote '#{remote}' exists already. Skipping ...".red
19
+ end
20
+ end
21
+ end
@@ -37,5 +37,9 @@ module Metaverse
37
37
  def map &block
38
38
  @repos.send(:map, &block)
39
39
  end
40
+
41
+ def empty?
42
+ @repos.count == 0
43
+ end
40
44
  end
41
45
  end
@@ -1,4 +1,5 @@
1
- require 'metaverse/git'
1
+ require 'rugged'
2
+ require 'iniparse'
2
3
 
3
4
  module Metaverse
4
5
  class Repo
@@ -8,6 +9,7 @@ module Metaverse
8
9
 
9
10
  def initialize path
10
11
  load path
12
+ @previous_branches = []
11
13
  end
12
14
 
13
15
 
@@ -26,16 +28,46 @@ module Metaverse
26
28
  File.basename @repo.workdir
27
29
  end
28
30
 
31
+ def workdir
32
+ @repo.workdir
33
+ end
29
34
 
30
35
  def checkout ref
31
- @repo.checkout ref
36
+ if @repo.branches[ref].nil?
37
+ is_reference = false
38
+ if Rugged::Reference.valid_name?(ref) and @repo.references[ref].nil?
39
+ raise Errors::ref_not_found ref
40
+ end
41
+ end
42
+
43
+ @previous_branches.push current_branch
44
+ Dir.chdir(@repo.workdir) do
45
+ `git checkout #{ref}`#TODO: find out what's going on, check out a branch
46
+ end
32
47
  end
33
48
 
34
49
 
35
50
  def current_branch
36
- @repo.head.name
51
+ branch_name = ''
52
+
53
+ Dir.chdir(@repo.workdir) do
54
+ branch_name = `git branch | sed -n '/\* /s///p'`
55
+ end
56
+
57
+ if branch_name.include? "detached"
58
+ return /[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+/.match(branch_name)[0]
59
+ end
60
+
61
+ branch_name.strip
37
62
  end
38
63
 
64
+ def peek_previous_branch
65
+ @previous_branches.last
66
+ end
67
+
68
+ def pop_previous_branch
69
+ @previous_branches.pop
70
+ end
39
71
 
40
72
  def head
41
73
  @repo.head
@@ -68,34 +100,60 @@ module Metaverse
68
100
  end
69
101
 
70
102
 
71
- def load_state prefix, state, remote = nil
72
- path_prefix = "refs/meta"
73
- path_prefix += "/local" if remote.nil?
74
- path_prefix += "/remotes/#{remote}" if not remote.nil?
75
- ref_name = "/#{prefix}/#{state}"
76
- @repo.checkout path_prefix + ref_name
103
+ def load_state prefix, state, remote = nil, should_create_branch = false
104
+ ref_name = "refs/meta"
105
+ ref_name += remote.nil? ? "/local" : "/remotes/#{remote}"
106
+ ref_name += "/#{prefix}/#{state}"
107
+ if should_create_branch
108
+ branch_name = "#{prefix}/#{state}"
109
+ # TODO: Decide what to do when a branch exists previous. Should we :
110
+ # - Replace it's ref by the saved state ? ( We lose the previous branch reference)
111
+ # - Load it as it is ( We may get an inconsistent state since the branch could point to a different commit than the state reference )
112
+ if @repo.branches[branch_name].nil?
113
+ puts "Creating branch #{branch_name} based on #{ref_name}"
114
+ @repo.create_branch branch_name, ref_name
115
+ end
116
+ return checkout branch_name
117
+ end
118
+ checkout ref_name
77
119
  end
78
120
 
79
121
 
80
- def send_state prefix, state, remote, is_branch
122
+ def send_state prefix, state, remote, is_branch, should_clean = false
81
123
  refs = ["refs/meta/local/#{prefix}/#{state}"]
82
- if (not prefix == 'snapshot') and is_branch and clean_branch prefix, state
83
- refs << "#{prefix}/#{state}"
84
- update_ref refs[0], @repo.head
124
+ branch_name = "#{prefix}/#{state}"
125
+ branch = @repo.branches[branch_name]
126
+ develop = @repo.branches['develop']
127
+
128
+ if branch.nil?
129
+ Errors::ref_not_found branch_name
130
+ return false
85
131
  end
132
+
133
+ if develop.nil?
134
+ Errors::ref_not_found 'develop'
135
+ return false
136
+ end
137
+
138
+ if (not prefix == 'snapshot') and is_branch and ahead_of_develop? branch
139
+ refs << branch_name
140
+ update_ref refs[0], @repo.branches[branch_name]
141
+ end
142
+
143
+ clean_branch branch if should_clean
144
+
86
145
  Dir.chdir(@repo.workdir) do
87
146
  `git push #{remote} #{refs.join(' ')}`
88
147
  end
89
148
  end
90
149
 
91
150
 
92
- def clean_branch prefix, state
93
- target_branch = @repo.branches["#{prefix}/#{state}"]
151
+ def clean_branch branch
94
152
 
95
- changed = has_changes? target_branch, @repo.branches['develop']
153
+ changed = ahead_of_develop? branch
96
154
  if not changed
97
- @repo.checkout 'develop'
98
- @repo.branches.delete target_branch
155
+ checkout 'develop'
156
+ @repo.branches.delete branch
99
157
  end
100
158
  changed
101
159
  end
@@ -103,13 +161,17 @@ module Metaverse
103
161
  def dirty?
104
162
  is_dirty = false
105
163
  @repo.status { |file, data|
106
- is_dirty = !(data.include? :ignored) or is_dirty
164
+ next if data == [:ignored]
165
+ is_dirty = true
107
166
  }
108
167
  is_dirty
109
168
  end
110
169
 
111
170
 
112
171
  def update remote = nil
172
+ if @repo.remotes[remote].nil?
173
+ return Errors::remote_not_found remote
174
+ end
113
175
  Dir.chdir(@repo.workdir) do
114
176
  `git remote update #{remote}`
115
177
  end
@@ -117,10 +179,11 @@ module Metaverse
117
179
 
118
180
 
119
181
  def add_option_to_remote remote, value, key
120
- cfg = IniParse.parse(File.read("#{@repo.path}/config"))
182
+ config_path = "#{@repo.path}config"
183
+ cfg = IniParse.parse(File.read(config_path))
121
184
  if not [*cfg["remote \"#{remote}\""][key]].include? value
122
185
  cfg["remote \"#{remote}\""][key] = [*cfg["remote \"#{remote}\""][key], value]
123
- cfg.save "#{@path}/config"
186
+ cfg.save config_path
124
187
  end
125
188
  end
126
189
 
@@ -130,8 +193,28 @@ module Metaverse
130
193
  end
131
194
 
132
195
 
196
+ def ahead_of_develop? branch
197
+ has_changes? branch, @repo.branches['develop']
198
+ end
199
+
133
200
  def pull remote
134
- `git pull #{remote}`
201
+ if @repo.remotes[remote].nil?
202
+ return Errors::remote_not_found remote
203
+ end
204
+ Dir.chdir(@repo.workdir) do
205
+ `git pull #{remote} #{current_branch}`
206
+ end
207
+ end
208
+
209
+ def add_remote name, url
210
+ return Errors::remote_exists name if not @repo.remotes[name].nil?
211
+ @repo.remotes.create name, url
212
+ end
213
+
214
+ def exec env, command
215
+ Dir.chdir(@repo.workdir) do
216
+ system env, command
217
+ end
135
218
  end
136
219
  end
137
220
  end
@@ -1,3 +1,3 @@
1
1
  module Metaverse
2
- VERSION = "0.2.rc1"
2
+ VERSION = "0.2.rc2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metaverse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.rc1
4
+ version: 0.2.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Omar Kamali
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-26 00:00:00.000000000 Z
11
+ date: 2015-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -115,6 +115,7 @@ files:
115
115
  - lib/metaverse.rb
116
116
  - lib/metaverse/base.rb
117
117
  - lib/metaverse/cli.rb
118
+ - lib/metaverse/errors.rb
118
119
  - lib/metaverse/iterator.rb
119
120
  - lib/metaverse/repo.rb
120
121
  - lib/metaverse/version.rb