-
Notifications
You must be signed in to change notification settings - Fork 5
/
utilities.jl
139 lines (118 loc) · 4.45 KB
/
utilities.jl
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
135
136
137
138
139
#####
##### GitHub things
#####
"""
PackageAnalyzer.github_auth(token::String="")
Obtain a GitHub authetication. Use the `token` argument if it is non-empty,
otherwise use the `GITHUB_TOKEN` and `GITHUB_AUTH` environment variables, if set
and of length 40. If all these methods fail, return an anonymous
authentication.
"""
function github_auth(token::String="")
auth = if !isempty(token)
GitHub.authenticate(token)
elseif haskey(ENV, "GITHUB_TOKEN") && length(ENV["GITHUB_TOKEN"]) == 40
GitHub.authenticate(ENV["GITHUB_TOKEN"])
elseif haskey(ENV, "GITHUB_AUTH") && length(ENV["GITHUB_AUTH"]) == 40
GitHub.authenticate(ENV["GITHUB_AUTH"])
else
GitHub.AnonymousAuth()
end
end
function github_extract_code!(dest::AbstractString, user::AbstractString, repo::AbstractString, tree_hash::AbstractString; auth)
# GitHub allows one to directly HTTP GET a URL like:
# https://github.com/$(user)/$(repo)/archive/tarball/$(tree_hash)
# But we go through `GitHub.gh_get` so we can easily use our auth token `auth`,
# in particular to support private packages.
# Note: GitHub does *not* support the `git archive` protocol, so we cannot just do a remote `git archive`.
path = "/repos/$(user)/$(repo)/tarball/$(tree_hash)"
resp = GitHub.gh_get(GitHub.DEFAULT_API, path; auth)
tmp = mktempdir()
Tar.extract(GzipDecompressorStream(IOBuffer(resp.body)), tmp)
files = only(readdir(tmp; join=true))
isdir(dest) || mkdir(dest)
mv(files, dest; force=true)
return nothing
end
#####
##### Parsing things
#####
function parse_project(dir)
bad_project = (; name="Invalid Project.toml", uuid=UUID(UInt128(0)), licenses_in_project=String[])
project_path = joinpath(dir, "Project.toml")
if !isfile(project_path)
project_path = joinpath(dir, "JuliaProject.toml")
end
isfile(project_path) || return bad_project
project = TOML.tryparsefile(project_path)
project isa TOML.ParserError && return bad_project
haskey(project, "name") && haskey(project, "uuid") || return bad_project
uuid = tryparse(UUID, project["uuid"]::String)
uuid === nothing && return bad_project
licenses_in_project = get(project, "license", String[])
if licenses_in_project isa String
licenses_in_project = [licenses_in_project]
end
return (; name=project["name"]::String, uuid, licenses_in_project)
end
function contribution_table(repo_name; auth)
return try
parse_contributions.(GitHub.contributors(GitHub.Repo(repo_name); auth, params=Dict("anon" => "true"))[1])
catch e
@error "Could not obtain contributors for $(repo_name)" exception = (e, catch_backtrace())
ContributionTableElType[]
end
end
function parse_contributions(c)
contrib = c["contributor"]
if contrib.typ == "Anonymous"
return (; login=missing, id=missing, contrib.name, type=contrib.typ, contributions=c["contributions"])
else
return (; contrib.login, contrib.id, name=missing, type=contrib.typ, contributions=c["contributions"])
end
end
#####
##### RegistryInstances thread safety
#####
# RegistryInstances uses LazilyInitializedFields, and if we try to initialize a field
# from two different threads, we have a data-race. We'll add a lock for these.
const REGISTRY_UUID_LOCK = ReentrantLock()
function get_uuids(name::AbstractString, registry)
return Base.@lock REGISTRY_UUID_LOCK begin
RegistryInstances.uuids_from_name(registry, name)
end
end
function get_uuids(uuid::UUID, registry)
return haskey(registry.pkgs, uuid) ? [uuid] : UUID[]
end
const REGISTRY_INFO_LOCK = ReentrantLock()
function registry_info(pkg::PkgEntry)
return Base.@lock REGISTRY_INFO_LOCK begin
RegistryInstances.registry_info(pkg)
end
end
#####
##### Other
#####
# Don't error if licensecheck isn't working, just log it
function _find_licenses(dir)
try
find_licenses(dir)
catch e
@error "Error finding licenses in $dir" exception=e maxlog=2
LicenseTableEltype[]
end
end
function get_tree_hash(dir::AbstractString)
return bytes2hex(Pkg.GitTools.tree_hash(dir))
end
function git()
use_local_env = parse(Bool, get(ENV,"USE_LOCAL_GIT", "false"))
# Git.jl has issues on MacOS, so if we have a local git there, use it,
# Also allow ENV-based opt-out. Why? Not sure about private packages.
if use_local_env || (Sys.isapple() && Sys.which("git") !== nothing)
return `git`
else
Git.git()
end
end