hub 1.8.4 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of hub might be problematic. Click here for more details.
- data/README.md +3 -18
- data/Rakefile +6 -2
- data/lib/hub.rb +2 -0
- data/lib/hub/commands.rb +51 -155
- data/lib/hub/context.rb +45 -185
- data/lib/hub/github_api.rb +359 -0
- data/lib/hub/json.rb +36 -0
- data/lib/hub/ssh_config.rb +91 -0
- data/lib/hub/standalone.rb +30 -29
- data/lib/hub/version.rb +1 -1
- data/man/hub.1 +3 -47
- data/man/hub.1.html +4 -25
- data/man/hub.1.ronn +3 -21
- data/test/helper.rb +14 -1
- data/test/hub_test.rb +100 -430
- data/test/standalone_test.rb +8 -3
- metadata +10 -8
data/README.md
CHANGED
@@ -302,24 +302,10 @@ superpowers:
|
|
302
302
|
Configuration
|
303
303
|
-------------
|
304
304
|
|
305
|
-
### GitHub
|
305
|
+
### GitHub OAuth authentication
|
306
306
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
To test it run this:
|
311
|
-
|
312
|
-
$ git config --global github.user
|
313
|
-
|
314
|
-
If you see nothing, you need to set the config setting:
|
315
|
-
|
316
|
-
$ git config --global github.user YOUR_USER
|
317
|
-
|
318
|
-
For commands that require write access to GitHub (such as `fork`), you'll want to
|
319
|
-
setup "github.token" as well. See [GitHub config guide][2] for more information.
|
320
|
-
|
321
|
-
If present, environment variables `GITHUB_USER` and `GITHUB_TOKEN` override the
|
322
|
-
values of "github.user" and "github.token".
|
307
|
+
Hub will prompt for GitHub username & password the first time it needs to access
|
308
|
+
the API and exchange it for an OAuth token, which it saves in "~/.config/hub".
|
323
309
|
|
324
310
|
### HTTPS insted of git protocol
|
325
311
|
|
@@ -381,5 +367,4 @@ GitHub simpler:
|
|
381
367
|
|
382
368
|
|
383
369
|
[speed]: http://gist.github.com/284823
|
384
|
-
[2]: http://help.github.com/set-your-user-name-email-and-github-token/
|
385
370
|
[gc]: https://twitter.com/brynary/status/49560668994674688
|
data/Rakefile
CHANGED
@@ -27,7 +27,7 @@ end
|
|
27
27
|
# Tests
|
28
28
|
#
|
29
29
|
|
30
|
-
task :default => :test
|
30
|
+
task :default => [:test, :features]
|
31
31
|
|
32
32
|
Rake::TestTask.new do |t|
|
33
33
|
t.libs << 'test'
|
@@ -36,6 +36,10 @@ Rake::TestTask.new do |t|
|
|
36
36
|
t.verbose = false
|
37
37
|
end
|
38
38
|
|
39
|
+
task :features do
|
40
|
+
sh 'RUBYLIB=lib cucumber -f progress -t ~@wip features'
|
41
|
+
end
|
42
|
+
|
39
43
|
#
|
40
44
|
# Manual
|
41
45
|
#
|
@@ -91,7 +95,7 @@ end
|
|
91
95
|
# Build
|
92
96
|
#
|
93
97
|
|
94
|
-
file "hub" => FileList.new("lib/hub/*.rb", "man/hub.1") do |task|
|
98
|
+
file "hub" => FileList.new("lib/hub.rb", "lib/hub/*.rb", "man/hub.1") do |task|
|
95
99
|
Rake::Task[:load_path].invoke
|
96
100
|
require 'hub/standalone'
|
97
101
|
Hub::Standalone.save(task.name)
|
data/lib/hub.rb
CHANGED
data/lib/hub/commands.rb
CHANGED
@@ -66,6 +66,8 @@ module Hub
|
|
66
66
|
else
|
67
67
|
raise
|
68
68
|
end
|
69
|
+
rescue Context::FatalError => err
|
70
|
+
abort "fatal: #{err.message}"
|
69
71
|
end
|
70
72
|
|
71
73
|
# $ hub pull-request
|
@@ -131,7 +133,7 @@ module Hub
|
|
131
133
|
options[:head] ||= (tracked_branch || current_branch).short_name
|
132
134
|
|
133
135
|
# when no tracking, assume remote branch is published under active user's fork
|
134
|
-
user = github_user(
|
136
|
+
user = github_user(head_project.host)
|
135
137
|
if head_project.owner != user and !tracked_branch and !explicit_owner
|
136
138
|
head_project = head_project.owned_by(user)
|
137
139
|
end
|
@@ -178,12 +180,12 @@ module Hub
|
|
178
180
|
}
|
179
181
|
end
|
180
182
|
|
181
|
-
pull = create_pullrequest(options)
|
183
|
+
pull = api_client.create_pullrequest(options)
|
182
184
|
|
183
185
|
args.executable = 'echo'
|
184
186
|
args.replace [pull['html_url']]
|
185
|
-
rescue
|
186
|
-
|
187
|
+
rescue GitHubAPI::Exceptions
|
188
|
+
display_api_exception("creating pull request", $!.response)
|
187
189
|
exit 1
|
188
190
|
end
|
189
191
|
|
@@ -214,9 +216,9 @@ module Hub
|
|
214
216
|
# FIXME: this logic shouldn't be duplicated here!
|
215
217
|
name, owner = arg, nil
|
216
218
|
owner, name = name.split('/', 2) if name.index('/')
|
217
|
-
host = ENV['GITHUB_HOST']
|
218
|
-
project = Context::GithubProject.new(nil, owner || github_user(
|
219
|
-
ssh ||= args[0] != 'submodule' && project.owner == github_user(
|
219
|
+
host = ENV['GITHUB_HOST'] || 'github.com'
|
220
|
+
project = Context::GithubProject.new(nil, owner || github_user(host), name, host)
|
221
|
+
ssh ||= args[0] != 'submodule' && project.owner == github_user(host) || host != 'github.com'
|
220
222
|
args[idx] = project.git_url(:private => ssh, :https => https_protocol?)
|
221
223
|
end
|
222
224
|
break
|
@@ -319,7 +321,7 @@ module Hub
|
|
319
321
|
projects = names.map { |name|
|
320
322
|
unless name =~ /\W/ or remotes.include?(name) or remotes_group(name)
|
321
323
|
project = github_project(nil, name)
|
322
|
-
project if repo_exists?(project)
|
324
|
+
project if api_client.repo_exists?(project)
|
323
325
|
end
|
324
326
|
}.compact
|
325
327
|
|
@@ -337,21 +339,18 @@ module Hub
|
|
337
339
|
_, url_arg, new_branch_name = args.words
|
338
340
|
if url = resolve_github_url(url_arg) and url.project_path =~ /^pull\/(\d+)/
|
339
341
|
pull_id = $1
|
340
|
-
|
341
|
-
load_net_http
|
342
|
-
response = http_request(url.project.api_pullrequest_url(pull_id, 'json'))
|
343
|
-
pull_data = JSON.parse(response.body)['pull']
|
342
|
+
pull_data = api_client.pullrequest_info(url.project, pull_id)
|
344
343
|
|
345
344
|
args.delete new_branch_name
|
346
345
|
user, branch = pull_data['head']['label'].split(':', 2)
|
347
|
-
abort "Error: #{user}'s fork is not available anymore" unless pull_data['head']['
|
346
|
+
abort "Error: #{user}'s fork is not available anymore" unless pull_data['head']['repo']
|
348
347
|
new_branch_name ||= "#{user}-#{branch}"
|
349
348
|
|
350
349
|
if remotes.include? user
|
351
350
|
args.before ['remote', 'set-branches', '--add', user, branch]
|
352
351
|
args.before ['fetch', user, "+refs/heads/#{branch}:refs/remotes/#{user}/#{branch}"]
|
353
352
|
else
|
354
|
-
url = github_project(url.project_name, user).git_url(:private => pull_data['head']['
|
353
|
+
url = github_project(url.project_name, user).git_url(:private => pull_data['head']['repo']['private'],
|
355
354
|
:https => https_protocol?)
|
356
355
|
args.before ['remote', 'add', '-f', '-t', branch, user, url]
|
357
356
|
end
|
@@ -426,8 +425,8 @@ module Hub
|
|
426
425
|
if args.delete('-g')
|
427
426
|
# can't use default_host because there is no local_repo yet
|
428
427
|
# FIXME: this shouldn't be here!
|
429
|
-
host = ENV['GITHUB_HOST']
|
430
|
-
project = Context::GithubProject.new(nil, github_user(
|
428
|
+
host = ENV['GITHUB_HOST'] || 'github.com'
|
429
|
+
project = Context::GithubProject.new(nil, github_user(host), File.basename(current_dir), host)
|
431
430
|
url = project.git_url(:private => true, :https => https_protocol?)
|
432
431
|
args.after ['remote', 'add', 'origin', url]
|
433
432
|
end
|
@@ -440,13 +439,13 @@ module Hub
|
|
440
439
|
unless project = local_repo.main_project
|
441
440
|
abort "Error: repository under 'origin' remote is not a GitHub project"
|
442
441
|
end
|
443
|
-
forked_project = project.owned_by(github_user(
|
442
|
+
forked_project = project.owned_by(github_user(project.host))
|
444
443
|
|
445
|
-
if repo_exists?(forked_project)
|
444
|
+
if api_client.repo_exists?(forked_project)
|
446
445
|
abort "Error creating fork: %s already exists on %s" %
|
447
446
|
[ forked_project.name_with_owner, forked_project.host ]
|
448
447
|
else
|
449
|
-
fork_repo(project) unless args.noop?
|
448
|
+
api_client.fork_repo(project) unless args.noop?
|
450
449
|
end
|
451
450
|
|
452
451
|
if args.include?('--no-remote')
|
@@ -456,8 +455,8 @@ module Hub
|
|
456
455
|
args.replace %W"remote add -f #{forked_project.owner} #{url}"
|
457
456
|
args.after 'echo', ['new remote:', forked_project.owner]
|
458
457
|
end
|
459
|
-
rescue
|
460
|
-
|
458
|
+
rescue GitHubAPI::Exceptions
|
459
|
+
display_api_exception("creating fork", $!.response)
|
461
460
|
exit 1
|
462
461
|
end
|
463
462
|
|
@@ -467,7 +466,7 @@ module Hub
|
|
467
466
|
def create(args)
|
468
467
|
if !is_repo?
|
469
468
|
abort "'create' must be run from inside a git repository"
|
470
|
-
elsif owner = github_user
|
469
|
+
elsif owner = github_user
|
471
470
|
args.shift
|
472
471
|
options = {}
|
473
472
|
options[:private] = true if args.delete('-p')
|
@@ -491,12 +490,12 @@ module Hub
|
|
491
490
|
new_repo_name ||= repo_name
|
492
491
|
new_project = github_project(new_repo_name, owner)
|
493
492
|
|
494
|
-
if repo_exists?(new_project)
|
493
|
+
if api_client.repo_exists?(new_project)
|
495
494
|
warn "#{new_project.name_with_owner} already exists on #{new_project.host}"
|
496
495
|
action = "set remote origin"
|
497
496
|
else
|
498
497
|
action = "created repository"
|
499
|
-
create_repo(new_project, options) unless args.noop?
|
498
|
+
api_client.create_repo(new_project, options) unless args.noop?
|
500
499
|
end
|
501
500
|
|
502
501
|
url = new_project.git_url(:private => true, :https => https_protocol?)
|
@@ -509,8 +508,8 @@ module Hub
|
|
509
508
|
|
510
509
|
args.after 'echo', ["#{action}:", new_project.name_with_owner]
|
511
510
|
end
|
512
|
-
rescue
|
513
|
-
|
511
|
+
rescue GitHubAPI::Exceptions
|
512
|
+
display_api_exception("creating repository", $!.response)
|
514
513
|
exit 1
|
515
514
|
end
|
516
515
|
|
@@ -618,10 +617,12 @@ module Hub
|
|
618
617
|
def hub(args)
|
619
618
|
return help(args) unless args[1] == 'standalone'
|
620
619
|
require 'hub/standalone'
|
621
|
-
|
620
|
+
Hub::Standalone.build $stdout
|
622
621
|
exit
|
623
622
|
rescue LoadError
|
624
|
-
abort "hub is running in standalone mode."
|
623
|
+
abort "hub is already running in standalone mode."
|
624
|
+
rescue Errno::EPIPE
|
625
|
+
exit # ignore broken pipe
|
625
626
|
end
|
626
627
|
|
627
628
|
def alias(args)
|
@@ -692,6 +693,21 @@ module Hub
|
|
692
693
|
# from the command line.
|
693
694
|
#
|
694
695
|
|
696
|
+
def api_client
|
697
|
+
@api_client ||= begin
|
698
|
+
config_file = ENV['HUB_CONFIG'] || '~/.config/hub'
|
699
|
+
file_store = GitHubAPI::FileStore.new File.expand_path(config_file)
|
700
|
+
file_config = GitHubAPI::Configuration.new file_store
|
701
|
+
GitHubAPI.new file_config, :app_url => 'http://defunkt.io/hub/'
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
705
|
+
def github_user host = nil
|
706
|
+
host ||= local_repo(false) && local_repo.main_host
|
707
|
+
return nil if host.nil?
|
708
|
+
api_client.config.username(host) { }
|
709
|
+
end
|
710
|
+
|
695
711
|
def custom_command? cmd
|
696
712
|
CUSTOM_COMMANDS.include? cmd
|
697
713
|
end
|
@@ -898,53 +914,6 @@ help
|
|
898
914
|
end
|
899
915
|
end
|
900
916
|
|
901
|
-
# Determines whether a user has a fork of the current repo on GitHub.
|
902
|
-
def repo_exists?(project)
|
903
|
-
load_net_http
|
904
|
-
Net::HTTPSuccess === http_request(project.api_show_url('yaml'))
|
905
|
-
end
|
906
|
-
|
907
|
-
# Forks the current repo using the GitHub API.
|
908
|
-
#
|
909
|
-
# Returns nothing.
|
910
|
-
def fork_repo(project)
|
911
|
-
load_net_http
|
912
|
-
response = http_post project.api_fork_url('yaml')
|
913
|
-
response.error! unless Net::HTTPSuccess === response
|
914
|
-
end
|
915
|
-
|
916
|
-
# Creates a new repo using the GitHub API.
|
917
|
-
#
|
918
|
-
# Returns nothing.
|
919
|
-
def create_repo(project, options = {})
|
920
|
-
is_org = project.owner != github_user(true, project.host)
|
921
|
-
params = {'name' => is_org ? project.name_with_owner : project.name}
|
922
|
-
params['public'] = '0' if options[:private]
|
923
|
-
params['description'] = options[:description] if options[:description]
|
924
|
-
params['homepage'] = options[:homepage] if options[:homepage]
|
925
|
-
|
926
|
-
load_net_http
|
927
|
-
response = http_post(project.api_create_url('json'), params)
|
928
|
-
response.error! unless Net::HTTPSuccess === response
|
929
|
-
end
|
930
|
-
|
931
|
-
# Returns parsed data from the new pull request.
|
932
|
-
def create_pullrequest(options)
|
933
|
-
project = options.fetch(:project)
|
934
|
-
params = {
|
935
|
-
'pull[base]' => options.fetch(:base),
|
936
|
-
'pull[head]' => options.fetch(:head)
|
937
|
-
}
|
938
|
-
params['pull[issue]'] = options[:issue] if options[:issue]
|
939
|
-
params['pull[title]'] = options[:title] if options[:title]
|
940
|
-
params['pull[body]'] = options[:body] if options[:body]
|
941
|
-
|
942
|
-
load_net_http
|
943
|
-
response = http_post(project.api_create_pullrequest_url('json'), params)
|
944
|
-
response.error! unless Net::HTTPSuccess === response
|
945
|
-
JSON.parse(response.body)['pull']
|
946
|
-
end
|
947
|
-
|
948
917
|
def pullrequest_editmsg(changes)
|
949
918
|
message_file = File.join(git_dir, 'PULLREQ_EDITMSG')
|
950
919
|
File.open(message_file, 'w') { |msg|
|
@@ -988,86 +957,13 @@ help
|
|
988
957
|
end
|
989
958
|
end
|
990
959
|
|
991
|
-
def
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
http = setup_http(url)
|
999
|
-
|
1000
|
-
yield req if block_given?
|
1001
|
-
http.start { http.request(req) }
|
1002
|
-
end
|
1003
|
-
|
1004
|
-
def http_post(url, params = nil)
|
1005
|
-
http_request(url, :Post) do |req|
|
1006
|
-
req.set_form_data params if params
|
1007
|
-
req['Content-Length'] = req.body ? req.body.length : 0
|
1008
|
-
end
|
1009
|
-
end
|
1010
|
-
|
1011
|
-
def setup_http(url)
|
1012
|
-
port = url.port
|
1013
|
-
if use_ssl = 'https' == url.scheme and not use_ssl?
|
1014
|
-
# ruby compiled without openssl
|
1015
|
-
use_ssl = false
|
1016
|
-
port = 80
|
1017
|
-
end
|
1018
|
-
|
1019
|
-
http_args = [url.host, port]
|
1020
|
-
if proxy = proxy_url(use_ssl)
|
1021
|
-
http_args.concat proxy.select(:host, :port)
|
1022
|
-
if proxy.userinfo
|
1023
|
-
require 'cgi'
|
1024
|
-
http_args.concat proxy.userinfo.split(':', 2).map {|a| CGI.unescape a }
|
1025
|
-
end
|
1026
|
-
end
|
1027
|
-
|
1028
|
-
http = Net::HTTP.new(*http_args)
|
1029
|
-
|
1030
|
-
if http.use_ssl = use_ssl
|
1031
|
-
# TODO: SSL peer verification
|
1032
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
1033
|
-
end
|
1034
|
-
return http
|
1035
|
-
end
|
1036
|
-
|
1037
|
-
def load_net_http
|
1038
|
-
require 'net/https'
|
1039
|
-
rescue LoadError
|
1040
|
-
require 'net/http'
|
1041
|
-
end
|
1042
|
-
|
1043
|
-
def use_ssl?
|
1044
|
-
defined? ::OpenSSL
|
1045
|
-
end
|
1046
|
-
|
1047
|
-
def proxy_url(use_ssl)
|
1048
|
-
env_name = "HTTP#{use_ssl ? 'S' : ''}_PROXY"
|
1049
|
-
if proxy = ENV[env_name] || ENV[env_name.downcase]
|
1050
|
-
proxy = "http://#{proxy}" unless proxy.include? '://'
|
1051
|
-
URI.parse(proxy)
|
1052
|
-
end
|
1053
|
-
end
|
1054
|
-
|
1055
|
-
# Fake exception type for net/http exception handling.
|
1056
|
-
# Necessary because net/http may or may not be loaded at the time.
|
1057
|
-
module HTTPExceptions
|
1058
|
-
def self.===(exception)
|
1059
|
-
exception.class.ancestors.map {|a| a.to_s }.include? 'Net::HTTPExceptions'
|
1060
|
-
end
|
1061
|
-
end
|
1062
|
-
|
1063
|
-
def display_http_exception(action, response)
|
1064
|
-
$stderr.puts "Error #{action}: #{response.message} (HTTP #{response.code})"
|
1065
|
-
case response.code.to_i
|
1066
|
-
when 401 then warn "Check your token configuration (`git config github.token`)"
|
1067
|
-
when 422
|
1068
|
-
if response.content_type =~ /\bjson\b/ and data = JSON.parse(response.body) and data["error"]
|
1069
|
-
$stderr.puts data["error"]
|
1070
|
-
end
|
960
|
+
def display_api_exception(action, response)
|
961
|
+
$stderr.puts "Error #{action}: #{response.message} (HTTP #{response.status})"
|
962
|
+
if 422 == response.status and response.error_message?
|
963
|
+
# display validation errors
|
964
|
+
msg = response.error_message
|
965
|
+
msg = msg.join("\n") if msg.respond_to? :join
|
966
|
+
warn msg
|
1071
967
|
end
|
1072
968
|
end
|
1073
969
|
|
data/lib/hub/context.rb
CHANGED
@@ -3,8 +3,8 @@ require 'forwardable'
|
|
3
3
|
require 'uri'
|
4
4
|
|
5
5
|
module Hub
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# Methods for inspecting the environment, such as reading git config,
|
7
|
+
# repository info, and other.
|
8
8
|
module Context
|
9
9
|
extend Forwardable
|
10
10
|
|
@@ -75,6 +75,9 @@ module Hub
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
class Error < RuntimeError; end
|
79
|
+
class FatalError < Error; end
|
80
|
+
|
78
81
|
private
|
79
82
|
|
80
83
|
def git_reader
|
@@ -89,7 +92,7 @@ module Hub
|
|
89
92
|
if is_repo?
|
90
93
|
LocalRepo.new git_reader, current_dir
|
91
94
|
elsif fatal
|
92
|
-
|
95
|
+
raise FatalError, "Not a git repository"
|
93
96
|
end
|
94
97
|
end
|
95
98
|
end
|
@@ -243,30 +246,6 @@ module Hub
|
|
243
246
|
else "git://#{host}/"
|
244
247
|
end + name_with_owner + '.git'
|
245
248
|
end
|
246
|
-
|
247
|
-
def api_url(type, resource, action)
|
248
|
-
URI("https://#{host}/api/v2/#{type}/#{resource}/#{action}")
|
249
|
-
end
|
250
|
-
|
251
|
-
def api_show_url(type)
|
252
|
-
api_url(type, 'repos', "show/#{owner}/#{name}")
|
253
|
-
end
|
254
|
-
|
255
|
-
def api_fork_url(type)
|
256
|
-
api_url(type, 'repos', "fork/#{owner}/#{name}")
|
257
|
-
end
|
258
|
-
|
259
|
-
def api_create_url(type)
|
260
|
-
api_url(type, 'repos', 'create')
|
261
|
-
end
|
262
|
-
|
263
|
-
def api_pullrequest_url(id, type)
|
264
|
-
api_url(type, 'pulls', "#{owner}/#{name}/#{id}")
|
265
|
-
end
|
266
|
-
|
267
|
-
def api_create_pullrequest_url(type)
|
268
|
-
api_url(type, 'pulls', "#{owner}/#{name}")
|
269
|
-
end
|
270
249
|
end
|
271
250
|
|
272
251
|
class GithubURL < URI::HTTPS
|
@@ -320,7 +299,7 @@ module Hub
|
|
320
299
|
|
321
300
|
def remote_name
|
322
301
|
name =~ %r{^refs/remotes/([^/]+)} and $1 or
|
323
|
-
raise "can't get remote name from #{name.inspect}"
|
302
|
+
raise Error, "can't get remote name from #{name.inspect}"
|
324
303
|
end
|
325
304
|
end
|
326
305
|
|
@@ -391,44 +370,6 @@ module Hub
|
|
391
370
|
GithubURL.resolve(url, local_repo) if url =~ /^https?:/
|
392
371
|
end
|
393
372
|
|
394
|
-
LGHCONF = "http://help.github.com/set-your-user-name-email-and-github-token/"
|
395
|
-
|
396
|
-
# Either returns the GitHub user as set by git-config(1) or aborts
|
397
|
-
# with an error message.
|
398
|
-
def github_user(fatal = true, host = nil)
|
399
|
-
if local = local_repo(false)
|
400
|
-
host ||= local.default_host
|
401
|
-
host = nil if host == local.main_host
|
402
|
-
end
|
403
|
-
host = %(."#{host}") if host
|
404
|
-
if user = ENV['GITHUB_USER'] || git_config("github#{host}.user")
|
405
|
-
user
|
406
|
-
elsif fatal
|
407
|
-
if host.nil?
|
408
|
-
abort("** No GitHub user set. See #{LGHCONF}")
|
409
|
-
else
|
410
|
-
abort("** No user set for github#{host}")
|
411
|
-
end
|
412
|
-
end
|
413
|
-
end
|
414
|
-
|
415
|
-
def github_token(fatal = true, host = nil)
|
416
|
-
if local = local_repo(false)
|
417
|
-
host ||= local.default_host
|
418
|
-
host = nil if host == local.main_host
|
419
|
-
end
|
420
|
-
host = %(."#{host}") if host
|
421
|
-
if token = ENV['GITHUB_TOKEN'] || git_config("github#{host}.token")
|
422
|
-
token
|
423
|
-
elsif fatal
|
424
|
-
if host.nil?
|
425
|
-
abort("** No GitHub token set. See #{LGHCONF}")
|
426
|
-
else
|
427
|
-
abort("** No token set for github#{host}")
|
428
|
-
end
|
429
|
-
end
|
430
|
-
end
|
431
|
-
|
432
373
|
# legacy setting
|
433
374
|
def http_clone?
|
434
375
|
git_config('--bool hub.http-clone') == 'true'
|
@@ -468,136 +409,55 @@ module Hub
|
|
468
409
|
editor.shellsplit
|
469
410
|
end
|
470
411
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
abort "Please set $BROWSER to a web launcher to use this command." unless browser
|
481
|
-
Array(browser)
|
482
|
-
end
|
483
|
-
|
484
|
-
def osx?
|
485
|
-
require 'rbconfig'
|
486
|
-
RbConfig::CONFIG['host_os'].to_s.include?('darwin')
|
487
|
-
end
|
412
|
+
module System
|
413
|
+
# Cross-platform web browser command; respects the value set in $BROWSER.
|
414
|
+
#
|
415
|
+
# Returns an array, e.g.: ['open']
|
416
|
+
def browser_launcher
|
417
|
+
browser = ENV['BROWSER'] || (
|
418
|
+
osx? ? 'open' : windows? ? 'start' :
|
419
|
+
%w[xdg-open cygstart x-www-browser firefox opera mozilla netscape].find { |comm| which comm }
|
420
|
+
)
|
488
421
|
|
489
|
-
|
490
|
-
|
491
|
-
RbConfig::CONFIG['host_os'] =~ /msdos|mswin|djgpp|mingw|windows/
|
492
|
-
end
|
493
|
-
|
494
|
-
# Cross-platform way of finding an executable in the $PATH.
|
495
|
-
#
|
496
|
-
# which('ruby') #=> /usr/bin/ruby
|
497
|
-
def which(cmd)
|
498
|
-
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
499
|
-
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
500
|
-
exts.each { |ext|
|
501
|
-
exe = "#{path}/#{cmd}#{ext}"
|
502
|
-
return exe if File.executable? exe
|
503
|
-
}
|
422
|
+
abort "Please set $BROWSER to a web launcher to use this command." unless browser
|
423
|
+
Array(browser)
|
504
424
|
end
|
505
|
-
return nil
|
506
|
-
end
|
507
425
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
#
|
512
|
-
# Returns a Boolean.
|
513
|
-
def command?(name)
|
514
|
-
!which(name).nil?
|
515
|
-
end
|
516
|
-
|
517
|
-
class SshConfig
|
518
|
-
CONFIG_FILES = %w(~/.ssh/config /etc/ssh_config /etc/ssh/ssh_config)
|
519
|
-
|
520
|
-
def initialize files = nil
|
521
|
-
@settings = Hash.new {|h,k| h[k] = {} }
|
522
|
-
Array(files || CONFIG_FILES).each do |path|
|
523
|
-
file = File.expand_path path
|
524
|
-
parse_file file if File.exist? file
|
525
|
-
end
|
426
|
+
def osx?
|
427
|
+
require 'rbconfig'
|
428
|
+
RbConfig::CONFIG['host_os'].to_s.include?('darwin')
|
526
429
|
end
|
527
430
|
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
@settings.each do |pattern, settings|
|
532
|
-
if pattern.match? hostname and found = settings[key]
|
533
|
-
return found
|
534
|
-
end
|
535
|
-
end
|
536
|
-
yield
|
537
|
-
end
|
538
|
-
|
539
|
-
class HostPattern
|
540
|
-
def initialize pattern
|
541
|
-
@pattern = pattern.to_s.downcase
|
542
|
-
end
|
543
|
-
|
544
|
-
def to_s() @pattern end
|
545
|
-
def ==(other) other.to_s == self.to_s end
|
546
|
-
|
547
|
-
def matcher
|
548
|
-
@matcher ||=
|
549
|
-
if '*' == @pattern
|
550
|
-
Proc.new { true }
|
551
|
-
elsif @pattern !~ /[?*]/
|
552
|
-
lambda { |hostname| hostname.to_s.downcase == @pattern }
|
553
|
-
else
|
554
|
-
re = self.class.pattern_to_regexp @pattern
|
555
|
-
lambda { |hostname| re =~ hostname }
|
556
|
-
end
|
557
|
-
end
|
558
|
-
|
559
|
-
def match? hostname
|
560
|
-
matcher.call hostname
|
561
|
-
end
|
562
|
-
|
563
|
-
def self.pattern_to_regexp pattern
|
564
|
-
escaped = Regexp.escape(pattern)
|
565
|
-
escaped.gsub!('\*', '.*')
|
566
|
-
escaped.gsub!('\?', '.')
|
567
|
-
/^#{escaped}$/i
|
568
|
-
end
|
431
|
+
def windows?
|
432
|
+
require 'rbconfig'
|
433
|
+
RbConfig::CONFIG['host_os'] =~ /msdos|mswin|djgpp|mingw|windows/
|
569
434
|
end
|
570
435
|
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
end
|
582
|
-
|
583
|
-
next if value.nil?
|
584
|
-
key.downcase!
|
585
|
-
value = $1 if value =~ /^"(.*)"$/
|
586
|
-
value.chomp!
|
587
|
-
|
588
|
-
if 'host' == key
|
589
|
-
host_patterns = value.split(/\s+/).map {|p| HostPattern.new p }
|
590
|
-
else
|
591
|
-
record_setting key, value, host_patterns
|
592
|
-
end
|
436
|
+
# Cross-platform way of finding an executable in the $PATH.
|
437
|
+
#
|
438
|
+
# which('ruby') #=> /usr/bin/ruby
|
439
|
+
def which(cmd)
|
440
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
441
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
442
|
+
exts.each { |ext|
|
443
|
+
exe = "#{path}/#{cmd}#{ext}"
|
444
|
+
return exe if File.executable? exe
|
445
|
+
}
|
593
446
|
end
|
447
|
+
return nil
|
594
448
|
end
|
595
449
|
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
450
|
+
# Checks whether a command exists on this system in the $PATH.
|
451
|
+
#
|
452
|
+
# name - The String name of the command to check for.
|
453
|
+
#
|
454
|
+
# Returns a Boolean.
|
455
|
+
def command?(name)
|
456
|
+
!which(name).nil?
|
600
457
|
end
|
601
458
|
end
|
459
|
+
|
460
|
+
include System
|
461
|
+
extend System
|
602
462
|
end
|
603
463
|
end
|