metaverse 0.1.7 → 0.1.8

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: 16a16d6aa3128a72012365e90dc57f3bdd82ec08
4
- data.tar.gz: 31fa51d1f81da1f724ad5e1040a18dd66ea5774b
3
+ metadata.gz: 69efcf0bc6d8443154da8f032a5f86442cf98da7
4
+ data.tar.gz: c60b16984a090cb2fb1508f902cb55db3994bd0d
5
5
  SHA512:
6
- metadata.gz: 138410c47d4d78ca106ecf2583a410ba4d894b3ace9a09791dd36b0cbb31fada4c436ffd66fb6d03fd0751f0dc7f835781394810a2281c876378d5a1ca1239d1
7
- data.tar.gz: 78e2cc25d28aa65a59fff90811e1235eb438f90533b42151a61ccb62fe7bcea4c97f96f90e9f4043c109dd2eef274f8f1fc47a0c803c48d004e8cf426e1de5b8
6
+ metadata.gz: 85da87fd9f3f070243eb859a55bf56f97250853830f3df18c62eb5d98bd36465e05fa07b3b1f7a69ffdd5ac4bf25340d4f4bb8d765283b7d6a2542be77980c69
7
+ data.tar.gz: 24f9fbcb26df13afea52723773765a8cf3d5df6bd8cf8aaa7a1a154d0262e2f07d3591793b2eb46a20ce93167b3851091ed6a1dfd749753d2d647a82e602b8a8
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in metaverse.gemspec
4
4
  gemspec
5
+ gem 'iniparse', git: "git://github.com/dejitaiza/iniparse.git"
data/README.md CHANGED
@@ -1,39 +1,126 @@
1
1
  # Metaverse
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/metaverse`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Metaverse multirepo management revolves around two main axes of functionality:
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ * Workflow related functionality
6
+ * State Management
6
7
 
7
- ## Installation
8
+ # Installation
8
9
 
9
- Add this line to your application's Gemfile:
10
+ Metaverse requires `rugged` which in turns relies on a bundled `libgit2` which
11
+ is a Ruby native extension.
10
12
 
11
- ```ruby
12
- gem 'metaverse'
13
- ```
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
+
19
+ # 1 - Workflow
20
+ ## Utilities
21
+ ### Checkout
22
+ `meta checkout REF_OR_SHA`
23
+
24
+ Checks out every repo to `REF_OR_SHA` - similar to `git checkout REF_OR_SHA`
25
+ ### Develop
26
+ `meta develop`
27
+
28
+ Checks out every repo to `develop` - ( shortcut for `meta checkout develop` )
29
+ ### Update
30
+ `meta update REMOTE_NAME`
31
+
32
+ Fetches from `REMOTE_NAME` in every repo - similar to `git remote update REMOTE_NAME`
33
+ If `REMOTE_NAME` is omitted, defaults to fetching from all the remotes
34
+
35
+ ### Pull
36
+ `meta pull`
37
+
38
+ Pulls the changes from the main remote in every repo - similar to `git pull origin`
39
+
40
+ ### Branches
41
+ `meta branches`
42
+
43
+ Displays the branch at which every repo is.
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
+
51
+ ## Features
52
+ ### New
53
+ `meta feature new FEATURE_NAME`
14
54
 
15
- And then execute:
55
+ Creates a feature branch named `feature/FEATURE_NAME` on every repo managed by metaverse.
16
56
 
17
- $ bundle
57
+ ### Load
58
+ `meta feature load FEATURE_NAME REMOTE_NAME`
18
59
 
19
- Or install it yourself as:
60
+ Checks out every repo to `feature/FEATURE_NAME` in a **detached HEAD** mode.
61
+ If the remote is set, tries to load the feature from that specific remote. Else, defaults to local features.
20
62
 
21
- $ gem install metaverse
63
+ ### Send
64
+ `meta feature send FEATURE_NAME REMOTE_NAME`
22
65
 
23
- ## Usage
66
+ Pushes the feature branch of the repos that have changes to the specified remote.
67
+ It also automatically delete the branches that have no changes compared to develop.
24
68
 
25
- TODO: Write usage instructions here
69
+ ## Releases and bugfixes
26
70
 
27
- ## Development
71
+ Releases and bugfixes follow the same structure than the features, except that they are executed respectively using `meta release` and `meta bugtfix`.
28
72
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
73
+ # 2 - State Management
74
+ ## Sanity checks
75
+ meta can be used to perform some sanity checks on the state of the system.
30
76
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
77
+ ### Dirtiness
78
+ `meta check dirtiness`
32
79
 
33
- ## Contributing
80
+ Checks if the system is dirty.
81
+ The system is considered dirty if a repo contains unstaged or uncommitted changes ( think about updating your `.gitignore`s :) )
82
+ In such a case, it displays the list of affected repos.
34
83
 
35
- 1. Fork it ( https://github.com/[my-github-username]/metaverse/fork )
36
- 2. Create your feature branch (`git checkout -b my-new-feature`)
37
- 3. Commit your changes (`git commit -am 'Add some feature'`)
38
- 4. Push to the branch (`git push origin my-new-feature`)
39
- 5. Create a new Pull Request
84
+ ### Status
85
+ ` meta status`
86
+
87
+ Checks the status of the system by running a dirtiness check, displays the output and the current state of the system.
88
+ The branch at which the system is is deduced by counting the occurrences of every branch and picking the most frequent one.
89
+
90
+ ## System state
91
+ meta provides a set of features aimed toward taking a snapshot of the system, and reloading it afterwards.
92
+
93
+ ### How does it work ?
94
+ Snapshots are stored in a separate ref namespace in order to avoid collision with branches. Snapshots' refs in particular follow this refspec : `refs/meta/local/snapshot/SNAPSHOT_NAME`
95
+ Similarly, refs are also used in addition to branches, to load features, releases, and bugfixes.
96
+
97
+ ### New
98
+ `meta snapshot new SNAPSHOT_NAME`
99
+
100
+ Take a snapshot of the system. The system stays in the same branch where it was before.
101
+
102
+ ### Load
103
+ `meta snapshot load SNAPSHOT_NAME`
104
+
105
+ Loads a previous snapshot of the system in a git **detached HEAD** mode. A good practice would be to create a feature, a release or a bugfix branch after loading a snapshot, prior to any modification on it.
106
+
107
+ ### Send
108
+ `meta snapshot send SNAPSHOT_NAME REMOTE_NAME`
109
+
110
+ Pushes a snapshot to the specified remote.
111
+
112
+ # Configuration
113
+
114
+ meta stores it's configuration in a file named .meta.yml in the root folder of your workspace.
115
+
116
+ This is an example of a `.meta.yml` file :
117
+
118
+ ```
119
+ remotes:
120
+ own: YOUR_OWN_REMOTE
121
+ main: YOUR_MAIN_REMOTE
122
+ ignore:
123
+ - A_REPO_TO_IGNORE
124
+ - A_REPO_TO_IGNORE
125
+ - ..
126
+ ```
data/bin/meta CHANGED
@@ -1,12 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  begin
4
- require "bundler/setup"
5
- require 'metaverse'
4
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'metaverse'))
6
5
  rescue LoadError
7
- require "bundler/setup"
8
6
  require 'rubygems'
9
- require 'metaverse'
7
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'metaverse'))
10
8
  end
11
9
 
12
10
  Metaverse::Cli.start
@@ -1,140 +1,190 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'colorize'
3
3
  require 'yaml'
4
+ require 'transacted'
4
5
  require 'metaverse/repo'
5
6
  require 'metaverse/iterator'
7
+ require 'metaverse/errors'
6
8
 
7
9
  module Metaverse
8
10
  class Base
9
11
  def initialize path
10
12
  @logger = Logger.new STDOUT
11
- readConfig "#{path}/.meta.yml"
12
- @reposPaths = Metaverse::Iterator.new path, @ignoredRepos
13
- @reposPaths.build
14
- @repos = @reposPaths.map {|repo| Metaverse::Repo.new repo}
13
+ @base_path = path
14
+ read_config "#{path}/.meta.yml"
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
21
+ @repos = @repos_paths.map {|repo| Metaverse::Repo.new repo}
15
22
  end
16
23
 
17
24
  def status
18
- checkConsistency
19
- checkDirtiness
25
+ check_dirtiness
20
26
  end
21
27
 
22
- def repos
23
- @repos
24
- end
25
28
 
26
29
  def checkout name
27
- @repos.each { |repo| repo.checkout name } if checkConsistency && checkDirtiness
28
- end
29
-
30
- def createSnapshot name
31
- @repos.each { |repo| repo.tag name } if checkConsistency && checkDirtiness
32
- end
33
-
34
- def loadSnapshot name
35
- @repos.each { |repo| repo.checkout "tags/#{name}" } if checkConsistency && checkDirtiness
30
+ action_options = -> (repo) {
31
+ {
32
+ up: -> {
33
+ puts "\n # #{repo.name}".blue
34
+ repo.checkout name
35
+ },
36
+ down: -> {
37
+ puts "\n # Rolling back in #{repo.name}".blue
38
+ repo.checkout repo.pop_previous_branch
39
+ }
40
+ }
41
+ }
42
+
43
+ if check_dirtiness
44
+ actions = @repos.map {|repo|
45
+ puts "\n # #{repo.name}".blue
46
+ Transacted::Action.new action_options.call(repo)
47
+ }
48
+
49
+ checkout_transaction = Transacted::Transaction.new actions
50
+ case checkout_transaction.execute
51
+ when :execution_success then puts "Checkout successful".green
52
+ when :rollback_success then puts "An error prevented checking out the system. Rolled back successfully".yellow
53
+ 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
54
+ end
55
+ end
56
+
36
57
  end
37
58
 
38
- def createFeature name
39
- branchName = "feature/#{name}"
59
+ def create_state prefix, state
40
60
  @repos.each { |repo|
41
61
  puts "\n # #{repo.name}".blue
42
- repo.branch branchName
43
- repo.checkout branchName
44
- } if checkConsistency && checkDirtiness
62
+ repo.create_state prefix, state
63
+ repo.checkout "#{prefix}/#{state}" if not prefix == "snapshot"
64
+ } if check_dirtiness
65
+ end
66
+
67
+
68
+ def load_state prefix, state, remote = nil, should_create_branch = false
69
+ action_options = -> (repo) {
70
+ {
71
+ up: -> {
72
+ puts "\n # #{repo.name}".blue
73
+ repo.load_state prefix, state, remote, should_create_branch
74
+ },
75
+ down: -> {
76
+ puts "\n # Rolling back in #{repo.name}".blue
77
+ puts repo.checkout repo.pop_previous_branch
78
+ }
79
+ }
80
+ }
81
+
82
+ if check_dirtiness
83
+
84
+ actions = @repos.map { |repo|
85
+ Transacted::Action.new action_options.call repo
86
+ }
87
+
88
+ load_state_transaction = Transacted::Transaction.new actions
89
+ case load_state_transaction.execute
90
+ when :execution_success then puts "Loading state successful".green
91
+ when :rollback_success then puts "An error prevented loading the state of the system. Rolled back successfully".yellow
92
+ 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
93
+ end
94
+ end
45
95
  end
46
96
 
47
- def loadFeature name
48
- @repos.each { |repo|
49
- puts "\n # #{repo.name}".blue
50
- repo.checkout "feature/#{name}"
51
- } if checkConsistency && checkDirtiness
52
- end
53
97
 
54
- def pushFeature name
98
+ def send_state prefix, state, remote, should_clean = false
55
99
  @repos.each { |repo|
56
100
  puts "\n # #{repo.name}".blue
57
- repo.push @ownRemote, "feature/#{name}"
58
- } if checkConsistency && checkDirtiness
101
+ is_branch = repo.current_branch.match /refs\/heads\/(.*)/
102
+ repo.send_state prefix, state, remote, !!is_branch, should_clean
103
+ } if check_dirtiness
59
104
  end
60
105
 
61
- def getBranches
62
- @repos.each { |repo| repo.branch }
63
- end
64
106
 
65
- def update remote
107
+ def update remote = nil
66
108
  @repos.each { |repo|
67
109
  puts "\n # #{repo.name}".blue
68
- repo.fetch "#{remote || @originRemote}"
69
- repo.reset "#{remote || @originRemote}/#{systemState[0]}"
70
- } if checkConsistency && checkDirtiness
71
- end
72
-
73
- def deploy state = nil
74
- @repos.each { |repo|
75
- if state === nil
76
- state = repo.currentBranch
77
- end
78
- repo.checkout state
79
- repo.deploy state
80
- } if checkConsistency && checkDirtiness
110
+ repo.update remote
111
+ } if check_dirtiness
81
112
  end
82
113
 
83
- def diff branch = nil
84
- if branch === nil
85
- branch = 'develop'
86
- end
87
-
88
- @repos.each { |repo| repo.countDiffBranches branch, repo.currentBranch}
89
- end
90
114
 
91
115
  def branches
92
- @repos.each { |repo| puts "#{repo.name}@#{repo.currentBranch}" }
116
+ @repos.each { |repo| puts "#{repo.name}@#{repo.current_branch}" }
93
117
  end
94
118
 
95
- def systemState
119
+ def system_state
96
120
  branches = Hash.new 0
97
- @repos.each {|repo| branches[repo.currentBranch] += 1}
121
+ @repos.each {|repo| branches[repo.current_branch] += 1}
98
122
 
99
123
  state = branches.max{|a,b| a[1] <=> b[1]}
100
124
  state[0] = state.first.strip
101
125
  state
102
126
  end
103
127
 
104
- def checkConsistency
105
- currentBranch = systemState
106
- if currentBranch.last != @repos.length
107
- branchName = currentBranch.first
108
- inconsistentRepos = @repos.reject { |repo|
109
- repo.currentBranch == branchName
110
- }.map &:name
111
-
112
- puts 'The system is in an inconsistent state. Please check the following repos :'.red, inconsistentRepos
113
- return false
114
- end
115
- puts 'The system is consistent'.green
116
- true
117
-
118
- end
119
-
120
- def checkDirtiness
121
- dirtyRepos = @repos.reject { |repo|
128
+ def check_dirtiness
129
+ dirty_repos = @repos.reject { |repo|
122
130
  !repo.dirty?
123
131
  }.map &:name
124
132
 
125
- if dirtyRepos.length == 0
133
+ if dirty_repos.length == 0
126
134
  puts 'The system is clean'.green
127
135
  else
128
- puts 'The system is dirty. Please check the following repos :'.red , dirtyRepos
136
+ puts 'The system is dirty. Please check the following repos :'.red , dirty_repos
129
137
  end
130
- dirtyRepos.length == 0
138
+ dirty_repos.length == 0
131
139
  end
132
140
 
133
- def readConfig path
141
+ def read_config path
142
+ Errors::config_not_found! if not File.exist? path
143
+
134
144
  config = YAML.load File.open(path)
135
- @originRemote = config['remotes']['main']
136
- @ownRemote = config['remotes']['own']
137
- @ignoredRepos = config['ignore'] || []
145
+ @repos_paths = config['repos'] || []
146
+ @origin_remote = config['remotes']['main']
147
+ @own_remote = config['remotes']['own']
148
+ @ignored_repos = config['ignore'] || []
149
+ end
150
+
151
+ def save_config path
152
+ config = {
153
+ 'repos' => @repos_paths.map{ |repo| repo },
154
+ 'remotes'=> {
155
+ 'main' => @origin_remote,
156
+ 'own' => @own_remote
157
+ },
158
+ 'ignore' => @ignored_repos
159
+ }
160
+ File.open(path, 'w') {|f| f.write config.to_yaml }
161
+ end
162
+
163
+ def clear_repos
164
+ @repos_paths = []
165
+ save_config "#{@base_path}/.meta.yml"
166
+ puts "Repos cache cleared.".green
167
+ end
168
+
169
+ def pull
170
+ @repos.each { |repo|
171
+ puts "\n # #{repo.name}".blue
172
+ repo.pull @origin_remote
173
+ } if check_dirtiness
174
+ end
175
+
176
+ def add_remote name, base_url
177
+ @repos.each { |repo|
178
+ puts "\n # #{repo.name}".blue
179
+ repo.add_remote name, "#{base_url}/#{repo.name}"
180
+ }
181
+ end
182
+
183
+ def exec env, command
184
+ @repos.each { |repo|
185
+ puts "\n # #{repo.name}".blue
186
+ repo.exec env, command
187
+ }
138
188
  end
139
189
  end
140
190
  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
@@ -15,34 +21,61 @@ module Metaverse
15
21
  end
16
22
 
17
23
  desc "snapshot [COMMAND] [SNAPSHOT_NAME]", "Creates or loads a snapshot of the system"
18
- def snapshot command, name
24
+ def snapshot command, name, remote = nil
19
25
  init
20
26
  case command
21
- when 'new' then @meta.createSnapshot name
22
- when 'load' then @meta.loadSnapshot name
23
- when 'deploy' then @meta.deploy "tags/#{name}"
27
+ when 'new' then @meta.create_state 'snapshot', name
28
+ when 'load' then @meta.load_state 'snapshot', name, remote
29
+ when 'send' then @meta.send_state 'snapshot', name, remote
24
30
  end
25
31
  end
26
32
 
27
33
  desc "feature [COMMAND] [FEATURE_NAME]", "Creates a new feature branch or loads a previous one"
28
- def feature command, name
34
+ option :branch, type: :boolean, aliases: '-b'
35
+ def feature command, name, remote = nil
36
+ init
37
+ case command
38
+ when 'new' then @meta.create_state 'feature', name
39
+ when 'load' then @meta.load_state 'feature', name, remote, options[:branch]
40
+ when 'send' then @meta.send_state 'feature', name, remote
41
+ when 'close' then @meta.send_state 'feature', name, remote, true
42
+ end
43
+ end
44
+
45
+
46
+ desc "release [COMMAND] [FEATURE_NAME]", "Creates a new release branch or loads a previous one"
47
+ option :branch, type: :boolean, aliases: '-b'
48
+ def release command, name, remote = nil
49
+ init
50
+ case command
51
+ when 'new' then @meta.create_state 'release', name
52
+ when 'load' then @meta.load_state 'release', name, remote, options[:branch]
53
+ when 'send' then @meta.send_state 'release', name, remote
54
+ when 'close' then @meta.send_state 'release', name, remote, true
55
+ end
56
+ end
57
+
58
+
59
+ desc "bugfix [COMMAND] [FEATURE_NAME]", "Creates a new bugfix branch or loads a previous one"
60
+ option :branch, type: :boolean, aliases: '-b'
61
+ def bugfix command, name, remote = nil
29
62
  init
30
63
  case command
31
- when 'new' then @meta.createFeature name
32
- when 'load' then @meta.loadFeature name
33
- when 'push' then @meta.pushFeature name
34
- when 'deploy' then @meta.deploy "feature/#{name}"
64
+ when 'new' then @meta.create_state 'bugfix', name
65
+ when 'load' then @meta.load_state 'bugfix', name, remote, options[:branch]
66
+ when 'send' then @meta.send_state 'bugfix', name, remote
67
+ when 'close' then @meta.send_state 'bugfix', name, remote, true
35
68
  end
36
69
  end
37
70
 
38
71
  desc "status", "Displays the status of the system"
39
72
  def status
40
73
  init
41
- puts "System is at #{@meta.systemState.first}"
74
+ puts "System is at #{@meta.system_state.first}"
42
75
  @meta.status
43
76
  end
44
77
 
45
- desc "update [REMOTE_NAME]", "Pulls the latest code corresponding to the current branch. Remote is optional"
78
+ desc "update [REMOTE_NAME]", "Fetches the latest code corresponding to the current branch. Remote is optional"
46
79
  def update remote = nil
47
80
  init
48
81
  @meta.update remote
@@ -64,9 +97,28 @@ module Metaverse
64
97
  def check command
65
98
  init
66
99
  case command
67
- when 'consistency' then @meta.checkConsistency
68
- when 'dirtiness' then @meta.checkDirtiness
100
+ when 'dirtiness' then @meta.check_dirtiness
69
101
  end
70
102
  end
103
+
104
+ desc "pull", "Pulls the latest changes from the main origin"
105
+ def pull
106
+ init
107
+ @meta.pull
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
71
123
  end
72
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
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env ruby
2
-
3
1
  require 'find'
4
2
  require 'logger'
5
3
 
@@ -39,5 +37,9 @@ module Metaverse
39
37
  def map &block
40
38
  @repos.send(:map, &block)
41
39
  end
40
+
41
+ def empty?
42
+ @repos.count == 0
43
+ end
42
44
  end
43
45
  end
@@ -1,18 +1,224 @@
1
- require 'metaverse/git'
1
+ require 'rugged'
2
+ require 'iniparse'
2
3
 
3
4
  module Metaverse
4
5
  class Repo
5
- include Git
6
-
6
+
7
+ # Initialize a git repository
8
+ # path - An absolute path to the repo root folder
9
+
7
10
  def initialize path
8
11
  load path
12
+ @previous_branches = []
13
+ end
14
+
15
+
16
+ def load path
17
+ @repo = Rugged::Repository.new(path)
18
+ @repo.remotes.each_name do |remote|
19
+ fetch_refspec = "+refs/meta/local/*:refs/meta/remotes/#{remote}/*"
20
+ push_refspec = "+refs/meta/local/*:refs/meta/local/*"
21
+ add_option_to_remote remote, fetch_refspec, 'fetch'
22
+ add_option_to_remote remote, push_refspec, 'push'
23
+ end
24
+ end
25
+
26
+
27
+ def name
28
+ File.basename @repo.workdir
29
+ end
30
+
31
+ def workdir
32
+ @repo.workdir
33
+ end
34
+
35
+ def 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
47
+ end
48
+
49
+
50
+ def current_branch
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
+ match = /[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+/.match(branch_name)
59
+ if not match.nil?
60
+ return match[0]
61
+ end
62
+ return 'HEAD'
63
+ end
64
+
65
+ branch_name.strip
66
+ end
67
+
68
+ def peek_previous_branch
69
+ @previous_branches.last
70
+ end
71
+
72
+ def pop_previous_branch
73
+ @previous_branches.pop
74
+ end
75
+
76
+ def head
77
+ @repo.head
78
+ end
79
+
80
+
81
+ def branches
82
+ @repo.branches
83
+ end
84
+
85
+
86
+ def create_ref ref_name, ref_target
87
+ @repo.references.create ref_name, ref_target.target_id
88
+ end
89
+
90
+
91
+ def update_ref ref_name, ref_target
92
+ @repo.references.update ref_name, ref_target.target_id
93
+ end
94
+
95
+
96
+ def create_state prefix, state
97
+ ref_target = @repo.head
98
+ if not prefix == 'snapshot'
99
+ branch_name = "#{prefix}/#{state}"
100
+ ref_target = @repo.create_branch branch_name
101
+ end
102
+ ref_name = "refs/meta/local/#{prefix}/#{state}"
103
+ create_ref ref_name, ref_target
9
104
  end
10
105
 
11
- def deploy feature
12
- Dir.chdir(@path) do
13
- puts `FEATURE_DOMAIN=#{feature} gulp deploy:testing`
106
+
107
+ def load_state prefix, state, remote = nil, should_create_branch = false
108
+ ref_name = "refs/meta"
109
+ ref_name += remote.nil? ? "/local" : "/remotes/#{remote}"
110
+ ref_name += "/#{prefix}/#{state}"
111
+ if should_create_branch
112
+ branch_name = "#{prefix}/#{state}"
113
+ # TODO: Decide what to do when a branch exists previous. Should we :
114
+ # - Replace it's ref by the saved state ? ( We lose the previous branch reference)
115
+ # - Load it as it is ( We may get an inconsistent state since the branch could point to a different commit than the state reference )
116
+ if @repo.branches[branch_name].nil?
117
+ puts "Creating branch #{branch_name} based on #{ref_name}"
118
+ @repo.create_branch branch_name, ref_name
119
+ end
120
+ return checkout branch_name
121
+ end
122
+ checkout ref_name
123
+ end
124
+
125
+
126
+ def send_state prefix, state, remote, is_branch, should_clean = false
127
+ refs = ["refs/meta/local/#{prefix}/#{state}"]
128
+ branch_name = "#{prefix}/#{state}"
129
+ branch = @repo.branches[branch_name]
130
+ develop = @repo.branches['develop']
131
+
132
+ if branch.nil?
133
+ Errors::ref_not_found branch_name
134
+ return false
135
+ end
136
+
137
+ if develop.nil?
138
+ Errors::ref_not_found 'develop'
139
+ return false
140
+ end
141
+
142
+ if (not prefix == 'snapshot') and is_branch and ahead_of_develop? branch
143
+ refs << branch_name
144
+ update_ref refs[0], @repo.branches[branch_name]
145
+ end
146
+
147
+ clean_branch branch if should_clean
148
+
149
+ Dir.chdir(@repo.workdir) do
150
+ `git push #{remote} #{refs.join(' ')}`
14
151
  end
15
152
  end
16
153
 
154
+
155
+ def clean_branch branch
156
+
157
+ changed = ahead_of_develop? branch
158
+ if not changed
159
+ checkout 'develop'
160
+ @repo.branches.delete branch
161
+ end
162
+ changed
163
+ end
164
+
165
+ def dirty?
166
+ is_dirty = false
167
+ @repo.status { |file, data|
168
+ next if data == [:ignored]
169
+ is_dirty = true
170
+ }
171
+ is_dirty
172
+ end
173
+
174
+
175
+ def update remote = nil
176
+ if @repo.remotes[remote].nil?
177
+ return Errors::remote_not_found remote
178
+ end
179
+ Dir.chdir(@repo.workdir) do
180
+ `git remote update #{remote}`
181
+ end
182
+ end
183
+
184
+
185
+ def add_option_to_remote remote, value, key
186
+ config_path = "#{@repo.path}config"
187
+ cfg = IniParse.parse(File.read(config_path))
188
+ if not [*cfg["remote \"#{remote}\""][key]].include? value
189
+ cfg["remote \"#{remote}\""][key] = [*cfg["remote \"#{remote}\""][key], value]
190
+ cfg.save config_path
191
+ end
192
+ end
193
+
194
+
195
+ def has_changes? ref1, ref2
196
+ not ref1.target_id == ref2.target_id
197
+ end
198
+
199
+
200
+ def ahead_of_develop? branch
201
+ has_changes? branch, @repo.branches['develop']
202
+ end
203
+
204
+ def pull remote
205
+ if @repo.remotes[remote].nil?
206
+ return Errors::remote_not_found remote
207
+ end
208
+ Dir.chdir(@repo.workdir) do
209
+ `git pull #{remote} #{current_branch}`
210
+ end
211
+ end
212
+
213
+ def add_remote name, url
214
+ return Errors::remote_exists name if not @repo.remotes[name].nil?
215
+ @repo.remotes.create name, url
216
+ end
217
+
218
+ def exec env, command
219
+ Dir.chdir(@repo.workdir) do
220
+ system env, command
221
+ end
222
+ end
17
223
  end
18
224
  end
@@ -1,3 +1,3 @@
1
1
  module Metaverse
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
data/lib/metaverse.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'bundler/setup'
1
2
  require "metaverse/version"
2
3
  require "metaverse/base"
3
4
  require "metaverse/cli"
data/metaverse.gemspec CHANGED
@@ -28,4 +28,6 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency "rspec"
29
29
  spec.add_runtime_dependency "thor", "~> 0.19"
30
30
  spec.add_runtime_dependency "colorize", "~> 0.7"
31
+ spec.add_runtime_dependency "rugged", "~> 0.21"
32
+ spec.add_runtime_dependency "transacted", "~> 0.1"
31
33
  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.1.7
4
+ version: 0.1.8
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-20 00:00:00.000000000 Z
11
+ date: 2015-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,34 @@ dependencies:
80
80
  - - ~>
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rugged
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '0.21'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '0.21'
97
+ - !ruby/object:Gem::Dependency
98
+ name: transacted
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '0.1'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '0.1'
83
111
  description:
84
112
  email:
85
113
  - okamali@productivemobile.com
@@ -101,7 +129,7 @@ files:
101
129
  - lib/metaverse.rb
102
130
  - lib/metaverse/base.rb
103
131
  - lib/metaverse/cli.rb
104
- - lib/metaverse/git.rb
132
+ - lib/metaverse/errors.rb
105
133
  - lib/metaverse/iterator.rb
106
134
  - lib/metaverse/repo.rb
107
135
  - lib/metaverse/version.rb
data/lib/metaverse/git.rb DELETED
@@ -1,84 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'find'
3
-
4
- module Metaverse
5
- module Git
6
- def load path
7
- if !File.directory? path + '/.git'
8
- raise "#{path} is not a git repo"
9
- end
10
- @path = path
11
- end
12
-
13
- def name
14
- File.basename @path
15
- end
16
-
17
- def checkout branch
18
- Dir.chdir(@path) do
19
- puts `git checkout #{branch}`
20
- end
21
- end
22
-
23
- def currentBranch
24
- Dir.chdir(@path) do
25
- branchName = `git rev-parse --abbrev-ref HEAD`;
26
- branchName.strip
27
- end
28
- end
29
-
30
- def branch branch
31
- Dir.chdir(@path) do
32
- puts `git branch #{branch}`
33
- end
34
- end
35
-
36
- def status
37
- Dir.chdir(@path) do
38
- puts `git status`
39
- end
40
- end
41
-
42
- def tag name
43
- Dir.chdir(@path) do
44
- puts `git tag #{name}`
45
- end
46
- end
47
-
48
- def pull remote
49
- Dir.chdir(@path) do
50
- `git pull #{remote} #{currentBranch}:#{currentBranch}`
51
- end
52
- end
53
-
54
- def push remote, branch
55
- Dir.chdir(@path) do
56
- puts `git push #{remote} #{branch}:#{branch}`
57
- end
58
- end
59
-
60
- def fetch remote
61
- Dir.chdir(@path) do
62
- `git fetch #{remote}`
63
- end
64
- end
65
-
66
- def reset branch
67
- Dir.chdir(@path) do
68
- puts `git reset --hard #{branch}`
69
- end
70
- end
71
-
72
- def countDiffBranches branch1, branch2
73
- Dir.chdir(@path) do
74
- `git rev-list --count #{branch1}..#{branch2}`
75
- end
76
- end
77
-
78
- def dirty?
79
- Dir.chdir(@path) do
80
- `git status --porcelain`.length != 0
81
- end
82
- end
83
- end
84
- end