@@ -27,77 +27,85 @@ function github_auth(;allow_anonymous::Bool=true)
27
27
return _github_auth[]
28
28
end
29
29
30
- function obtain_token (; ins = stdin , outs= stdout , github_api= GitHub. DEFAULT_API)
30
+ function obtain_token (; outs= stdout , github_api= GitHub. DEFAULT_API)
31
31
println (outs)
32
- printstyled (outs, " Creating a github access token\n " , bold= true )
32
+ printstyled (outs, " Authenticating with GitHub\n " , bold= true )
33
+
34
+
35
+ @label retry
36
+
37
+ headers = Dict {String, String} (" User-Agent" => " BinaryBuilder-jl" ,
38
+ " Accept" => " application/json" )
39
+
40
+ # Request device authentication flow for BinaryBuilder OAauth APP
41
+ resp = HTTP. post (" https://github.com/login/device/code" , headers,
42
+ " client_id=2a955f9ca1a7c5b720f3&scope=public_repo" )
43
+ if resp. status != 200
44
+ GitHub. handle_response_error (resp)
45
+ end
46
+ reply = JSON. parse (HTTP. payload (resp, String))
47
+
33
48
println (outs, """
34
- To continue, we need to create a GitHub access token, so that we can do
35
- things like fork Yggdrasil and create pull requests against it. To create
36
- an access token, you will be asked to enter your GitHub credentials:
37
- """ )
49
+ To continue, we need to authenticate you with GitHub. Please navigate to
50
+ the following page in your browser and enter the code below:
38
51
39
- params = Dict (
40
- # The only thing we really need to do is write to public repos
41
- " scopes" => [
42
- " public_repo" ,
43
- ],
44
- " note" => " BinaryBuilder.jl generated token at $(now ()) " ,
45
- " note_url" => " https://github.com/JuliaPackaging/BinaryBuilder.jl" ,
46
- " fingerprint" => randstring (40 )
47
- )
52
+ $(HTTP. URIs. unescapeuri (reply[" verification_uri" ]))
53
+
54
+ #############
55
+ # $(reply[" user_code" ]) #
56
+ #############
57
+ """ )
48
58
59
+ interval = reply[" interval" ]
60
+ device_code = reply[" device_code" ]
49
61
while true
50
- if ! isopen (ins) || (! Sys. iswindows () && ! isopen (stdin ))
51
- # We have to check also `stdin` because `_getpass` ignores `ins` and
52
- # always uses `stdin` on Unices.
53
- error (" Cannot read from input stream" )
54
- end
62
+ # Poll for completion
63
+ sleep (interval)
64
+ resp = HTTP. post (" https://github.com/login/oauth/access_token" , headers,
65
+ " client_id=2a955f9ca1a7c5b720f3&grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=$device_code " )
55
66
56
- user = nonempty_line_prompt (" Username" , " GitHub username:" , ins= ins, outs= outs)
57
- password = nonempty_line_prompt (" Password" , " GitHub password:" ; ins= ins, outs= outs, echo= false )
58
-
59
- # Shuffle this junk off to the GH API
60
- headers = Dict {String, String} (" User-Agent" => " BinaryBuilder-jl" )
61
- auth = GitHub. UsernamePassAuth (user, password)
62
- resp = GitHub. gh_post (
63
- github_api,
64
- " /authorizations" ;
65
- headers= headers,
66
- params= params,
67
- handle_error= false ,
68
- auth= auth,
69
- )
70
- if resp. status == 401 && startswith (strip (HTTP. getkv (resp. headers, " X-GitHub-OTP" , " " )), " required" )
71
- otp_code = nonempty_line_prompt (" Authorization code" , " Two-factor authentication in use, enter auth code:" )
72
- resp = GitHub. gh_post (
73
- github_api,
74
- " /authorizations" ;
75
- headers= merge (headers, Dict (" X-GitHub-OTP" => otp_code)),
76
- params= params,
77
- handle_error= false ,
78
- auth = auth,
79
- )
80
- end
81
- if resp. status == 401
82
- printstyled (outs, " Invalid credentials!" , color= :red )
83
- println (outs)
84
- continue
85
- end
86
67
if resp. status != 200
87
68
GitHub. handle_response_error (resp)
88
69
end
89
- token = JSON. parse (HTTP. payload (resp, String))[" token" ]
70
+
71
+
72
+ token_reply = JSON. parse (HTTP. payload (resp, String))
73
+ if haskey (token_reply, " error" )
74
+ error_kind = token_reply[" error" ]
75
+ if error_kind == " authorization_pending"
76
+ continue
77
+ elseif error_kind == " slow_down"
78
+ @warn " GitHub Auth rate limit exceeded. Waiting 10s. (This shouldn't happen)"
79
+ sleep (10 )
80
+ elseif error_kind == " expired_token"
81
+ @error " Token request expired. Starting over!"
82
+ @goto retry
83
+ elseif error_kind == " access_denied"
84
+ @error " Authentication request canceled by user. Starting over!"
85
+ @goto retry
86
+ elseif error_kind in (" unsupported_grant_type" ,
87
+ " incorrect_client_credentials" , " incorrect_device_code" )
88
+ error (" Received error kind $(error_kind) . Please file an issue." )
89
+ else
90
+ error (" Unexpected GitHub login error $(error_kind) " )
91
+ end
92
+ end
93
+
94
+ token = token_reply[" access_token" ]
90
95
91
96
print (outs, strip ("""
92
97
Successfully obtained GitHub authorization token!
93
- This token will be used for the rest of this BB session, however if you
94
- wish to use this token permanently, you may set it in your environment
95
- as via the following line in your """ ))
98
+ This token will be used for the rest of this BB session.
99
+ You will have to re-authenticate for any future session.
100
+ However, if you wish to bypass this step, you may create a
101
+ personal access token at """ ))
102
+ printstyled (" https://github.com/settings/tokens" ; bold= true )
103
+ println (" \n and add the token to the" )
96
104
printstyled (outs, " ~/.julia/config/startup.jl" ; bold= true )
97
- println (outs, " file:" )
105
+ println (outs, " file as :" )
98
106
println (outs)
99
107
100
- printstyled (outs, " ENV[\" GITHUB_TOKEN\" ] = \" $( token) \" " ; bold= true )
108
+ printstyled (outs, " ENV[\" GITHUB_TOKEN\" ] = < token> " ; bold= true )
101
109
println (outs)
102
110
103
111
println (outs, " This token is sensitive, so only do this in a computing environment you trust." )
0 commit comments