git-process-lib 2.0.4 → 3.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.
@@ -33,8 +33,14 @@ module GitProc
33
33
  end
34
34
  @logger.level = log_level.nil? ? GitLogger::WARN : log_level
35
35
  @logger.datetime_format = '%Y-%m-%d %H:%M:%S'
36
- @logger.formatter = proc do |_, _, _, msg|
37
- "#{msg}\n"
36
+ @logger.formatter = proc do |severity, datetime, progname, msg|
37
+ if progname.nil?
38
+ m = "#{msg}\n"
39
+ else
40
+ m = "#{progname} => #{msg}\n"
41
+ end
42
+
43
+ @logger.debug? ? "[#{'%-5.5s' % severity}] #{datetime} - #{m}" : m
38
44
  end
39
45
  end
40
46
 
@@ -45,38 +51,27 @@ module GitProc
45
51
 
46
52
 
47
53
  def debug(msg = nil, &block)
48
- if msg.nil?
49
- @logger.debug(&block)
50
- else
51
- @logger.debug(msg)
52
- end
54
+ @logger.debug(msg, &block)
53
55
  end
54
56
 
55
57
 
56
58
  def info(msg = nil, &block)
57
- if msg.nil?
58
- @logger.info(&block)
59
- else
60
- @logger.info(msg)
61
- end
59
+ @logger.info(msg, &block)
62
60
  end
63
61
 
64
62
 
65
63
  def warn(msg = nil, &block)
66
- if msg.nil?
67
- @logger.send(:warn, &block)
68
- else
69
- @logger.send(:warn, msg)
70
- end
64
+ @logger.warn(msg, &block)
71
65
  end
72
66
 
73
67
 
74
68
  def error(msg = nil, &block)
75
- if msg.nil?
76
- @logger.error(&block)
77
- else
78
- @logger.error(msg)
79
- end
69
+ @logger.error(msg, &block)
70
+ end
71
+
72
+
73
+ def fatal(msg = nil, &block)
74
+ @logger.fatal(msg, &block)
80
75
  end
81
76
 
82
77
  end
@@ -107,13 +107,20 @@ module GitProc
107
107
  end
108
108
 
109
109
 
110
+ # noinspection RubyLocalVariableNamingConvention
110
111
  def should_remove_master?
111
- my_branches = gitlib.branches()
112
- gitlib.has_a_remote? and
113
- my_branches.include?(config.master_branch) and
114
- my_branches.current.name != config.master_branch and
115
- !keep_local_integration_branch? and
116
- my_branches[config.integration_branch].contains_all_of(config.master_branch)
112
+ has_a_remote = gitlib.has_a_remote?
113
+ my_branches = gitlib.branches
114
+ includes_master_branch = my_branches.include?(config.master_branch)
115
+ current_branch_is_not_master = my_branches.current.name != config.master_branch
116
+ do_not_keep_integration_branch = !keep_local_integration_branch?
117
+ integration_branch_contains_all_of_master = my_branches[config.integration_branch].contains_all_of(config.master_branch)
118
+
119
+ return (has_a_remote and
120
+ includes_master_branch and
121
+ current_branch_is_not_master and
122
+ do_not_keep_integration_branch and
123
+ integration_branch_contains_all_of_master)
117
124
  end
118
125
 
119
126
 
@@ -12,9 +12,6 @@
12
12
 
13
13
  require 'git-process/git_config'
14
14
  require 'addressable/uri'
15
- #require 'git-process/git_branches'
16
- #require 'git-process/git_status'
17
- #require 'git-process/git_process_error'
18
15
 
19
16
 
20
17
  class String
@@ -61,7 +58,7 @@ module GitProc
61
58
 
62
59
 
63
60
  # @deprecated
64
- # TODO: Remove
61
+ # @todo Remove
65
62
  def server_name
66
63
  @server_name ||= self.remote_name
67
64
  end
@@ -77,6 +74,13 @@ module GitProc
77
74
  end
78
75
 
79
76
 
77
+ #
78
+ # The name of the repository
79
+ #
80
+ # @example
81
+ # repo_name #=> "jdigger/git-process"
82
+ #
83
+ # @return [String] the name of the repository
80
84
  def repo_name
81
85
  unless @repo_name
82
86
  url = config["remote.#{name}.url"]
@@ -88,7 +92,14 @@ module GitProc
88
92
  end
89
93
 
90
94
 
91
- def name
95
+ #
96
+ # Returns the "remote name" to use. By convention the most common name is "origin".
97
+ #
98
+ # If the Git configuration "gitProcess.remoteName" is set, that will always be used. Otherwise this
99
+ # simple returns the first name it finds in the list of remotes.
100
+ #
101
+ # @return [String, nil] the remote name, or nil if there are none defined
102
+ def remote_name
92
103
  unless @remote_name
93
104
  @remote_name = config['gitProcess.remoteName']
94
105
  if @remote_name.nil? or @remote_name.empty?
@@ -97,7 +108,7 @@ module GitProc
97
108
  @remote_name = nil
98
109
  else
99
110
  @remote_name = remotes[0]
100
- raise '!@remote_name.is_a? String' unless @remote_name.is_a? String
111
+ raise "remote name is not a String: #{@remote_name.inspect}" unless @remote_name.is_a? String
101
112
  end
102
113
  end
103
114
  logger.debug { "Using remote name of '#{@remote_name}'" }
@@ -106,11 +117,25 @@ module GitProc
106
117
  end
107
118
 
108
119
 
120
+ alias :name :remote_name
121
+
122
+
123
+ #
124
+ # Takes {#remote_name} and combines it with {GitConfig#master_branch}.
125
+ #
126
+ # @example
127
+ # master_branch_name #=> origin/master
128
+ #
129
+ # @return [String] the complete remote name of the integration branch
130
+ #
109
131
  def master_branch_name
110
132
  "#{self.name}/#{config.master_branch}"
111
133
  end
112
134
 
113
135
 
136
+ alias :remote_integration_branch_name :master_branch_name
137
+
138
+
114
139
  def remote_names
115
140
  remote_str = config.gitlib.command(:remote, [:show])
116
141
  if remote_str.nil? or remote_str.empty?
@@ -136,6 +161,8 @@ module GitProc
136
161
  # @raise [URI::InvalidURIError] the retrieved URL does not have a schema
137
162
  # @raise [GitHubService::NoRemoteRepository] if could not figure out a host for the retrieved URL
138
163
  # @raise [::ArgumentError] if a server name is not provided
164
+ #
165
+ # @todo use the netrc gem
139
166
  def expanded_url(server_name = 'origin', raw_url = nil, opts = {})
140
167
  if raw_url.nil?
141
168
  raise ArgumentError.new('Need server_name') unless server_name
@@ -185,6 +212,7 @@ module GitProc
185
212
  alias :add :add_remote
186
213
 
187
214
 
215
+ # @todo use the netrc gem
188
216
  #noinspection RubyClassMethodNamingConvention
189
217
  def self.hostname_and_user_from_ssh_config(host_alias, config_file)
190
218
  if File.exists?(config_file)
@@ -10,31 +10,34 @@
10
10
  # See the License for the specific language governing permissions and
11
11
  # limitations under the License.
12
12
 
13
- require 'git-process/git_lib'
13
+ require File.dirname(__FILE__) + '/git_lib'
14
+ require File.dirname(__FILE__) + '/git_logger'
14
15
  require 'highline/import'
15
16
  require 'octokit'
17
+ require 'octokit/default'
16
18
  require 'uri'
19
+ require 'faraday'
20
+ require 'faraday/response/logger'
17
21
 
18
22
 
19
- #
20
- # Provides methods related to GitHub configuration
21
- #
22
23
  module GitHubService
23
24
 
25
+ #
26
+ # Provides methods related to GitHub configuration
27
+ #
24
28
  class Configuration
25
29
 
26
- attr_reader :git_config
27
-
28
-
29
30
  #
30
31
  # @param [GitProc::GitConfig] git_config
31
32
  # @param [Hash] opts
32
- # @option opts [String] :remote_name (#remote_name) The "remote" name to use (e.g., 'origin')
33
+ # @option opts [String] :remote_name (Configuration#remote_name) The "remote" name to use (e.g., 'origin')
33
34
  # @option opts [String] :user the username to authenticate with
34
- # @option opts [String] :password (#password) the password to authenticate with
35
+ # @option opts [String] :password (Configuration#password) the password to authenticate with
35
36
  #
36
37
  # @return [String] the OAuth token
37
38
  #
39
+ # @todo pass in {GitLib} instead of {GitConfig}
40
+ #
38
41
  def initialize(git_config, opts = {})
39
42
  @git_config = git_config
40
43
  @user = opts[:user]
@@ -43,7 +46,15 @@ module GitHubService
43
46
  end
44
47
 
45
48
 
46
- # @return [String]
49
+ # @return [GitProc::GitConfig]
50
+ def git_config
51
+ @git_config
52
+ end
53
+
54
+
55
+ # @return [String] the "remote name" (e.g., origin) for GitHub
56
+ #
57
+ # @see GitProc::GitRemote#remote_name
47
58
  def remote_name
48
59
  unless @remote_name
49
60
  @remote_name = gitlib.remote.name
@@ -53,21 +64,21 @@ module GitHubService
53
64
  end
54
65
 
55
66
 
56
- # @return [String]
67
+ # @return [String] the user name for GitHub
57
68
  def user
58
69
  @user ||= Configuration.ask_for_user(gitlib)
59
70
  end
60
71
 
61
72
 
62
- # @return [String]
73
+ # @return [String] the password for GitHub
63
74
  def password
64
75
  @password ||= Configuration.ask_for_password
65
76
  end
66
77
 
67
78
 
68
- # @return [Octokit::Client]
79
+ # @return [Octokit::Client] the client for communicating with GitHub using {Configuration#user} and {Configuration#auth_token}
69
80
  def client
70
- create_client
81
+ @client ||= create_client
71
82
  end
72
83
 
73
84
 
@@ -77,33 +88,26 @@ module GitHubService
77
88
  end
78
89
 
79
90
 
80
- # @return [Octokit::Client]
81
- def create_client(opts = {})
82
- logger.debug { "Creating GitHub client for user #{user} using token '#{auth_token}'" }
83
-
84
- base_url = opts[:base_url] || base_github_api_url_for_remote
85
-
86
- configure_octokit(:base_url => base_url)
87
-
88
- Octokit::Client.new(:login => user, :oauth_token => auth_token)
89
- end
90
-
91
-
92
91
  #
93
92
  # Configures Octokit to use the appropriate URLs for GitHub server.
94
93
  #
95
- # @param [Hash] opts the options to create a message with
96
- # @option opts [String] :base_url The base URL to use for the GitHub server
94
+ # @option opts [String] :base_url The base URL to use for the GitHub server; defaults to {#base_github_api_url_for_remote}
97
95
  #
98
96
  # @return [void]
99
97
  #
98
+ # @todo remove opts and pass in base_url directly
99
+ #
100
100
  def configure_octokit(opts = {})
101
101
  base_url = opts[:base_url] || base_github_api_url_for_remote
102
102
  Octokit.configure do |c|
103
103
  c.api_endpoint = api_endpoint(base_url)
104
104
  c.web_endpoint = web_endpoint(base_url)
105
- c.faraday_config do |f|
106
- #f.response :logger
105
+ end
106
+ if logger.level < ::GitProc::GitLogger::INFO
107
+ Octokit.middleware = Faraday::RackBuilder.new do |builder|
108
+ builder.response :logger, logger
109
+ builder.use Octokit::Response::RaiseError
110
+ builder.adapter Faraday.default_adapter
107
111
  end
108
112
  end
109
113
  end
@@ -124,7 +128,7 @@ module GitHubService
124
128
  if /github.com/ !~ base_url
125
129
  "#{base_url}/api/v3"
126
130
  else
127
- Octokit::Configuration::DEFAULT_API_ENDPOINT
131
+ Octokit::Default::API_ENDPOINT
128
132
  end
129
133
  end
130
134
 
@@ -144,7 +148,7 @@ module GitHubService
144
148
  if /github.com/ !~ base_url
145
149
  base_url
146
150
  else
147
- Octokit::Configuration::DEFAULT_WEB_ENDPOINT
151
+ Octokit::Default::WEB_ENDPOINT
148
152
  end
149
153
  end
150
154
 
@@ -167,17 +171,24 @@ module GitHubService
167
171
  # @param url [String] the URL to translate
168
172
  # @return [String] the base GitHub API URL
169
173
  #
174
+ # @example
175
+ # url_to_base_github_api_url('git@github.com:jdigger/git-process.git') #=> 'https://api.github.com'
176
+ #
177
+ # url_to_base_github_api_url('http://ghe.myco.com/jdigger/git-process.git') #=> 'http://ghe.myco.com'
178
+ #
179
+ # @todo use Octokit's improved ability to determine this
180
+ #
170
181
  def self.url_to_base_github_api_url(url)
171
182
  uri = URI.parse(url)
172
183
  host = uri.host
173
184
 
174
185
  if /github.com$/ =~ host
175
- 'https://api.github.com'
186
+ return 'https://api.github.com'
176
187
  else
177
188
  scheme = uri.scheme
178
189
  scheme = 'https' unless scheme.start_with?('http')
179
190
  host = 'unknown-host' unless host
180
- "#{scheme}://#{host}"
191
+ return "#{scheme}://#{host}"
181
192
  end
182
193
  end
183
194
 
@@ -192,11 +203,14 @@ module GitHubService
192
203
  # @option opts [String] :user the username to authenticate with
193
204
  # @option opts [String] :password (#password) the password to authenticate with
194
205
  #
206
+ # @return [Octokit::Client] the Octokit client for communicating with GitHub
207
+ #
195
208
  def create_pw_client(opts = {})
196
209
  usr = opts[:user] || user()
197
210
  pw = opts[:password] || password()
211
+ remote = opts[:remote_name] || self.remote_name
198
212
 
199
- logger.debug { "Creating GitHub client for user #{usr} using BasicAuth w/ password" }
213
+ logger.info("Authorizing #{usr} to work with #{remote}.")
200
214
 
201
215
  configure_octokit(opts)
202
216
 
@@ -224,34 +238,72 @@ module GitHubService
224
238
  #
225
239
  # Connects to GitHub to get an OAuth token.
226
240
  #
227
- # @param [Hash] opts
228
241
  # @option opts [String] :base_url The base URL to use for the GitHub server
229
- # @option opts [String] :remote_name (#remote_name) The "remote" name to use (e.g., 'origin')
242
+ # @option opts [String] :remote_name (Configuration#remote_name) The "remote" name to use (e.g., 'origin')
230
243
  # @option opts [String] :user the username to authenticate with
231
244
  # @option opts [String] :password (#password) the password to authenticate with
245
+ # @option opts [String] :two_factor (#password) the password to authenticate with
232
246
  #
233
247
  # @return [String] the OAuth token
234
248
  #
249
+ # noinspection RubyStringKeysInHashInspection
235
250
  def create_authorization(opts = {})
236
- username = opts[:user] || self.user
237
- remote = opts[:remote_name] || self.remote_name
238
- logger.info("Authorizing #{username} to work with #{remote}.")
251
+ client = opts[:client] || create_pw_client(opts)
252
+
253
+ return create_authorization_token(client, opts[:two_factor])
254
+ end
255
+
239
256
 
240
- auth = create_pw_client(opts).create_authorization(
241
- :scopes => %w(repo user gist),
242
- :note => 'Git-Process',
243
- :note_url => 'http://jdigger.github.com/git-process')
257
+ def create_authorization_token(client, two_factor)
258
+ begin
259
+ # noinspection RubyStringKeysInHashInspection
260
+ headers = two_factor ? {'X-GitHub-OTP' => two_factor} : nil
261
+ auth = client.create_authorization(
262
+ :scopes => %w(repo user gist),
263
+ :note => 'Git-Process',
264
+ :note_url => 'http://jdigger.github.com/git-process',
265
+ :headers => headers
266
+ )
267
+ rescue Octokit::OneTimePasswordRequired
268
+ return create_2f_authorization(client)
269
+ rescue Octokit::UnprocessableEntity => exp
270
+ return unprocessable_authorization(exp)
271
+ end
244
272
 
245
- config_auth_token = auth['token']
273
+ config_auth_token = auth[:token]
246
274
 
247
275
  # remember it for next time
248
276
  gitlib.config['gitProcess.github.authToken'] = config_auth_token
249
277
 
250
- config_auth_token
278
+ return config_auth_token
251
279
  end
252
280
 
253
281
 
254
- # @return [String]
282
+ #
283
+ # Connects to GitHub to get an OAuth token.
284
+ #
285
+ # @option opts [String] :base_url The base URL to use for the GitHub server
286
+ # @option opts [String] :remote_name (Configuration#remote_name) The "remote" name to use (e.g., 'origin')
287
+ # @option opts [String] :user the username to authenticate with
288
+ # @option opts [String] :password (#password) the password to authenticate with
289
+ # @option opts [String] :two_factor (#password) the password to authenticate with
290
+ #
291
+ # @return [String] the OAuth token
292
+ #
293
+ # noinspection RubyStringKeysInHashInspection
294
+ def create_2f_authorization(client)
295
+ two_factor = Configuration.ask_for_two_factor
296
+
297
+ create_authorization_token(client, two_factor)
298
+ end
299
+
300
+
301
+ def two_factor_auth(authorization_count, opts)
302
+ return create_authorization(opts.merge(:two_factor => two_factor, :authorization_count => authorization_count + 1))
303
+ end
304
+
305
+
306
+ # @return [String, nil] the OAuth token, or nil if not found
255
307
  def get_config_auth_token
256
308
  c_auth_token = gitlib.config['gitProcess.github.authToken']
257
309
  (c_auth_token.nil? or c_auth_token.empty?) ? nil : c_auth_token
@@ -266,6 +318,24 @@ module GitHubService
266
318
  private
267
319
 
268
320
 
321
+ #
322
+ # Create a client for communicating with GitHub using {Configuration#user} and {Configuration#auth_token}
323
+ #
324
+ # @return [Octokit::Client]
325
+ #
326
+ # @todo have the params passed in explicitly instead of via opts
327
+ #
328
+ def create_client(opts = {})
329
+ logger.debug { "Creating GitHub client for user #{user} using token '#{auth_token}'" }
330
+
331
+ base_url = opts[:base_url] || base_github_api_url_for_remote
332
+
333
+ configure_octokit(:base_url => base_url)
334
+
335
+ Octokit::Client.new(:access_token => auth_token)
336
+ end
337
+
338
+
269
339
  def self.ask_for_user(gitlib)
270
340
  user = gitlib.config['github.user']
271
341
  if user.nil? or user.empty?
@@ -285,6 +355,42 @@ module GitHubService
285
355
  end
286
356
  end
287
357
 
358
+
359
+ def self.ask_for_two_factor
360
+ ask("Your <%= color('GitHub', [:bold, :blue]) %> two-factor code: ") do |q|
361
+ q.validate = /^\w\w+$/
362
+ end
363
+ end
364
+
365
+
366
+ # @todo implement https://github.com/jdigger/git-process/issues/142
367
+ def ask_about_resetting_authorization
368
+ raise TokenAlreadyExists.new("The token already exists. Please check your OAuth settings for your account.")
369
+ end
370
+
371
+
372
+ #
373
+ # Tries to more gracefully handle the token already existing. See
374
+ #
375
+ # @return [String] the OAuth token
376
+ #
377
+ # @raise [TokenAlreadyExists] the token already exists
378
+ # @raise [Octokit::UnprocessableEntity] there was another problem
379
+ def unprocessable_authorization(exp)
380
+ errors = exp.errors
381
+ if not (errors.nil? or errors.empty?)
382
+ error_hash = errors[0]
383
+ if error_hash[:resource] == 'OauthAccess'
384
+ # error_hash[:code]
385
+ return ask_about_resetting_authorization
386
+ else
387
+ raise exp
388
+ end
389
+ else
390
+ raise exp
391
+ end
392
+ end
393
+
288
394
  end
289
395
 
290
396
 
@@ -295,4 +401,7 @@ module GitHubService
295
401
  class NoRemoteRepository < Error
296
402
  end
297
403
 
404
+ class TokenAlreadyExists < Error
405
+ end
406
+
298
407
  end