From 6ec26b69c5ad5338bbd3b3248aa0103e61e44d02 Mon Sep 17 00:00:00 2001 From: Joris Kraak Date: Sat, 6 Jan 2024 14:32:35 +0100 Subject: [PATCH] Support scheme definition for GitLab remotes GitLab is fairly easy to self-host. Although best practice, this also means that the scheme over which GitLab is served is not guaranteed to be `https`. For this reason, the provided `host` should be allowed to define what scheme the GitLab instance is being hosted under and respect that in generated links. The same applies to ports and potentially paths, but those are already supported given the current mechanism (provided no trailing slash is included in the `host`). One benefit of this approach is that it also allows a full "base" URL to be provided as the host, e.g. the `CI_SERVER_URL` predefined variable within GitLab CI environments[^1]. According to RFC3986[^2], URI schemes should be matched case insensitively, which this approach does. It also prescribes that only lowercase schemes should be produced, which this chooses to not do to keep the implementation simpler. [^1]: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html [^2]: https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 --- src/utilities/Remotes.jl | 15 ++++++++++++++- test/remotes.jl | 9 +++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/utilities/Remotes.jl b/src/utilities/Remotes.jl index 1a3add0f50..997d6732f6 100644 --- a/src/utilities/Remotes.jl +++ b/src/utilities/Remotes.jl @@ -150,6 +150,9 @@ makedocs( ) ``` +The `host` may include a scheme. If the host does not define a scheme, it +defaults to `https`. + The single argument constructor assumes that the end user and repository parts are separated by a slash (e.g., `JuliaDocs/Documenter.jl`). @@ -164,7 +167,17 @@ function GitLab(remote::AbstractString) user, repo = split(remote, '/') GitLab(user, repo) end -repourl(remote::GitLab) = "https://$(remote.host)/$(remote.user)/$(remote.repo)" +function host_with_scheme(remote::GitLab) + # A scheme in a URI consists of a letter followed by zero or more letters, + # digits, pluses, dashes or periods followed by a colon. They should be + # matched case insensitively + # + # See https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 + scheme_matcher = r"^\p{L}[\p{L}\d+-.]*:" + scheme_from_host = match(scheme_matcher, remote.host) + return isnothing(scheme_from_host) ? "https://$(remote.host)" : remote.host +end +repourl(remote::GitLab) = "$(host_with_scheme(remote))/$(remote.user)/$(remote.repo)" function fileurl(remote::GitLab, ref::AbstractString, filename::AbstractString, linerange) url = "$(repourl(remote))/-/tree/$(ref)/$(filename)" isnothing(linerange) && return url diff --git a/test/remotes.jl b/test/remotes.jl index c3ee9fa30f..2ebafa58c4 100644 --- a/test/remotes.jl +++ b/test/remotes.jl @@ -87,6 +87,15 @@ using .Remotes: repofile, repourl, issueurl, URL, GitHub, GitLab @test issueurl(r, "123") == "https://my-gitlab.com/JuliaDocs/Documenter.jl/-/issues/123" end + let r = GitLab("http://my-gitlab.com", "JuliaDocs", "Documenter.jl") + @test repourl(r) == "http://my-gitlab.com/JuliaDocs/Documenter.jl" + @test repofile(r, "mybranch", "src/foo.jl") == "http://my-gitlab.com/JuliaDocs/Documenter.jl/-/tree/mybranch/src/foo.jl" + @test repofile(r, "mybranch", "src/foo.jl", 5) == "http://my-gitlab.com/JuliaDocs/Documenter.jl/-/tree/mybranch/src/foo.jl#L5" + @test repofile(r, "mybranch", "src/foo.jl", 5:5) == "http://my-gitlab.com/JuliaDocs/Documenter.jl/-/tree/mybranch/src/foo.jl#L5" + @test repofile(r, "mybranch", "src/foo.jl", 5:8) == "http://my-gitlab.com/JuliaDocs/Documenter.jl/-/tree/mybranch/src/foo.jl#L5-L8" + @test issueurl(r, "123") == "http://my-gitlab.com/JuliaDocs/Documenter.jl/-/issues/123" + end + let r = GitLab("JuliaDocs/Documenter.jl") @test repourl(r) == "https://gitlab.com/JuliaDocs/Documenter.jl" @test repofile(r, "mybranch", "src/foo.jl") == "https://gitlab.com/JuliaDocs/Documenter.jl/-/tree/mybranch/src/foo.jl"