/usr/share/julia/base/pkg/github.jl is in julia-common 0.4.7-6.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | # This file is a part of Julia. License is MIT: http://julialang.org/license
module GitHub
import Main, ..Git, ..Dir
const AUTH_NOTE = "Julia Package Manager"
const AUTH_DATA = Dict{Any,Any}(
"scopes" => ["repo"],
"note" => AUTH_NOTE,
"note_url" => "http://docs.julialang.org/en/latest/manual/packages/",
)
function user()
if !success(`git config --global github.user`)
error("""
no GitHub user name configured; please configure it with:
git config --global github.user USERNAME
where USERNAME is replaced with your GitHub user name.
""")
end
readchomp(`git config --global github.user`)
end
function json()
isdefined(:JSON) || try eval(Main, :(import JSON))
catch err
warn(err)
error("using the GitHub API requires having the JSON package installed ")
end
Main.JSON
end
function curl(url::AbstractString, opts::Cmd=``)
extrapath = @windows? joinpath(JULIA_HOME,"..","Git","usr","bin")*";" : ""
withenv("PATH" => extrapath * ENV["PATH"]) do
success(`curl --version`) || error("using the GitHub API requires having `curl` installed")
out, proc = open(`curl -i -s -S $opts $url`,"r")
head = readline(out)
status = parse(Int,split(head,r"\s+";limit=3)[2])
header = Dict{AbstractString,AbstractString}()
for line in eachline(out)
if !ismatch(r"^\s*$",line)
(k,v) = split(line, r":\s*"; limit=2)
header[k] = v
continue
end
wait(proc); return status, header, readall(out)
end
error("strangely formatted HTTP response")
end
end
curl(url::AbstractString, data::Void, opts::Cmd=``) = curl(url,opts)
curl(url::AbstractString, data, opts::Cmd=``) =
curl(url,`--data $(sprint(io->json().print(io,data))) $opts`)
function delete_token()
tokfile = Dir.path(".github","token")
Base.rm(tokfile)
info("Could not authenticate with existing token. Deleting token and trying again.")
end
function token(user::AbstractString=user())
tokfile = Dir.path(".github","token")
if isfile(tokfile)
tok = strip(readchomp(tokfile))
!isempty(tok) && return tok
end
params = merge(AUTH_DATA, ["fingerprint" => randstring(40)])
status, header, content = curl("https://api.github.com/authorizations",params,`-u $user`)
tfa = false
# Check for two-factor authentication
if status == 401 && get(header, "X-GitHub-OTP", "") |> x->startswith(x, "required") && isinteractive()
tfa = true
info("Two-factor authentication in use. Enter auth code. (You may have to re-enter your password.)")
print(STDERR, "Authentication code: ")
code = readline(STDIN) |> chomp
status, header, content = curl("https://api.github.com/authorizations",params,`-H "X-GitHub-OTP: $code" -u $user`)
end
if status == 422
error_code = json().parse(content)["errors"][1]["code"]
error("GitHub returned validation error (422): $error_code: $(json().parse(content)["message"])")
else
(status != 401 && status != 403) || error("$status: $(json().parse(content)["message"])")
tok = json().parse(content)["token"]
end
mkpath(dirname(tokfile))
open(io->println(io,tok),tokfile,"w")
return tok
end
function req(resource::AbstractString, data, opts::Cmd=``)
url = "https://api.github.com/$resource"
status, header, content = curl(url,data,`-u $(token()):x-oauth-basic $opts`)
response = json().parse(content)
status, response
end
GET(resource::AbstractString, data, opts::Cmd=``) = req(resource,data,opts)
HEAD(resource::AbstractString, data, opts::Cmd=``) = req(resource,data,`-I $opts`)
PUT(resource::AbstractString, data, opts::Cmd=``) = req(resource,data,`-X PUT $opts`)
POST(resource::AbstractString, data, opts::Cmd=``) = req(resource,data,`-X POST $opts`)
PATCH(resource::AbstractString, data, opts::Cmd=``) = req(resource,data,`-X PATCH $opts`)
DELETE(resource::AbstractString, data, opts::Cmd=``) = req(resource,data,`-X DELETE $opts`)
for m in (:GET,:HEAD,:PUT,:POST,:PATCH,:DELETE)
@eval $m(resource::AbstractString, opts::Cmd=``) = $m(resource,nothing,opts)
end
function pushable(owner::AbstractString, repo::AbstractString, user::AbstractString=user())
status, response = HEAD("repos/$owner/$repo")
status == 404 && error("repo $owner/$repo does not exist")
status, response = GET("repos/$owner/$repo/collaborators/$user")
status == 204 && return true
status == 404 && return false
error("unexpected API status code: $status – $(response["message"])")
end
function fork(owner::AbstractString, repo::AbstractString)
status, response = POST("repos/$owner/$repo/forks")
if status == 401
delete_token()
status, response = POST("repos/$owner/$repo/forks")
end
status == 202 || error("forking $owner/$repo failed: $(response["message"])")
return response
end
end # module
|