class Travis::Tools::Github

def acceptable?(token)

def acceptable?(token)
  return true unless check_token
  gh = GH.with(token:)
  gh['user']
  true
rescue GH::Error => e
  debug "token is not acceptable: #{gh_error(e)}"
  false
end

def api_host

def api_host
  return GITHUB_API unless api_url
  api_url[%r{^(?:https?://)?([^/]+)}, 1]
end

def composer_token

def composer_token
  file(composer_path) do |content|
    token = JSON.parse(content)['config'].fetch('github-oauth', {})[host]
    yield token if token
  end
end

def debug(line)

def debug(line)
  return unless @debug
  @debug.call "Tools::Github: #{line}"
end

def each_token

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
end

def file(path, default = nil)

def file(path, default = nil)
  path        &&= File.expand_path(path)
  @file       ||= {}
  @file[path] ||= if path && File.readable?(path)
                    debug "reading #{path}"
                    yield File.read(path)
                  end
  @file[path] || default
rescue StandardError => e
  raise e if explode
end

def gh_error(error)

def gh_error(error)
  raise error if explode
  if error.info.key? :response_body
    JSON.parse(error.info[:response_body])['message'].to_s
  else
    'Unknown error'
  end
end

def git_tokens

def git_tokens
  return unless System.has? 'git'
  git_config_keys.each do |key|
    `git config --get-all #{key}`.each_line do |line|
      token = line.strip
      yield token unless token.empty?
    end
  end
end

def github_for_mac_token(&block)

def github_for_mac_token(&block)
  command = '-s "github.com/mac"'
  security(:internet, :w, command, 'GitHub for Mac token', &block) if host == 'github.com'
end

def host

def host
  api_host == GITHUB_API ? GITHUB_HOST : api_host
end

def hub

def hub
  file(hub_path, {}) do |contents|
    YAML.load(contents)
  end
end

def hub_tokens

def hub_tokens
  hub.fetch(host, []).each do |entry|
    yield entry['oauth_token'] if entry['oauth_token']
  end
end

def initialize(options = nil)

def initialize(options = nil)
  @check_token     = true
  @ask_login       = proc { raise 'ask_login callback not set' }
  @after_tokens    = proc {}
  @debug           = proc { |_| }
  @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}=" }
  yield self if block_given?
end

def issuepost_token(&block)

def issuepost_token(&block)
  security(:generic, :w, '-l issuepost.github.access_token', 'issuepost token', &block) if host == 'github.com'
end

def oauth_file_tokens

def oauth_file_tokens
  oauth_paths.each do |path|
    file(path) do |content|
      token = content.strip
      yield token unless token.empty?
    end
  end
end

def possible_tokens(&block)

def possible_tokens(&block)
  return block[github_token] if github_token
  if auto_token
    git_tokens(&block)
    hub_tokens(&block)
    oauth_file_tokens(&block)
    github_for_mac_token(&block)
    issuepost_token(&block)
    composer_token(&block)
  end
  if github_token || auto_token
    after_tokens.call
  elsif login_header
    login_header.call
  end
end

def security(type, key, arg, name)

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 = `security find-#{type}-password #{arg} -#{key} 2>&1`.chomp
  $CHILD_STATUS.success? ? yield(result) : debug('request denied')
rescue StandardError => e
  raise e if explode
end

def with_session

def with_session
  with_token { |t| GH.with(token: t) { yield(t) } }
end

def with_token

def with_token
  each_token { |t| break yield(t) }
end