gitorious-merge-request 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,2 +1,5 @@
1
- Gemfile.lock
2
- pkg
1
+ /Gemfile.lock
2
+ /pkg
3
+
4
+ *.swp
5
+ *.swo
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require File.expand_path('../lib/gitorious-merge-request', File.dirname(__FILE__))
3
+ require File.expand_path('../lib/gitorious_merge_request', File.dirname(__FILE__))
4
4
 
5
- SubCommands = %w(new show rm)
5
+ SubCommands = %w(new show rm list checkout diff)
6
6
  global_opts = Trollop::options do
7
7
  version "gitorious-merge-request #{GitoriousMergeRequest::Version}"
8
8
  banner <<-EOS
@@ -16,18 +16,14 @@ EOS
16
16
  stop_on SubCommands
17
17
  end
18
18
 
19
+ $origin_repo = Gitorious.remote_to_repo 'origin'
20
+ $current_branch = Git.current_branch
21
+
19
22
  def login_opts
20
23
  opt :email, "Email for login", :short => 'e', :type => String
21
24
  end
22
25
  def code_opts
23
- opt :code, "project/repo:id (e.g. noosfero/noosfero:248)", :type => String
24
- end
25
-
26
- def login_dies opts
27
- Trollop::die :email, "must exist" unless opts[:email]
28
- end
29
- def code_dies opts
30
- Trollop::die :code, "must exist" unless opts[:code]
26
+ opt :code, "id (using git's origin repository) or remote-name:id or project/repo:id (e.g. noosfero/noosfero:248)", :type => String
31
27
  end
32
28
 
33
29
  cmd = ARGV.shift # get the subcommand
@@ -45,10 +41,12 @@ EOS
45
41
  login_opts
46
42
  opt :summary, "Summary", :short => 's', :type => String
47
43
  opt :proposal, "Proposal", :short => 'p', :type => String
48
- opt :forked_repo, "Forked repo id (e.g. ~brauliobo/noosfero/brauliobos-noosfero)", :short => 'f', :type => String
44
+ opt :forked_repo, "Git remote name OR forked repo id (e.g. ~brauliobo/noosfero/brauliobos-noosfero)", :short => 'f', :type => String
49
45
  opt :forked_branch, "Forked branch", :short => 'a', :type => String
50
- opt :target_repo, "Target repo id (e.g. noosfero/noosfero)", :short => 't', :type => String
51
- opt :target_branch, "Target branch", :short => 'b', :type => String
46
+ opt :target_repo, "Git remote name OR target repo id (e.g. noosfero/noosfero, defaults to git origin repository)",
47
+ :short => 't', :type => String, :default => $origin_repo
48
+ opt :target_branch, "Target branch (defaults to git's current branch)",
49
+ :short => 'b', :type => String, :default => $current_branch
52
50
  end
53
51
  when 'show'
54
52
  Trollop::options do
@@ -59,12 +57,29 @@ EOS
59
57
  login_opts
60
58
  code_opts
61
59
  end
60
+ when 'list'
61
+ Trollop::options do
62
+ opt :repo, "git-remote (defaults to git origin repository) or project/repo (e.g. noosfero/noosfero)",
63
+ :short => 'r', :type => String
64
+ opt :status, "Status (open or closed)", :type => String
65
+ end
66
+ when 'checkout'
67
+ Trollop::options do
68
+ code_opts
69
+ end
70
+ when 'diff'
71
+ Trollop::options do
72
+ code_opts
73
+ end
62
74
  else
63
75
  Trollop::die "use one of the subcommands: #{SubCommands.join ' '}"
64
76
  end
65
77
 
66
- include GitoriousMergeRequest
67
-
68
- send "cmd_#{cmd}", cmd_opts
78
+ #require 'irb'
79
+ #IRB.start
80
+ #exit
69
81
 
82
+ $mech = Mechanize.new
83
+ gmr = GitoriousMergeRequest.new
84
+ gmr.send "cmd_#{cmd}", cmd_opts
70
85
 
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require File.expand_path('./lib/gitorious-merge-request/version', File.dirname(__FILE__))
3
+ require File.expand_path('./lib/gitorious_merge_request/version', File.dirname(__FILE__))
4
4
 
5
5
  Gem::Specification.new do |gem|
6
6
  gem.authors = ["Braulio Bhavamitra"]
@@ -0,0 +1,31 @@
1
+ # inspired from https://gitorious.org/gitorious/merge-request-cli
2
+ class Git
3
+
4
+ def self.remote_url remote='origin'
5
+ output = `git remote -v`.scan(/^#{remote}\s+(.*.git)(.*)/)
6
+ output.first[0] if output
7
+ end
8
+
9
+ def self.current_branch
10
+ self.run('rev-parse', '--abbrev-ref HEAD', :quiet => true).squish
11
+ end
12
+
13
+ protected
14
+
15
+ def self.method_missing method, *args, &block
16
+ options = args.last
17
+ a = args.first
18
+ self.run method, a, options
19
+ end
20
+
21
+ def self.run cmd, args, options = {}
22
+ cmdline = "git #{cmd} #{args}"
23
+ if options[:quiet]
24
+ `#{cmdline} 2>&1`
25
+ else
26
+ puts "$ #{cmdline}"
27
+ system cmdline
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,27 @@
1
+ class Gitorious
2
+
3
+ Host = "https://gitorious.org"
4
+ MRListUrl = "#{Host}/%{repo}/merge_requests.xml?status=%{status}"
5
+ MRUrl = "#{Host}/%{repo}/merge_requests/%{id}"
6
+ MRUrlXml = "#{Host}/%{repo}/merge_requests/%{id}.xml"
7
+ NewMRUrl = "#{Host}/%{forked_repo}/merge_requests/new"
8
+ CommitListUrl = "#{Host}/%{forked_repo}/merge_requests/commit_list"
9
+
10
+ def self.login email, password
11
+ page = $mech.get 'https://gitorious.org/login'
12
+ form = page.forms.first
13
+ fields = form.fields
14
+ fields.find{ |f| f.name == 'email' }.value = email
15
+ fields.find{ |f| f.name == 'password' }.value = password
16
+ page = form.submit
17
+
18
+ raise "Can't login" unless page.content.include?('Logged in successfully')
19
+ page
20
+ end
21
+
22
+ def self.remote_to_repo remote='origin'
23
+ Git.remote_url(remote) =~ /gitorious\.org[\/:](.+).git/
24
+ $1
25
+ end
26
+
27
+ end
@@ -0,0 +1,124 @@
1
+ class Gitorious::MergeRequest
2
+
3
+ attr_accessor :id
4
+ attr_accessor :username
5
+ attr_accessor :proposal, :summary
6
+ attr_accessor :target_repo, :target_branch
7
+ attr_accessor :forked_repo, :forked_branch
8
+ attr_accessor :merge_base_sha, :ending_commit
9
+
10
+ def initialize *args
11
+ repo = args[0]
12
+ if args[1].is_a?(Nokogiri::XML::Node)
13
+ page = args[1]
14
+ else
15
+ id = args[1]
16
+ page = Nokogiri::HTML $mech.get(Gitorious::MRUrlXml % {:repo => repo, :id => id}).content
17
+ end
18
+
19
+ self.id = page.at("id").content
20
+ self.summary = page.at("summary").content
21
+ self.proposal = page.at("proposal").content
22
+ self.username = page.at("username").content
23
+ self.target_repo = repo
24
+ self.target_branch = page.at('target_repository/branch').content
25
+ self.forked_repo = "#{self.username}/#{page.at('source_repository/name').content}"
26
+ self.forked_branch = page.at('source_repository/branch').content
27
+ self.ending_commit = page.at("ending-commit").content
28
+ version = page.xpath('//versions/version').last
29
+ self.merge_base_sha = version.at('merge_base_sha').content if version
30
+ end
31
+
32
+ def self.list repo, status="Open"
33
+ page = Nokogiri::HTML $mech.get(Gitorious::MRListUrl % {:repo => repo, :status => status}).content
34
+ page.xpath('//merge-request').map do |node|
35
+ mr = self.new repo, node
36
+ end
37
+ end
38
+
39
+ def self.delete repo, id
40
+ page = $mech.get Gitorious::MRUrl % {:repo => repo, :id => id}
41
+ form = page.form_with :action => "/#{repo}/merge_requests/#{id}"
42
+ page = form.submit
43
+ end
44
+
45
+ def self.create attrs = {}
46
+ @summary = attrs[:summary]
47
+ @proposal = attrs[:proposal]
48
+ @forked_repo = attrs[:forked_repo]
49
+ @forked_branch = attrs[:forked_branch]
50
+ @target_repo = attrs[:target_repo]
51
+ @target_branch = attrs[:target_branch]
52
+
53
+ forked_repo = @forked_repo.split('/')[1,2].join('/')
54
+ target_repo = @target_repo.split('/').last
55
+
56
+ page = $mech.get Gitorious::NewMRUrl % {:forked_repo => forked_repo}
57
+ form = page.form_with :id => 'new_merge_request'
58
+
59
+ form.set_fields 'merge_request[summary]' => @summary
60
+ form.set_fields 'merge_request[proposal]' => @proposal
61
+
62
+ @authenticity_token = form.fields.find{ |f| f.name == 'authenticity_token' }.value
63
+
64
+ target_option = page.parser.css('#merge_request_target_repository_id option').find{ |o| o.text == target_repo }
65
+ raise "Can't find target repository" unless target_option
66
+ target_id = target_option['value']
67
+ form.set_fields 'merge_request[target_repository_id]' => target_id
68
+
69
+ target_branch_option = page.parser.css("#merge_request_target_branch option[value=#{@target_branch}]").first
70
+ raise "Can't find target branch" unless target_branch_option
71
+ form.set_fields 'merge_request[target_branch]' => @target_branch
72
+
73
+ forked_branch_option = page.parser.css("#merge_request_source_branch option[value=#{@forked_branch}]").first
74
+ raise "Can't find forked branch" unless forked_branch_option
75
+ form.set_fields 'merge_request[source_branch]' => @forked_branch
76
+
77
+ commit = self.select_ending_commit target_id
78
+ form.add_field! 'merge_request[ending_commit]', commit
79
+
80
+ page = form.submit
81
+ raise "Error while creating merge request" if page.parser.css('.errorExplanation').first
82
+
83
+ repo = @target_repo
84
+ id = page.uri.to_s.split('/').last
85
+ mr = self.new repo, id
86
+ end
87
+
88
+ def branch
89
+ "merge-requests/#{self.id}"
90
+ end
91
+
92
+ def checkout remote='origin', options = {}
93
+ if $current_branch != self.branch
94
+ Git.branch "-D #{branch}", :quiet => true
95
+ Git.checkout "-b #{branch} #{self.merge_base_sha}", options
96
+ end
97
+ Git.pull "#{remote} refs/#{branch}", :quiet => true
98
+ end
99
+
100
+ def diff remote='origin'
101
+ self.checkout remote, :quiet => true
102
+ Git.checkout $current_branch, :quiet => true
103
+ Git.diff branch
104
+ end
105
+
106
+ protected
107
+
108
+ def self.select_ending_commit target_id
109
+ forked_repo = @forked_repo.split('/')[1,2].join('/')
110
+
111
+ puts "Selecting last commit"
112
+ page = $mech.post Gitorious::CommitListUrl % {:forked_repo => forked_repo}, {
113
+ 'authenticity_token' => @authenticity_token,
114
+ 'merge_request[target_repository_id]' => target_id,
115
+ 'merge_request[target_branch]' => @target_branch,
116
+ 'merge_request[source_branch]' => @forked_branch}
117
+
118
+ # TODO: give option to select
119
+ first_commit = page.parser.css('#commit_0 input').first
120
+ raise 'No commit to merge' unless first_commit
121
+ first_commit['value']
122
+ end
123
+
124
+ end
@@ -0,0 +1,104 @@
1
+ require 'rubygems'
2
+
3
+ require 'mechanize'
4
+ require 'trollop'
5
+ require 'active_support/all'
6
+ require 'highline/import'
7
+ require 'i18n/core_ext/string/interpolate'
8
+
9
+ require File.expand_path('./gitorious_merge_request/version', File.dirname(__FILE__))
10
+ require File.expand_path('./git', File.dirname(__FILE__))
11
+ require File.expand_path('./gitorious', File.dirname(__FILE__))
12
+ require File.expand_path('./gitorious/merge_request', File.dirname(__FILE__))
13
+
14
+ class GitoriousMergeRequest
15
+
16
+ def login opts
17
+ Trollop::die :email, "must exist" unless opts[:email]
18
+ email = opts[:email]
19
+ password = ask('Password: '){ |q| q.echo = false }
20
+
21
+ puts 'Login...'
22
+ Gitorious.login email, password
23
+ end
24
+
25
+ def cmd_new opts
26
+ Trollop::die :summary, "must exist" unless opts[:summary]
27
+ Trollop::die :forked, "must exist" unless opts[:forked_repo]
28
+ Trollop::die :forked_branch, "must exist" unless opts[:forked_branch]
29
+ Trollop::die :target, "must exist" unless opts[:target_repo]
30
+ Trollop::die :target_branch, "must exist" unless opts[:target_branch]
31
+
32
+ login opts
33
+ puts "Opening merge request page"
34
+ mr = Gitorious::MergeRequest.create opts
35
+ puts "YOUR NEW MERGE REQUEST\n"
36
+ show mr
37
+ end
38
+
39
+ def parse_code code
40
+ id = Integer(code) rescue nil
41
+ id = nil if id.zero?
42
+ if id or code.blank?
43
+ repo = $origin_repo
44
+ else
45
+ raise 'Invalid code format' unless code =~ /(.+):(\d+)/
46
+ repo_or_remote, id = $1, $2.to_i
47
+ repo = if (remote = Gitorious.remote_to_repo(repo_or_remote))
48
+ then remote else repo_or_remote end
49
+ end
50
+ [repo, id]
51
+ end
52
+
53
+ def cmd_rm opts
54
+ repo, id = parse_code opts[:code]
55
+ login opts
56
+ Gitorious::MergeRequest.delete repo, id
57
+ end
58
+
59
+ def cmd_show opts
60
+ repo, id = parse_code opts[:code]
61
+ mr = Gitorious::MergeRequest.new repo, id
62
+ show mr
63
+ end
64
+
65
+ def cmd_list opts
66
+ repo, id = parse_code opts[:code]
67
+ status = opts[:status] || ''
68
+ list = Gitorious::MergeRequest.list repo, status
69
+ list.each do |mr|
70
+ puts "#{mr.id} from #{mr.username}: #{mr.summary}"
71
+ end
72
+ end
73
+
74
+ def cmd_checkout opts
75
+ repo, id = parse_code opts[:code]
76
+ mr = Gitorious::MergeRequest.new repo, id
77
+ mr.checkout
78
+ end
79
+
80
+ def cmd_diff opts
81
+ repo, id = parse_code opts[:code]
82
+ mr = Gitorious::MergeRequest.new repo, id
83
+ mr.diff
84
+ end
85
+
86
+ def show mr
87
+ #Requester: #{mr.requester}
88
+ puts <<-EOS
89
+ ID: #{mr.id} (#{Gitorious::MRUrl % {:repo => mr.target_repo, :id => mr.id}})
90
+ User: #{mr.username} (#{Gitorious::Host}/#{mr.username})
91
+ Summary: #{mr.summary}
92
+ Proposal:
93
+ #{mr.proposal}
94
+
95
+ Fork: branch #{mr.forked_branch} of #{mr.forked_repo} (#{Gitorious::Host}/#{mr.forked_repo})
96
+ Target: branch #{mr.target_branch} of #{mr.target_repo} (#{Gitorious::Host}/#{mr.target_repo})
97
+ Ending commit: #{mr.ending_commit}
98
+ Merge base SHA: #{mr.merge_base_sha}
99
+
100
+ EOS
101
+ end
102
+
103
+ end
104
+
@@ -0,0 +1,6 @@
1
+ class GitoriousMergeRequest
2
+
3
+ Version = '2.0.0'
4
+
5
+ end
6
+
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitorious-merge-request
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
- - 1
8
- - 0
9
7
  - 2
10
- version: 1.0.2
8
+ - 0
9
+ - 0
10
+ version: 2.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Braulio Bhavamitra
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-11-10 00:00:00 +00:00
18
+ date: 2012-11-14 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -90,9 +90,12 @@ files:
90
90
  - Rakefile
91
91
  - bin/gitorious-merge-request
92
92
  - gitorious-merge-request.gemspec
93
- - lib/gitorious-merge-request.rb
94
- - lib/gitorious-merge-request/.gitkeep
95
- - lib/gitorious-merge-request/version.rb
93
+ - lib/git.rb
94
+ - lib/gitorious.rb
95
+ - lib/gitorious/merge_request.rb
96
+ - lib/gitorious_merge_request.rb
97
+ - lib/gitorious_merge_request/.gitkeep
98
+ - lib/gitorious_merge_request/version.rb
96
99
  has_rdoc: true
97
100
  homepage: http://github.com/brauliobo/gitorious-merge-request
98
101
  licenses: []
@@ -1,161 +0,0 @@
1
- require 'rubygems'
2
-
3
- require 'mechanize'
4
- require 'trollop'
5
- require 'active_support/all'
6
- require 'highline/import'
7
-
8
- require File.expand_path('./gitorious-merge-request/version', File.dirname(__FILE__))
9
-
10
- module GitoriousMergeRequest
11
-
12
- MRUrl = "https://gitorious.org/%{repo}/merge_requests/%{id}"
13
- NewMRUrl = "https://gitorious.org/%{forked_repo}/merge_requests/new"
14
- CommitListUrl = "https://gitorious.org/%{forked_repo}/merge_requests/commit_list"
15
-
16
- def login opts
17
- @email = opts[:email]
18
-
19
- password = ask('Password: '){ |q| q.echo = false }
20
-
21
- puts 'Login...'
22
- page = @mechanize.get 'https://gitorious.org/login'
23
- form = page.forms.first
24
- fields = form.fields
25
- fields.find{ |f| f.name == 'email' }.value = @email
26
- fields.find{ |f| f.name == 'password' }.value = password
27
- page = form.submit
28
-
29
- raise "Can't login" unless page.content.include?('Logged in successfully')
30
- end
31
-
32
- def cmd_new opts
33
- login_dies opts
34
- Trollop::die :summary, "must exist" unless opts[:summary]
35
- Trollop::die :forked, "must exist" unless opts[:forked_repo]
36
- Trollop::die :forked_branch, "must exist" unless opts[:forked_branch]
37
- Trollop::die :target, "must exist" unless opts[:target_repo]
38
- Trollop::die :target_branch, "must exist" unless opts[:target_branch]
39
-
40
- @summary = opts[:summary]
41
- @proposal = opts[:proposal]
42
- @forked_repo = opts[:forked_repo]
43
- @forked_branch = opts[:forked_branch]
44
- @target_repo = opts[:target_repo]
45
- @target_branch = opts[:target_branch]
46
-
47
- def ending_commit(target_id)
48
- forked_repo = @forked_repo.split('/')[1,2].join('/')
49
-
50
- puts "Selecting last commit"
51
- page = @mechanize.post CommitListUrl % {:forked_repo => forked_repo}, {
52
- 'authenticity_token' => @authenticity_token,
53
- 'merge_request[target_repository_id]' => target_id,
54
- 'merge_request[target_branch]' => @target_branch,
55
- 'merge_request[source_branch]' => @forked_branch}
56
-
57
- # TODO: give option to select
58
- first_commit = page.parser.css('#commit_0 input').first
59
- raise 'No commit to merge' unless first_commit
60
- first_commit.attr 'value'
61
- end
62
-
63
- def merge_request
64
- forked_repo = @forked_repo.split('/')[1,2].join('/')
65
- target_repo = @target_repo.split('/').last
66
-
67
- puts "Opening merge request page"
68
- page = @mechanize.get NewMRUrl % {:forked_repo => forked_repo}
69
- form = page.form_with :id => 'new_merge_request'
70
-
71
- form.set_fields 'merge_request[summary]' => @summary
72
- form.set_fields 'merge_request[proposal]' => @proposal
73
-
74
- @authenticity_token = form.fields.find{ |f| f.name == 'authenticity_token' }.value
75
-
76
- target_option = page.parser.css('#merge_request_target_repository_id option').find{ |o| o.text == target_repo }
77
- raise "Can't find target repository" unless target_option
78
- target_id = target_option.attr('value')
79
- form.set_fields 'merge_request[target_repository_id]' => target_id
80
-
81
- target_branch_option = page.parser.css("#merge_request_target_branch option[value=#{@target_branch}]").first
82
- raise "Can't find target branch" unless target_branch_option
83
- form.set_fields 'merge_request[target_branch]' => @target_branch
84
-
85
- forked_branch_option = page.parser.css("#merge_request_source_branch option[value=#{@forked_branch}]").first
86
- raise "Can't find forked branch" unless forked_branch_option
87
- form.set_fields 'merge_request[source_branch]' => @forked_branch
88
-
89
- commit = ending_commit target_id
90
- form.add_field! 'merge_request[ending_commit]', commit
91
-
92
- page = form.submit
93
- raise "Error while creating merge request" if page.parser.css('.errorExplanation').first
94
-
95
- puts "YOUR NEW MERGE REQUEST\n"
96
- cmd_show_for page
97
- end
98
-
99
- @mechanize = Mechanize.new
100
- login opts
101
- merge_request
102
- end
103
-
104
- def parse_code opts
105
- @code = opts[:code]
106
-
107
- raise "can't parse code" unless @code =~ /(.+):(\d+)/
108
- repo = $1
109
- id = $2.to_i
110
-
111
- [repo, id]
112
- end
113
-
114
- def cmd_rm opts
115
- login_dies opts
116
- code_dies opts
117
-
118
- repo, id = parse_code opts
119
-
120
- @mechanize = Mechanize.new
121
- login opts
122
- page = @mechanize.get MRUrl % {:repo => repo, :id => id}
123
- form = page.form_with :action => "/#{repo}/merge_requests/#{id}"
124
- page = form.submit
125
- end
126
-
127
- def cmd_show opts
128
- code_dies opts
129
-
130
- repo, id = parse_code opts
131
-
132
- @mechanize = Mechanize.new
133
- page = @mechanize.get MRUrl % {:repo => repo, :id => id}
134
-
135
- cmd_show_for page
136
- end
137
-
138
- def cmd_show_for page
139
- raise "can't grab summary" unless page.parser.css('#content h1').first.text.strip =~ /[^#]+ #(\d+): (.+)/
140
- id, summary = $1, $2
141
- proposal = page.parser.css('.proposal').first.text.strip
142
-
143
- lis = page.parser.css('ul.meta li')
144
- raise "can't get meta content" if lis.empty?
145
-
146
- a = lis[0].css('a').last
147
- requester = "#{a.text.strip} (http://gitorious.org#{a.attr('href')})"
148
-
149
- puts <<-EOS
150
- Requester: #{requester}
151
-
152
- ID: #{id}
153
- Summary: #{summary}
154
- Proposal:
155
- #{proposal}
156
-
157
- EOS
158
- end
159
-
160
- end
161
-
@@ -1,7 +0,0 @@
1
-
2
- module GitoriousMergeRequest
3
-
4
- Version = '1.0.2'
5
-
6
- end
7
-