From 98dc246d3bf00967d069e5a6352d08a89197bdf3 Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Mon, 14 Oct 2024 17:23:52 +1300 Subject: [PATCH 1/8] experimental: add _register_package --- src/JuliaHub.jl | 1 + src/packages.jl | 152 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 src/packages.jl diff --git a/src/JuliaHub.jl b/src/JuliaHub.jl index b371d35417..e1b5ff3413 100644 --- a/src/JuliaHub.jl +++ b/src/JuliaHub.jl @@ -30,6 +30,7 @@ include("jobs/request.jl") include("jobs/logging.jl") include("jobs/logging-kafka.jl") include("jobs/logging-legacy.jl") +include("packages.jl") function __init__() # We'll only attempt to determine the local timezone once, when the package loads, diff --git a/src/packages.jl b/src/packages.jl new file mode 100644 index 0000000000..8318018bc3 --- /dev/null +++ b/src/packages.jl @@ -0,0 +1,152 @@ +struct _Registry + uuid::UUIDs.UUID + name::String +end + +function _parse_registry(registry_dict::Dict) + name, uuid = try + registry_dict["name"], tryparse(UUIDs.UUID, registry_dict["uuid"]) + catch e + @error "Invalid registry value in API response" exception = (e, catch_backtrace()) + return nothing + end + return _Registry(uuid, name) +end + +function _registries(auth) + r = _restcall(auth, :GET, ("app", "packages", "registries"), nothing) + if r.status != 200 || !r.json["success"] + throw(JuliaHubError("Invalid response from JuliaHub (code $(r.status))\n$(r.body)")) + end + _parse_registry.(r.json["registries"]) +end + +""" + JuliaHub._register_package( + auth::Authentication, + registry::Union{AbstractString, _Registry}, + repository_url::AbstractString; + # Optional keyword arguments: + [notes::AbstractString,] + [branch::AbstractString,] + [subdirectory::AbstractString,] + [git_server_type::AbstractString] + ) -> String | Nothing + +Initiates a registration PR of the package at `repository_url` in +Returns the URL of the registry PR, or `nothing` if the registration failed. + +# Example + +``` +using JuliaHub +auth = JuliaHub.authenticate("juliahub.com") +JuliaHub._registries(auth) + +r = JuliaHub._register_package( + auth, + "MyInternalRegistry", + "https://github.com/MyUser/MyPackage.jl"; + notes = "This was initiated via JuliaHub.jl", +) + +!!! warning "Experimental API" + + This API is not part of the public API and does not adhere to semantic versioning. +""" +function _register_package( + auth::Authentication, + registry::Union{AbstractString, _Registry}, + repository_url::AbstractString; + notes::Union{AbstractString, Nothing}=nothing, + branch::Union{AbstractString, Nothing}=nothing, + subdirectory::AbstractString="", + git_server_type::Union{AbstractString, Nothing}=nothing, +) + if !isnothing(branch) && isempty(branch) + throw(ArgumentError("branch can not be an empty string")) + end + git_server_type = if isnothing(git_server_type) + if startswith(repository_url, "https://github.com") + "github" + else + throw(ArgumentError("Unable to determine git_server_type for repository: $(repository_url)")) + end + else + git_server_type + end + # Interpret the registry argument + registry_name::String = if isa(registry, _Registry) + registry.name + else + String(registry) + end + # ... + body = Dict( + "requests" => [ + Dict( + "registry_name" => registry_name, + "repo_url" => repository_url, + "branch" => something(branch, ""), + "notes" => something(notes, ""), + "subdir" => subdirectory, + "git_server_type" => git_server_type, + ) + ] + ) + r = _restcall( + auth, + :POST, + ("app", "registrator", "register"), + JSON.json(body); + headers=["Content-Type" => "application/json"], + ) + if r.status != 200 + throw(JuliaHubError("Invalid response from JuliaHub (code $(r.status))\n$(r.body)")) + elseif !r.json["success"] + error_message = get(get(r.json, "message", Dict()), "error", nothing) + if isnothing(error_message) + throw(JuliaHubError("Invalid response from JuliaHub (code $(r.status))\n$(r.body)")) + end + throw(InvalidRequestError(error_message)) + end + id, message = r.json["id"], r.json["message"] + @info "Initiated registration in $(registry_name)" id message repository_url + sleep(1) # registration won't go through right away anyway + status = _registration_status(auth, id) + δt = 2 + while status.state == "pending" + sleep(δt) + δt = min(δt * 2, 10) # double the sleep time, to a max of 10s + status = _registration_status(auth, id) + if status.state == "pending" + @info ".. waiting for registration to succeed" status.message + end + end + if status.state != "success" + @error "Registration failed ($id)" status.state status.message + return nothing + end + return status.message +end + +struct _RegistrationStatus + state::String + message::String +end + +function _registration_status(auth::Authentication, id::AbstractString) + r = _restcall( + auth, + :POST, + ("app", "registrator", "status"), + JSON.json(Dict( + "id" => id + )); + headers=["Content-Type" => "application/json"], + ) + if r.status != 200 || !r.json["success"] + throw(JuliaHubError("Invalid response from JuliaHub (code $(r.status))\n$(r.body)")) + end + return _RegistrationStatus(r.json["state"], r.json["message"]) +end From a6aefb9294dd15acb58cad5a72bcf015e9255669 Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Tue, 20 May 2025 21:53:47 +1200 Subject: [PATCH 2/8] Experimental module --- docs/src/reference/experimental.md | 15 +++++++++++ src/experimental.jl | 40 ++++++++++++++++++++++++++++++ src/packages.jl | 38 +++++++++++++++------------- 3 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 docs/src/reference/experimental.md create mode 100644 src/experimental.jl diff --git a/docs/src/reference/experimental.md b/docs/src/reference/experimental.md new file mode 100644 index 0000000000..661401188b --- /dev/null +++ b/docs/src/reference/experimental.md @@ -0,0 +1,15 @@ +# Experimental APIs + +The [`JuliaHub.Experimental`](@ref) module contains various experimental APIs. + +```@docs +JuliaHub.Experimental +``` + +## Reference + +```@docs +JuliaHub.Experimental.Registry +JuliaHub.Experimental.registries +JuliaHub.Experimental.register_package +``` diff --git a/src/experimental.jl b/src/experimental.jl new file mode 100644 index 0000000000..b6ac8b18fe --- /dev/null +++ b/src/experimental.jl @@ -0,0 +1,40 @@ +""" + module JuliaHub.Experimental + +Home for experimental JuliaHub.jl APIs. + +!!! warning "Unstable APIs" + + These APIs are considered highly unstable. + Both JuliaHub platform version changes, and also JuliaHub.jl package changes may break these APIs at any time. + Depend on them at your own peril. +""" +module Experimental + +using UUIDs: UUIDs + +const _DOCS_EXPERIMENTAL_API = """ +!!! warning "Unstable API" + This API is not part of the public API and does not adhere to semantic versioning. + + This APIs is considered highly unstable. + Both JuliaHub platform version changes, and also JuliaHub.jl package changes may break it at any time. + Depend on it at your own peril. +""" + +""" + struct Registry + +Represents a Julia package registry on JuliaHub. + +$(_DOCS_EXPERIMENTAL_API) +""" +struct Registry + uuid::UUIDs.UUID + name::String +end + +function registries end +function register_package end + +end diff --git a/src/packages.jl b/src/packages.jl index 8318018bc3..4c7f67e850 100644 --- a/src/packages.jl +++ b/src/packages.jl @@ -1,8 +1,3 @@ -struct _Registry - uuid::UUIDs.UUID - name::String -end - function _parse_registry(registry_dict::Dict) name, uuid = try registry_dict["name"], tryparse(UUIDs.UUID, registry_dict["uuid"]) @@ -10,10 +5,18 @@ function _parse_registry(registry_dict::Dict) @error "Invalid registry value in API response" exception = (e, catch_backtrace()) return nothing end - return _Registry(uuid, name) + return Experimental.Registry(uuid, name) end -function _registries(auth) +""" + JuliaHub.Experimental.registries() -> Vector{Experimental.Registry} + +Return the list of registries configured on the JuliaHub instance. + +$(Experimental._DOCS_EXPERIMENTAL_API) +""" +function Experimental.registries(auth::Authentication) + # NOTE: this API endpoint is not considered stable as of now r = _restcall(auth, :GET, ("app", "packages", "registries"), nothing) if r.status != 200 || !r.json["success"] throw(JuliaHubError("Invalid response from JuliaHub (code $(r.status))\n$(r.body)")) @@ -22,9 +25,9 @@ function _registries(auth) end """ - JuliaHub._register_package( + JuliaHub.Experimental.register_package( auth::Authentication, - registry::Union{AbstractString, _Registry}, + registry::Union{AbstractString, Registry}, repository_url::AbstractString; # Optional keyword arguments: [notes::AbstractString,] @@ -43,20 +46,19 @@ using JuliaHub auth = JuliaHub.authenticate("juliahub.com") JuliaHub._registries(auth) -r = JuliaHub._register_package( +r = JuliaHub.Experimental.register_package( auth, "MyInternalRegistry", "https://github.com/MyUser/MyPackage.jl"; notes = "This was initiated via JuliaHub.jl", ) +``` -!!! warning "Experimental API" - - This API is not part of the public API and does not adhere to semantic versioning. +$(Experimental._DOCS_EXPERIMENTAL_API) """ -function _register_package( +function Experimental.register_package( auth::Authentication, - registry::Union{AbstractString, _Registry}, + registry::Union{AbstractString, Experimental.Registry}, repository_url::AbstractString; notes::Union{AbstractString, Nothing}=nothing, branch::Union{AbstractString, Nothing}=nothing, @@ -76,12 +78,13 @@ function _register_package( git_server_type end # Interpret the registry argument - registry_name::String = if isa(registry, _Registry) + registry_name::String = if isa(registry, Experimental.Registry) registry.name else String(registry) end - # ... + # Do the package registration POST request. + # NOTE: this API endpoint is not considered stable as of now body = Dict( "requests" => [ Dict( @@ -136,6 +139,7 @@ struct _RegistrationStatus end function _registration_status(auth::Authentication, id::AbstractString) + # NOTE: this API endpoint is not considered stable as of now r = _restcall( auth, :POST, From d427d79c361148d3f7d3059f752046e7d9a8593f Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Tue, 20 May 2025 21:56:28 +1200 Subject: [PATCH 3/8] cosmetic fix --- src/experimental.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/experimental.jl b/src/experimental.jl index b6ac8b18fe..397c8a08aa 100644 --- a/src/experimental.jl +++ b/src/experimental.jl @@ -1,5 +1,5 @@ """ - module JuliaHub.Experimental + module Experimental Home for experimental JuliaHub.jl APIs. From ecdfef3f918056462f56de4ecaf3b275a11a62d8 Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Tue, 20 May 2025 21:58:54 +1200 Subject: [PATCH 4/8] :dragon: --- docs/src/reference/experimental.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/reference/experimental.md b/docs/src/reference/experimental.md index 661401188b..a69666be8f 100644 --- a/docs/src/reference/experimental.md +++ b/docs/src/reference/experimental.md @@ -1,5 +1,7 @@ # Experimental APIs +> Hic Sunt Dracones. + The [`JuliaHub.Experimental`](@ref) module contains various experimental APIs. ```@docs From 059cc39d2f158f91692841f811b3198403115cab Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Tue, 20 May 2025 22:04:31 +1200 Subject: [PATCH 5/8] :dragon: --- docs/src/reference/experimental.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/reference/experimental.md b/docs/src/reference/experimental.md index a69666be8f..de5b852e33 100644 --- a/docs/src/reference/experimental.md +++ b/docs/src/reference/experimental.md @@ -1,6 +1,6 @@ # Experimental APIs -> Hic Sunt Dracones. +> 🐉 Hic Sunt Dracones. The [`JuliaHub.Experimental`](@ref) module contains various experimental APIs. From bb268feb2644ce64c598b746e421e99eec4809d5 Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Wed, 21 May 2025 15:55:15 +1200 Subject: [PATCH 6/8] -. --- docs/src/reference/experimental.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/reference/experimental.md b/docs/src/reference/experimental.md index de5b852e33..0473e12cbd 100644 --- a/docs/src/reference/experimental.md +++ b/docs/src/reference/experimental.md @@ -1,6 +1,6 @@ # Experimental APIs -> 🐉 Hic Sunt Dracones. +> 🐉 Hic Sunt Dracones The [`JuliaHub.Experimental`](@ref) module contains various experimental APIs. From 0888fede49dd884c0342b699e2ebdab35bfdf416 Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Fri, 23 May 2025 16:32:25 +1200 Subject: [PATCH 7/8] don't make Experimental public API --- src/JuliaHub.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/JuliaHub.jl b/src/JuliaHub.jl index f43f94bc06..039f93f878 100644 --- a/src/JuliaHub.jl +++ b/src/JuliaHub.jl @@ -44,7 +44,8 @@ function _find_public_names() return filter(names(@__MODULE__; all=true)) do s # We don't need to check or mark public the main module itself (s == :JuliaHub) && return false - startswith(string(s), "_") && return false + # The Experimental module (or anything within it) is not public. + (s == :Experimental) && return false # Internal functions and types, prefixed by _ startswith(string(s), "_") && return false # Internal macros, prefixed by _ From fdcec7529812b0d547f16973aa7e2745344c8c5b Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Fri, 23 May 2025 16:35:06 +1200 Subject: [PATCH 8/8] format --- src/packages.jl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/packages.jl b/src/packages.jl index 4c7f67e850..f9d9be66ef 100644 --- a/src/packages.jl +++ b/src/packages.jl @@ -72,7 +72,11 @@ function Experimental.register_package( if startswith(repository_url, "https://github.com") "github" else - throw(ArgumentError("Unable to determine git_server_type for repository: $(repository_url)")) + throw( + ArgumentError( + "Unable to determine git_server_type for repository: $(repository_url)" + ), + ) end else git_server_type @@ -94,8 +98,8 @@ function Experimental.register_package( "notes" => something(notes, ""), "subdir" => subdirectory, "git_server_type" => git_server_type, - ) - ] + ), + ], ) r = _restcall( auth,