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 +4 -4
- data/README.md +17 -0
- data/lib/metaverse/base.rb +100 -13
- data/lib/metaverse/cli.rb +29 -3
- data/lib/metaverse/errors.rb +21 -0
- data/lib/metaverse/iterator.rb +4 -0
- data/lib/metaverse/repo.rb +105 -22
- data/lib/metaverse/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 708f891e0a48058bf390887594a82e99c05bd13e
|
4
|
+
data.tar.gz: 116dce5ac37a3fc4652faea5ed7978bbd89762cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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`
|
data/lib/metaverse/base.rb
CHANGED
@@ -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.
|
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
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
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|
|
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
|
data/lib/metaverse/iterator.rb
CHANGED
data/lib/metaverse/repo.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require '
|
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.
|
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
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
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
|
93
|
-
target_branch = @repo.branches["#{prefix}/#{state}"]
|
151
|
+
def clean_branch branch
|
94
152
|
|
95
|
-
changed =
|
153
|
+
changed = ahead_of_develop? branch
|
96
154
|
if not changed
|
97
|
-
|
98
|
-
@repo.branches.delete
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
data/lib/metaverse/version.rb
CHANGED
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.
|
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-
|
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
|