git-review 2.0.0.alpha → 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.
@@ -1,289 +0,0 @@
1
- require 'net/http'
2
- require 'net/https'
3
- # Used to handle json data
4
- require 'yajl'
5
- # Required to hide password
6
- require 'io/console'
7
- # Required by yajl for decoding
8
- require 'stringio'
9
- # Used to retrieve hostname
10
- require 'socket'
11
-
12
-
13
- module GitReview
14
-
15
- class Github
16
-
17
- include ::GitReview::Internals
18
-
19
- attr_reader :github
20
- attr_accessor :source_repo
21
-
22
- # acts like a singleton class but it's actually not
23
- # use ::GitReview::Github.instance everywhere except in tests
24
- def self.instance
25
- @instance ||= new
26
- end
27
-
28
- def initialize
29
- configure_github_access
30
- end
31
-
32
- # setup connection with Github via OAuth
33
- # @return [String] the username logged in
34
- def configure_github_access
35
- settings = ::GitReview::Settings.instance
36
- if settings.oauth_token && settings.username
37
- @github = Octokit::Client.new(
38
- :login => settings.username,
39
- :access_token => settings.oauth_token,
40
- :auto_traversal => true
41
- )
42
- @github.login
43
- else
44
- configure_oauth
45
- configure_github_access
46
- end
47
- end
48
-
49
- # @return [Boolean, Hash] the specified request if exists, otherwise false.
50
- # Instead of true, the request itself is returned, so another round-trip
51
- # of pull_request can be avoided.
52
- def request_exists?(number, state='open')
53
- return false if number.nil?
54
- request = @github.pull_request(source_repo, number)
55
- request.state == state ? request : false
56
- rescue Octokit::NotFound
57
- false
58
- end
59
-
60
- def request_exists_for_branch?(upstream=false, branch=local.source_branch)
61
- target_repo = local.target_repo(upstream)
62
- @github.pull_requests(target_repo).any? { |r|
63
- r.head.ref == branch
64
- }
65
- end
66
-
67
- # an alias to pull_requests
68
- def current_requests(repo=source_repo)
69
- @github.pull_requests(repo)
70
- end
71
-
72
- # a more detailed collection of requests
73
- def current_requests_full(repo=source_repo)
74
- @github.pull_requests(repo).collect { |request|
75
- @github.pull_request(repo, request.number)
76
- }
77
- end
78
-
79
- def update
80
- git_call('fetch origin')
81
- end
82
-
83
- # @return [Array(String, String)] user and repo name from local git config
84
- def repo_info_from_config
85
- git_config = local.config
86
- url = git_config['remote.origin.url']
87
- raise ::GitReview::InvalidGitRepositoryError if url.nil?
88
-
89
- user, project = github_url_matching(url)
90
- # if there are no results yet, look for 'insteadof' substitutions
91
- # in URL and try again
92
- unless user && project
93
- insteadof_url, true_url = github_insteadof_matching(git_config, url)
94
- if insteadof_url and true_url
95
- url = url.sub(insteadof_url, true_url)
96
- user, project = github_url_matching(url)
97
- end
98
- end
99
- [user, project]
100
- end
101
-
102
- # @return [String] the source repo
103
- def source_repo
104
- # cache source_repo
105
- if @source_repo
106
- @source_repo
107
- else
108
- user, repo = repo_info_from_config
109
- @source_repo = "#{user}/#{repo}" if user && repo
110
- end
111
- end
112
-
113
- def commit_discussion(number)
114
- pull_commits = @github.pull_commits(source_repo, number)
115
- repo = @github.pull_request(source_repo, number).head.repo.full_name
116
- discussion = ["Commits on pull request:\n\n"]
117
- discussion += pull_commits.collect { |commit|
118
- # commit message
119
- name = commit.committer.login
120
- output = "\e[35m#{name}\e[m "
121
- output << "committed \e[36m#{commit.sha[0..6]}\e[m "
122
- output << "on #{format_time(commit.commit.committer.date)}"
123
- output << ":\n#{''.rjust(output.length + 1, "-")}\n"
124
- output << "#{commit.commit.message}"
125
- output << "\n\n"
126
- result = [output]
127
-
128
- # comments on commit
129
- comments = @github.commit_comments(repo, commit.sha)
130
- result + comments.collect { |comment|
131
- name = comment.user.login
132
- output = "\e[35m#{name}\e[m "
133
- output << "added a comment to \e[36m#{commit.sha[0..6]}\e[m"
134
- output << " on #{format_time(comment.created_at)}"
135
- unless comment.created_at == comment.updated_at
136
- output << " (updated on #{format_time(comment.updated_at)})"
137
- end
138
- output << ":\n#{''.rjust(output.length + 1, "-")}\n"
139
- output << comment.body
140
- output << "\n\n"
141
- }
142
- }
143
- discussion.compact.flatten unless discussion.empty?
144
- end
145
-
146
- def issue_discussion(number)
147
- comments = @github.issue_comments(source_repo, number)
148
- discussion = ["\nComments on pull request:\n\n"]
149
- discussion += comments.collect { |comment|
150
- name = comment.user.login
151
- output = "\e[35m#{name}\e[m "
152
- output << "added a comment to \e[36m#{comment.id}\e[m"
153
- output << " on #{format_time(comment.created_at)}"
154
- unless comment.created_at == comment.updated_at
155
- output << " (updated on #{format_time(comment.updated_at)})"
156
- end
157
- output << ":\n#{''.rjust(output.length + 1, "-")}\n"
158
- output << comment.body
159
- output << "\n\n"
160
- }
161
- discussion.compact.flatten unless discussion.empty?
162
- end
163
-
164
- # show discussion for a request
165
- def discussion(number)
166
- commit_discussion(number) +
167
- issue_discussion(number)
168
- end
169
-
170
- # show latest pull request number
171
- def latest_request_number(repo=source_repo)
172
- current_requests(repo).collect(&:number).sort.last.to_i
173
- end
174
-
175
- # get the number of the request that matches the title
176
- def request_number_by_title(title, repo=source_repo)
177
- request = current_requests(repo).find { |r| r.title == title }
178
- request.number if request
179
- end
180
-
181
- # delegate methods that interact with Github to Octokit client
182
- def method_missing(method, *args)
183
- if @github.respond_to?(method)
184
- @github.send(method, *args)
185
- else
186
- super
187
- end
188
- end
189
-
190
- def respond_to?(method)
191
- @github.respond_to?(method) || super
192
- end
193
-
194
- private
195
-
196
- def configure_oauth
197
- begin
198
- prepare_username_and_password
199
- prepare_description
200
- authorize
201
- rescue ::GitReview::AuthenticationError => e
202
- warn e.message
203
- rescue ::GitReview::UnprocessableState => e
204
- warn e.message
205
- exit 1
206
- end
207
- end
208
-
209
- def prepare_username_and_password
210
- puts "Requesting a OAuth token for git-review."
211
- puts "This procedure will grant access to your public and private "\
212
- "repositories."
213
- puts "You can revoke this authorization by visiting the following page: "\
214
- "https://github.com/settings/applications"
215
- print "Please enter your GitHub's username: "
216
- @username = STDIN.gets.chomp
217
- print "Please enter your GitHub's password (it won't be stored anywhere): "
218
- @password = STDIN.noecho(&:gets).chomp
219
- print "\n"
220
- end
221
-
222
- def prepare_description(chosen_description=nil)
223
- if chosen_description
224
- @description = chosen_description
225
- else
226
- @description = "git-review - #{Socket.gethostname}"
227
- puts "Please enter a description to associate to this token, it will "\
228
- "make easier to find it inside of GitHub's application page."
229
- puts "Press enter to accept the proposed description"
230
- print "Description [#{@description}]:"
231
- user_description = STDIN.gets.chomp
232
- @description = user_description.empty? ? @description : user_description
233
- end
234
- end
235
-
236
- def authorize
237
- uri = URI('https://api.github.com/authorizations')
238
- http = Net::HTTP.new(uri.host, uri.port)
239
- http.use_ssl = true
240
- req = Net::HTTP::Post.new(uri.request_uri)
241
- req.basic_auth(@username, @password)
242
- req.body = Yajl::Encoder.encode(
243
- {
244
- :scopes => %w(repo),
245
- :note => @description
246
- }
247
- )
248
- response = http.request(req)
249
- if response.code == '201'
250
- parser_response = Yajl::Parser.parse(response.body)
251
- save_oauth_token(parser_response['token'])
252
- elsif response.code == '401'
253
- raise ::GitReview::AuthenticationError
254
- else
255
- raise ::GitReview::UnprocessableState, response.body
256
- end
257
- end
258
-
259
- def save_oauth_token(token)
260
- settings = ::GitReview::Settings.instance
261
- settings.oauth_token = token
262
- settings.username = @username
263
- settings.save!
264
- puts "OAuth token successfully created.\n"
265
- end
266
-
267
- # extract user and project name from GitHub URL.
268
- def github_url_matching(url)
269
- matches = /github\.com.(.*?)\/(.*)/.match(url)
270
- matches ? [matches[1], matches[2].sub(/\.git\z/, '')] : [nil, nil]
271
- end
272
-
273
- # look for 'insteadof' substitutions in URL.
274
- def github_insteadof_matching(config, url)
275
- first_match = config.keys.collect { |key|
276
- [config[key], /url\.(.*github\.com.*)\.insteadof/.match(key)]
277
- }.find { |insteadof_url, true_url|
278
- url.index(insteadof_url) and true_url != nil
279
- }
280
- first_match ? [first_match[0], first_match[1][1]] : [nil, nil]
281
- end
282
-
283
- def local
284
- @local ||= ::GitReview::Local.instance
285
- end
286
-
287
- end
288
-
289
- end
@@ -1,47 +0,0 @@
1
- module GitReview
2
-
3
- module Internals
4
-
5
- private
6
-
7
- # System call to 'git'.
8
- def git_call(command, verbose = debug_mode, enforce_success = false)
9
- if verbose
10
- puts
11
- puts " git #{command}"
12
- puts
13
- end
14
- output = `git #{command}`
15
- puts output if verbose and not output.empty?
16
- # If we need sth. to succeed, but it doesn't, then stop right there.
17
- if enforce_success and not last_command_successful?
18
- puts output unless output.empty?
19
- raise ::GitReview::UnprocessableState
20
- end
21
- output
22
- end
23
-
24
- # @return [Boolean] whether the last issued system call was successful
25
- def last_command_successful?
26
- $?.exitstatus == 0
27
- end
28
-
29
- def debug_mode
30
- ::GitReview::Settings.instance.review_mode == 'debug'
31
- end
32
-
33
- # display helper to make output more configurable
34
- def format_text(info, size)
35
- info.to_s.gsub("\n", ' ')[0, size-1].ljust(size)
36
- end
37
-
38
- # display helper to unify time output
39
- def format_time(time)
40
- time = Time.parse(time) if time.is_a?(String)
41
- time.strftime('%d-%b-%y')
42
- end
43
-
44
- end
45
-
46
- end
47
-