gitorious-merge-request 1.0.2 → 2.0.0

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.
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
-