diff --git a/Project.toml b/Project.toml index 010d9f9..0663ea5 100644 --- a/Project.toml +++ b/Project.toml @@ -5,9 +5,7 @@ version = "0.1.0-DEV" [deps] RegistryInstances = "2792f1a3-b283-48e8-9a74-f99dce5104f3" -URIs = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" [compat] RegistryInstances = "0.1" -URIs = "1" julia = "1.10" diff --git a/src/MethodURL.jl b/src/MethodURL.jl index 89b8d8f..e3f7bc0 100644 --- a/src/MethodURL.jl +++ b/src/MethodURL.jl @@ -3,9 +3,10 @@ module MethodURL +using Base: PkgId, UUID, inbase using RegistryInstances: reachable_registries, registry_info -using URIs: URI -using Base: PkgId + +export url function repo_and_path_to_url(repo, version, path, line) repo = chopsuffix(repo, ".git") @@ -13,11 +14,12 @@ function repo_and_path_to_url(repo, version, path, line) if startswith(repo, "https://github.com") return join([repo, "blob", "v" * version, path * "#L$line"], "/") else - error("failed to handle $repo") + error("Failed to construct URL for repository $repo.") end end -function repos_package(uuid) +# Find repository in reachable registries by looking up UUID +function repos_package(uuid::UUID) repos = String[] for reg in reachable_registries() entry = get(reg, uuid, nothing) @@ -29,26 +31,53 @@ function repos_package(uuid) return repos end +# Return errors instead of `nothing` +function _uuid(M::Module) + uuid = PkgId(M).uuid + isnothing(uuid) && error("Failed to find UUID of package $M.") + return uuid +end + +# Return errors instead of `nothing` +function _pkgdir(M::Module) + dir = pkgdir(M) + isnothing(dir) && error("Failed to find directory of package $M.") + return dir +end + # TODO: If package is devved use local path # TODO: If package is added by URL, use that function url(m::Method) M = parentmodule(m) - uuid = PkgId(M).uuid + file = String(m.file) line = m.line - pkg_splitpath = splitpath(pkgdir(M)) - file_splitpath = splitpath(String(m.file)) - while !isempty(pkg_splitpath) && first(pkg_splitpath) == first(file_splitpath) - popfirst!(pkg_splitpath) - popfirst!(file_splitpath) - end - local_dir = join(file_splitpath, "/") - - v = string(pkgversion(M)) urls = String[] - for repo in repos_package(uuid) - url = repo_and_path_to_url(repo, v, local_dir, line) + if inbase(M) + # adapted from https://github.com/JuliaLang/julia/blob/8f5b7ca12ad48c6d740e058312fc8cf2bbe67848/base/methodshow.jl#L382-L388 + commit = Base.GIT_VERSION_INFO.commit + if isempty(commit) + url = "https://github.com/JuliaLang/julia/tree/v$VERSION/base/$file#L$line" + else + url = "https://github.com/JuliaLang/julia/tree/$commit/base/$file#L$line" + end push!(urls, url) + else + uuid = _uuid(M) + pkg_splitpath = splitpath(_pkgdir(M)) + file_splitpath = splitpath(file) + while !isempty(pkg_splitpath) && first(pkg_splitpath) == first(file_splitpath) + popfirst!(pkg_splitpath) + popfirst!(file_splitpath) + end + local_dir = join(file_splitpath, "/") + # @info M file uuid _pkgdir(M) local_dir + + v = string(pkgversion(M)) + for repo in repos_package(uuid) + url = repo_and_path_to_url(repo, v, local_dir, line) + push!(urls, url) + end end return urls end diff --git a/test/Project.toml b/test/Project.toml index 5904861..16e03fe 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,6 +1,7 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7" +HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/runtests.jl b/test/runtests.jl index 8059033..6ad8fa1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,5 @@ using MethodURL + using Test using JuliaFormatter: JuliaFormatter using Aqua: Aqua @@ -12,6 +13,18 @@ using ExplicitImports: check_all_qualified_accesses_via_owners, check_all_qualified_accesses_are_public +using HTTP: request + +function url_exists(url) + response = request("GET", url; status_exception=false, redirect=true, retry=true) + if 200 ≤ response.status < 400 + return true + else + @warn "Failed to request URL" url response.status response + return false + end +end + @testset verbose = true "MethodURL.jl" begin @testset verbose = true "Linting" begin @testset "Code formatting (JuliaFormatter.jl)" begin @@ -31,13 +44,44 @@ using ExplicitImports: @testset "Improper explicit imports" begin @test isnothing(check_no_stale_explicit_imports(MethodURL)) @test isnothing(check_all_explicit_imports_via_owners(MethodURL)) - @test isnothing(check_all_explicit_imports_are_public(MethodURL)) + @test isnothing( + check_all_explicit_imports_are_public( + MethodURL; ignore=(:PkgId, :inbase) + ), + ) end @testset "Improper qualified accesses" begin @test isnothing(check_all_qualified_accesses_via_owners(MethodURL)) @test isnothing(check_no_self_qualified_accesses(MethodURL)) - @test isnothing(check_all_qualified_accesses_are_public(MethodURL)) + @test isnothing( + check_all_qualified_accesses_are_public( + MethodURL; ignore=(:GIT_VERSION_INFO,) + ), + ) end end end + @testset verbose = true "URL" begin + @testset "Base" begin + m1 = @which sqrt(1.0) + u1 = first(@inferred url(m1)) + # @test url_exists(u1) + end + # @testset "Stdlib" begin + # m2 = @which @test true + # u2 = first(@inferred url(m2)) + # # @test url_exists(u2) + # end + # @testset "Local" begin + # _m = @which sqrt(1.0) + # m3 = @which url(_m) + # u3 = first(@inferred url(m3)) + # # @test url_exists(u3) + # end + @testset "External" begin + m4 = @which Aqua.test_all(MethodURL) + u4 = first(@inferred url(m4)) + # @test url_exists(u4) + end + end end