Skip to content

Instantly share code, notes, and snippets.

@KristofferC
Created October 8, 2019 07:39
Show Gist options
  • Save KristofferC/a0c56b731a55e071ccf58da682cc1e40 to your computer and use it in GitHub Desktop.
Save KristofferC/a0c56b731a55e071ccf58da682cc1e40 to your computer and use it in GitHub Desktop.
import Pkg
import Pkg.TOML
import Pkg.Types: VersionRange, ≲
using UUIDs
using GitHub
using ProgressMeter
#const auth = GitHub.authenticate(ENV["GITHUB_KEY"])
# Copied from Registrator.jl
#=====================#
struct RegistryData
name::String
uuid::UUID
repo::Union{String, Nothing}
description::Union{String, Nothing}
packages::Dict{String, Dict{String, Any}}
extra::Dict{String, Any}
end
function RegistryData(
name::AbstractString,
uuid::Union{UUID, AbstractString};
repo::Union{AbstractString, Nothing}=nothing,
description::Union{AbstractString, Nothing}=nothing,
packages::Dict=Dict(),
extras::Dict=Dict(),
)
RegistryData(name, UUID(uuid), repo, description, packages, extras)
end
function Base.copy(reg::RegistryData)
RegistryData(
reg.name,
reg.uuid,
reg.repo,
reg.description,
deepcopy(reg.packages),
deepcopy(reg.extra),
)
end
function parse_registry(io::IO)
data = TOML.parse(io)
name = pop!(data, "name")
uuid = pop!(data, "uuid")
repo = pop!(data, "repo", nothing)
description = pop!(data, "description", nothing)
packages = pop!(data, "packages", Dict())
RegistryData(name, UUID(uuid), repo, description, packages, data)
end
parse_registry(str::AbstractString) = open(parse_registry, str)
function TOML.print(io::IO, reg::RegistryData)
println(io, "name = ", repr(reg.name))
println(io, "uuid = ", repr(string(reg.uuid)))
if reg.repo !== nothing
println(io, "repo = ", repr(reg.repo))
end
if reg.description !== nothing
# print long-form string if there are multiple lines
if '\n' in reg.description
print(io, """\n
description = \"\"\"
$(reg.description)\"\"\"
"""
)
else
println(io, "description = ", repr(reg.description))
end
end
for (k, v) in pairs(reg.extra)
TOML.print(io, Dict(k => v), sorted=true)
end
println(io, "\n[packages]")
for (uuid, data) in sort!(collect(reg.packages), by=first)
print(io, uuid, " = { ")
print(io, "name = ", repr(data["name"]))
print(io, ", path = ", repr(data["path"]))
println(io, " }")
end
nothing
end
#===============================================================#
struct Package
uuid::UUID
name::String
path::String
url::String
versions::Dict{VersionNumber, Base.SHA1}
end
Package(uuid, name, path, url) = Package(uuid, name, path, url, Dict{VersionNumber, Base.SHA1}())
function remove_registry(filter; print_deletions=false)
regpath = joinpath(homedir(), ".julia/registries/General")
packages = Package[]
reg = parse_registry(joinpath(regpath, "Registry.toml"))
for (uuid, data) in reg.packages
path = data["path"]
pkgpath = joinpath(regpath, path)
pkgfile = Pkg.TOML.parsefile(joinpath(pkgpath, "Package.toml"))
url = pkgfile["repo"]
push!(packages, Package(UUID(uuid), data["name"], path, url))
end
# Using the fact that there is only one uuid per name in General right now
pkgs_completely_deleted = Set{String}()
sort!(packages; by= x -> x.name)
@showprogress for pkg in packages
pkgpath = joinpath(regpath, pkg.path)
compatfile = joinpath(pkgpath, "Compat.toml")
depfile = joinpath(pkgpath, "Deps.toml")
merge!(pkg.versions, Pkg.Operations.load_versions(pkgpath; include_yanked = true))
# @show pkg.versions
versions_to_delete = VersionNumber[]
compats = nothing
if isfile(compatfile)
compats = Pkg.Compress.load(joinpath(pkgpath, "Compat.toml"))
end
deps = nothing
if isfile(depfile)
deps = Pkg.Compress.load(joinpath(pkgpath, "Deps.toml"))
end
versions_to_delete = filter(compats, deps, pkg)
foreach(v -> delete!(pkg.versions, v), versions_to_delete)
if isempty(pkg.versions)
delete!(reg.packages, string(pkg.uuid))
rm(pkgpath, force=true, recursive=true)
push!(pkgs_completely_deleted, pkg.name)
@info "Deleting $(pkg.name) completely..."
else
if !isempty(versions_to_delete)
# Write out new versions
versions_file = joinpath(pkgpath, "Versions.toml")
open(versions_file, "w") do io
versions_string = Dict(string(v) => Dict{String,Any}("git-tree-sha1" => string(sha)) for (v, sha) in pkg.versions)
Pkg.TOML.print(io, versions_string; sorted=true, by=x->VersionNumber(x))
end
# Write out the new compressed versions
for file in (compatfile, depfile)
if isfile(file)
data = Pkg.Compress.load(file)
foreach(v -> delete!(data, v), versions_to_delete)
Pkg.Compress.save(file, data)
end
end
end
end
end
open(joinpath(regpath, "Registry.toml"), "w") do io
TOML.print(io, reg)
end
return pkgs_completely_deleted
end
function cleanup_registry()
@info "Removing versions that are not compatible with Julia 1.0..."
pkgs_completely_deleted = remove_registry(; print_deletions=true) do compats, deps, pkg
versions_to_delete = VersionNumber[]
compats == nothing && return versions_to_delete
for (version, compat) in compats
for (dep, version_range) in compat
if dep === "julia"
pkg.name === "AlgebraicMultiGrid" && @show version_range
if version_range isa Vector
version_range = maximum(VersionNumber.(version_range))
end
vr = VersionRange(version_range)
if vr.upper ≲ v"0.7" && vr.upper != Pkg.Types.VersionBound("*") # Special case because it seems bugged
push!(versions_to_delete, version)
end
end
end
end
return versions_to_delete
end
@info "Removing packages which repo does not exist"
stdlibs = readdir(Sys.STDLIB)
# Explicitly list these since checking availability on every run is slow
repos_deleted = ["DigitalComm", "Keys", "LibGit2", "MackeyGlass"]
pkgs_completely_deleted_pass_2 = remove_registry(; print_deletions=true) do compats, deps, pkg
versions_to_delete = VersionNumber[]
rep = match(r"https://github.com/(.*?).git", pkg.url)
if rep === nothing
@warn pkg.url
else
rep = rep.captures[1]
#
#repo = GitHub.repo(rep; handle_error=false, auth=auth)
deps === nothing && return versions_to_delete
repo_deleted = pkg.name in repos_deleted
if repo_deleted #repo.id === nothing
@info "Deleting $(pkg.name) because its repo does not exist anymore"
end
for (version, compat) in deps
if repo_deleted
push!(versions_to_delete, version)
end
end
end
return versions_to_delete
end
pkgs_completely_deleted = union(pkgs_completely_deleted, pkgs_completely_deleted_pass_2)
pkgs_completely_deleted_pass_3 = remove_registry(; print_deletions=true) do compats, deps, pkg
versions_to_delete = VersionNumber[]
deps == nothing && return versions_to_delete
for (version, compat) in deps
#@show version => compat
for (deppkg, uuid) in compat
# Packages that got moved to STDLIB kept their UUID
deppkg in stdlibs && continue
if deppkg in pkgs_completely_deleted
@info "Deleting $(pkg.name):$(version) because it depends on $deppkg which was completely removed"
push!(versions_to_delete, version)
break
end
end
end
return versions_to_delete
end
pkgs_completely_deleted = union(pkgs_completely_deleted, pkgs_completely_deleted_pass_3)
write("packagages_deleted", join(sort(collect(pkgs_completely_deleted)), '\n'))
return
end
function check_registry_consistency()
regpath = joinpath(homedir(), ".julia/registries/General")
packages = Package[]
reg = parse_registry(joinpath(regpath, "Registry.toml"))
all_uuids = Set{UUID}()
for (uuid, data) in reg.packages
push!(packages, Package(UUID(uuid), data["name"], data["path"], ""))
push!(all_uuids, UUID(uuid))
end
stdlibs = readdir(Sys.STDLIB)
for pkg in packages
pkgpath = joinpath(regpath, pkg.path)
depfile = joinpath(pkgpath, "Deps.toml")
if isfile(depfile)
deps = Pkg.Compress.load(joinpath(pkgpath, "Deps.toml"))
for (version, compat) in deps
for (deppkg, uuid) in compat
deppkg in stdlibs && continue
if !(UUID(uuid) in all_uuids)
error("$(pkg.name) depends on unknown package with uuid $uuid")
end
end
end
end
end
end
cleanup_registry()
check_registry_consistency()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment