class Travis::Tools::Github

Constants

GITHUB_API
GITHUB_HOST
TOKEN_SIZE

Attributes

after_tokens[RW]
api_url[RW]
ask_login[RW]
ask_otp[RW]
ask_password[RW]
auto_password[RW]
auto_token[RW]
callback[RW]
check_token[RW]
composer_path[RW]
debug[RW]
drop_token[RW]
explode[RW]
git_config_keys[RW]
github_login[RW]
github_token[RW]
hub_path[RW]
login_header[RW]
manual_login[RW]
netrc_path[RW]
no_token[RW]
note[RW]
oauth_paths[RW]
scopes[RW]

Public Class Methods

new(options = nil) { |self| ... } click to toggle source
# File lib/travis/tools/github.rb, line 16
def initialize(options = nil)
  @check_token     = true
  @manual_login    = true
  @ask_login       = proc { raise "ask_login callback not set" }
  @after_tokens    = proc { }
  @ask_password    = proc { |_| raise "ask_password callback not set" }
  @ask_otp         = proc { |_| raise "ask_otp callback not set" }
  @debug           = proc { |_| }
  @netrc_path      = '~/.netrc'
  @hub_path        = ENV['HUB_CONFIG'] || '~/.config/hub'
  @oauth_paths     = ['~/.github-oauth-token']
  @composer_path   = "~/.composer/config.json"
  @note            = 'temporary token'
  @git_config_keys = %w[github.token github.oauth-token]
  @scopes          = ['user', 'user:email', 'repo'] # overridden by value from /config
  options.each_pair { |k,v| send("#{k}=", v) if respond_to? "#{k}=" } if options
  yield self if block_given?
end

Public Instance Methods

acceptable?(token) click to toggle source
# File lib/travis/tools/github.rb, line 228
def acceptable?(token)
  return true unless check_token
  gh   = GH.with(:token => token)
  user = gh['user']

  if github_login and github_login != user['login']
    debug "token is not acceptable: identifies %p instead of %p" % [user['login'], github_login]
    false
  else
    true
  end
rescue GH::Error => error
  debug "token is not acceptable: #{gh_error(error)}"
  false
end
api_host() click to toggle source
# File lib/travis/tools/github.rb, line 202
def api_host
  return GITHUB_API unless api_url
  api_url[%r{^(?:https?://)?([^/]+)}, 1]
end
ask_credentials() click to toggle source
# File lib/travis/tools/github.rb, line 86
def ask_credentials
  login_header.call if login_header
  user     = github_login || ask_login.call
  password = ask_password.arity == 0 ? ask_password.call : ask_password.call(user)
  [user, password]
end
basic_auth(user, password, die = true, otp = nil) { |with| ... } click to toggle source
# File lib/travis/tools/github.rb, line 207
def basic_auth(user, password, die = true, otp = nil, &block)
  opt           = { :username => user, :password => password }
  opt[:headers] = { "X-GitHub-OTP" => otp } if otp
  yield GH.with(opt)
rescue GH::Error => error
  if error.info[:response_status] == 401 and error.info[:response_headers]['x-github-otp'].to_s =~ /required/
    otp = ask_otp.arity == 0 ? ask_otp.call : ask_otp.call(user)
    basic_auth(user, password, die, otp, &block)
  elsif die
    raise gh_error(error)
  end
end
composer_token() { |token| ... } click to toggle source
# File lib/travis/tools/github.rb, line 120
def composer_token
  file(composer_path) do |content|
    token = JSON.parse(content)['config'].fetch('github-oauth', {})[host]
    yield token if token
  end
end
each_token() { |t| ... } click to toggle source
# File lib/travis/tools/github.rb, line 47
def each_token
  require 'gh' unless defined? GH
  possible_tokens { |t| yield(t) if acceptable?(t) }
ensure
  callback, self.callback = self.callback, nil
  callback.call if callback
end
git_tokens() { |token| ... } click to toggle source
# File lib/travis/tools/github.rb, line 110
def git_tokens
  return unless System.has? 'git'
  git_config_keys.each do |key|
    %x`git config --get-all #{key}`.each_line do |line|
      token = line.strip
      yield token unless token.empty?
    end
  end
end
github_for_mac_token(&block) click to toggle source
# File lib/travis/tools/github.rb, line 192
def github_for_mac_token(&block)
  command = '-s "github.com/mac"'
  command << " -a #{github_login}" if github_login
  security(:internet, :w, command, "GitHub for Mac token", &block) if host == 'github.com'
end
host() click to toggle source
# File lib/travis/tools/github.rb, line 198
def host
  api_host == GITHUB_API ? GITHUB_HOST : api_host
end
hub() click to toggle source
# File lib/travis/tools/github.rb, line 182
def hub
  file(hub_path, {}) do |contents|
    YAML.load(contents)
  end
end
hub_logins() { |entry, entry| ... } click to toggle source
# File lib/travis/tools/github.rb, line 151
def hub_logins
  hub.fetch(host, []).each do |entry|
    next if github_login and github_login != entry["user"]
    yield entry["user"], entry["password"] if entry["user"] and entry["password"]
  end
end
hub_tokens() { |entry| ... } click to toggle source
# File lib/travis/tools/github.rb, line 127
def hub_tokens
  hub.fetch(host, []).each do |entry|
    next if github_login and github_login != entry["user"]
    yield entry["oauth_token"] if entry["oauth_token"]
  end
end
issuepost_token(&block) click to toggle source
# File lib/travis/tools/github.rb, line 188
def issuepost_token(&block)
  security(:generic, :w, "-l issuepost.github.access_token",  "issuepost token", &block) if host == 'github.com'
end
keychain_login() { |github_login, password| ... } click to toggle source
# File lib/travis/tools/github.rb, line 158
def keychain_login
  if github_login
    security(:internet, :w, "-s #{host} -a #{github_login}", "#{host} password for #{github_login}") do |password|
      yield github_login, password if password and not password.empty?
    end
  else
    security(:internet, :g, "-s #{host}", "#{host} login and password") do |data|
      username = data[/^\s+"acct"<blob>="(.*)"$/, 1].to_s
      password = data[/^password: "(.*)"$/, 1].to_s
      yield username, password unless username.empty? or password.empty?
    end
  end
end
login(user, password, die = true, otp = nil) click to toggle source
# File lib/travis/tools/github.rb, line 220
def login(user, password, die = true, otp = nil)
  basic_auth(user, password, die, otp) do |gh|
    reply         = gh.post('/authorizations', :scopes => scopes, :note => note)
    self.callback = proc { gh.delete reply['_links']['self']['href'] } if drop_token
    reply['token']
  end
end
netrc() click to toggle source
# File lib/travis/tools/github.rb, line 172
def netrc
  file(netrc_path, []) do |contents|
    contents.scan(/^\s*(\S+)\s+(\S+)\s*$/).inject([]) do |mapping, (key, value)|
      mapping << {} if key == "machine"
      mapping.last[key] = value if mapping.last
      mapping
    end
  end
end
netrc_logins() { |entry, entry| ... } click to toggle source
# File lib/travis/tools/github.rb, line 143
def netrc_logins
  netrc.each do |entry|
    next unless entry["machine"] == api_host or entry["machine"] == host
    next if github_login and github_login != entry["login"]
    yield entry["login"], entry["password"] if entry["login"] and entry["password"]
  end
end
netrc_tokens() { |entry| ... } click to toggle source
# File lib/travis/tools/github.rb, line 99
def netrc_tokens
  netrc.each do |entry|
    next unless entry["machine"] == api_host or entry["machine"] == host
    entry.values_at("token", "login", "password").each do |entry|
      next if entry.to_s.size != TOKEN_SIZE
      debug "found oauth token in netrc"
      yield entry
    end
  end
end
oauth_file_tokens() { |token| ... } click to toggle source
# File lib/travis/tools/github.rb, line 134
def oauth_file_tokens(&block)
  oauth_paths.each do |path|
    file(path) do |content|
      token = content.strip
      yield token unless token.empty?
    end
  end
end
possible_logins(&block) click to toggle source
# File lib/travis/tools/github.rb, line 93
def possible_logins(&block)
  netrc_logins(&block)
  hub_logins(&block)
  keychain_login(&block)
end
possible_tokens() { |login(user, password, false)| ... } click to toggle source
# File lib/travis/tools/github.rb, line 59
def possible_tokens(&block)
  return block[github_token] if github_token

  if auto_token
    netrc_tokens(&block)
    git_tokens(&block)
    hub_tokens(&block)
    oauth_file_tokens(&block)
    github_for_mac_token(&block)
    issuepost_token(&block)
    composer_token(&block)
  end

  if auto_password
    possible_logins do |user, password|
      yield login(user, password, false)
    end
  end

  if manual_login
    user, password = ask_credentials
    yield login(user, password, true)
  end

  after_tokens.call
end
with_basic_auth() { |gh| ... } click to toggle source
# File lib/travis/tools/github.rb, line 39
def with_basic_auth(&block)
  user, password = ask_credentials
  basic_auth(user, password, true) do |gh|
    gh['user'] # so otp kicks in
    yield gh
  end
end
with_session() { |t| ... } click to toggle source
# File lib/travis/tools/github.rb, line 55
def with_session(&block)
  with_token { |t| GH.with(:token => t) { yield(t) } }
end
with_token() { |t| ... } click to toggle source
# File lib/travis/tools/github.rb, line 35
def with_token
  each_token { |t| break yield(t) }
end

Private Instance Methods

file(path, default = nil) { |read| ... } click to toggle source
# File lib/travis/tools/github.rb, line 266
def file(path, default = nil)
  path        &&= File.expand_path(path)
  @file       ||= {}
  @file[path] ||= if path and File.readable?(path)
    debug "reading #{path}"
    yield File.read(path)
  end
  @file[path] || default
rescue => e
  raise e if explode
end
gh_error(error) click to toggle source
# File lib/travis/tools/github.rb, line 246
def gh_error(error)
  raise error if explode
  JSON.parse(error.info[:response_body])["message"].to_s
end
security(type, key, arg, name) { |result| ... } click to toggle source
# File lib/travis/tools/github.rb, line 256
def security(type, key, arg, name)
  return false unless System.has? 'security'
  return false unless system "security find-#{type}-password #{arg} 2>/dev/null >/dev/null"
  debug "requesting to load #{name} from keychain"
  result = %x[security find-#{type}-password #{arg} -#{key} 2>&1].chomp
  $?.success? ? yield(result) : debug("request denied")
rescue => e
  raise e if explode
end