diff options
| -rw-r--r-- | Library/Homebrew/cmd/gist-logs.rb | 19 | ||||
| -rw-r--r-- | Library/Homebrew/utils.rb | 76 | 
2 files changed, 76 insertions, 19 deletions
diff --git a/Library/Homebrew/cmd/gist-logs.rb b/Library/Homebrew/cmd/gist-logs.rb index acc9a10ea..5954c031e 100644 --- a/Library/Homebrew/cmd/gist-logs.rb +++ b/Library/Homebrew/cmd/gist-logs.rb @@ -37,12 +37,12 @@ module Homebrew      if ARGV.include?("--new-issue") || ARGV.switch?("n")        auth = :AUTH_TOKEN -      unless GitHub.api_credentials +      if GitHub.api_credentials_type == :none          puts "You can create a personal access token: https://github.com/settings/tokens"          puts "and then set HOMEBREW_GITHUB_API_TOKEN as authentication method."          puts -        auth = :AUTH_BASIC +        auth = :AUTH_USER_LOGIN        end        url = new_issue(f.tap, "#{f.name} failed to build on #{MacOS.full_version}", url, auth) @@ -118,9 +118,21 @@ module Homebrew      headers = GitHub.api_headers      headers["Content-Type"] = "application/json" +    basic_auth_credentials = nil +    if auth != :AUTH_USER_LOGIN +      token, username = GitHub.api_credentials +      case GitHub.api_credentials_type +      when :keychain +        basic_auth_credentials = [username, token] +      when :environment +        headers["Authorization"] = "token #{token}" +      end +    end +      request = Net::HTTP::Post.new(path, headers) +    request.basic_auth(*basic_auth_credentials) if basic_auth_credentials -    login(request) if auth == :AUTH_BASIC +    login(request) if auth == :AUTH_USER_LOGIN      request.body = Utils::JSON.dump(data)      request @@ -133,6 +145,7 @@ module Homebrew      when Net::HTTPCreated        Utils::JSON.load get_body(response)      else +      GitHub.api_credentials_error_message(response)        raise "HTTP #{response.code} #{response.message} (expected 201)"      end    end diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index 1fbb5afac..98ebcc9c8 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -484,7 +484,7 @@ module GitHub    class RateLimitExceededError < Error      def initialize(reset, error)        super <<-EOS.undent -        GitHub #{error} +        GitHub API Error: #{error}          Try again in #{pretty_ratelimit_reset(reset)}, or create a personal access token:            #{Tty.em}https://github.com/settings/tokens/new?scopes=&description=Homebrew#{Tty.reset}          and then set the token as: export HOMEBREW_GITHUB_API_TOKEN="your_new_token" @@ -506,9 +506,12 @@ module GitHub          EOS        else          message << <<-EOS.undent -          The GitHub credentials in the OS X keychain are invalid. +          The GitHub credentials in the OS X keychain may be invalid.            Clear them with:              printf "protocol=https\\nhost=github.com\\n" | git credential-osxkeychain erase +          Or create a personal access token: +            #{Tty.em}https://github.com/settings/tokens/new?scopes=&description=Homebrew#{Tty.reset} +          and then set the token as: export HOMEBREW_GITHUB_API_TOKEN="your_new_token"          EOS        end        super message @@ -536,32 +539,71 @@ module GitHub      end    end -  def api_headers -    @api_headers ||= begin -        headers = { -        "User-Agent" => HOMEBREW_USER_AGENT, -        "Accept"     => "application/vnd.github.v3+json" -      } -      token, username = api_credentials -      if token && !token.empty? -        if username && !username.empty? -          headers[:http_basic_authentication] = [username, token] -        else -          headers["Authorization"] = "token #{token}" +  def api_credentials_type +    token, username = api_credentials +    if token && !token.empty? +      if username && !username.empty? +        :keychain +      else +        :environment +      end +    else +      :none +    end +  end + +  def api_credentials_error_message(response_headers) +    @api_credentials_error_message_printed ||= begin +      unauthorized = (response_headers["status"] == "401 Unauthorized") +      scopes = response_headers["x-accepted-oauth-scopes"].to_s.split(", ") +      if !unauthorized && scopes.empty? +        credentials_scopes = response_headers["x-oauth-scopes"].to_s.split(", ") + +        case GitHub.api_credentials_type +        when :keychain +          onoe <<-EOS.undent +            Your OS X keychain GitHub credentials do not have sufficient scope! +            Scopes they have: #{credentials_scopes} +            Create a personal access token: https://github.com/settings/tokens +            and then set HOMEBREW_GITHUB_API_TOKEN as the authentication method instead. +          EOS +        when :environment +          onoe <<-EOS.undent +            Your HOMEBREW_GITHUB_API_TOKEN does not have sufficient scope! +            Scopes it has: #{credentials_scopes} +            Create a new personal access token: https://github.com/settings/tokens +            and then set the new HOMEBREW_GITHUB_API_TOKEN as the authentication method instead. +          EOS          end        end -      headers +      true      end    end +  def api_headers +    { +      "User-Agent" => HOMEBREW_USER_AGENT, +      "Accept"     => "application/vnd.github.v3+json" +    } +  end +    def open(url, &_block)      # This is a no-op if the user is opting out of using the GitHub API.      return if ENV["HOMEBREW_NO_GITHUB_API"]      require "net/https" +    headers = api_headers +    token, username = api_credentials +    case api_credentials_type +    when :keychain +      headers[:http_basic_authentication] = [username, token] +    when :environment +      headers["Authorization"] = "token #{token}" +    end +      begin -      Kernel.open(url, api_headers) { |f| yield Utils::JSON.load(f.read) } +      Kernel.open(url, headers) { |f| yield Utils::JSON.load(f.read) }      rescue OpenURI::HTTPError => e        handle_api_error(e)      rescue EOFError, SocketError, OpenSSL::SSL::SSLError => e @@ -578,6 +620,8 @@ module GitHub        raise RateLimitExceededError.new(reset, error)      end +    GitHub.api_credentials_error_message(e.io.meta) +      case e.io.status.first      when "401", "403"        raise AuthenticationFailedError.new(e.message)  | 
