diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a7b32ffbd..33d3f9166f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +* Support self-hosted GitHub instances. ([#2755]) + * Added option `treat_markdown_warnings_as_error` which throws an error when encountering a markdown/interpolation warning ([#2792], [#2751]) * Footnotes can now be previewed by hovering over the link. ([#2080]) * The version selector now attempts to stay on the same page when switching between documentation versions. If the page doesn't exist in the target version, it falls back to the version homepage. ([#2801]) @@ -2196,6 +2198,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#2751]: https://github.com/JuliaDocs/Documenter.jl/issues/2751 [#2752]: https://github.com/JuliaDocs/Documenter.jl/issues/2752 [#2753]: https://github.com/JuliaDocs/Documenter.jl/issues/2753 +[#2755]: https://github.com/JuliaDocs/Documenter.jl/issues/2755 [#2761]: https://github.com/JuliaDocs/Documenter.jl/issues/2761 [#2762]: https://github.com/JuliaDocs/Documenter.jl/issues/2762 [#2772]: https://github.com/JuliaDocs/Documenter.jl/issues/2772 diff --git a/Project.toml b/Project.toml index e2213e7d39..c433b5c8f2 100644 --- a/Project.toml +++ b/Project.toml @@ -24,6 +24,7 @@ RegistryInstances = "2792f1a3-b283-48e8-9a74-f99dce5104f3" SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +URIs = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [compat] @@ -50,6 +51,7 @@ RegistryInstances = "0.1" SHA = "0.7, 1" TOML = "1" Test = "1.6" +URIs = "1.6.1" UUIDs = "1.6" Unicode = "1.6" julia = "1.6" diff --git a/docs/src/lib/internals/utilities.md b/docs/src/lib/internals/utilities.md index e25ec7827f..01da3f6d85 100644 --- a/docs/src/lib/internals/utilities.md +++ b/docs/src/lib/internals/utilities.md @@ -12,4 +12,6 @@ Pages = ["utilities/utilities.jl"] ```@docs Remotes.URL Remotes.repofile +Remotes.github_host +Remotes.parse_url ``` diff --git a/src/deployconfig.jl b/src/deployconfig.jl index 547b9fd412..f3260e8a9f 100644 --- a/src/deployconfig.jl +++ b/src/deployconfig.jl @@ -297,6 +297,9 @@ end Implementation of `DeployConfig` for deploying from GitHub Actions. +For self-hosted GitHub installation use the `GitHubActions(pages_url)` constructor +to specify the URL to the GitHub pages location. + The following environment variables influences the build when using the `GitHubActions` configuration: @@ -311,6 +314,12 @@ when using the `GitHubActions` configuration: - `GITHUB_TOKEN` or `DOCUMENTER_KEY`: used for authentication with GitHub, see the manual section for [GitHub Actions](@ref) for more information. + - `GITHUB_API_URL`: specifies the GitHub API URL, which generally is `https://api.github.com`, + but may be different for self-hosted GitHub instances. + + - `GITHUB_ACTOR`: name of the person or app that initiated the workflow. For + example, `octocat`. This is used to construct API calls. + The `GITHUB_*` variables are set automatically on GitHub Actions, see the [documentation](https://docs.github.com/en/actions/reference/workflows-and-actions/variables#default-environment-variables). """ @@ -318,12 +327,48 @@ struct GitHubActions <: DeployConfig github_repository::String github_event_name::String github_ref::String + github_host::String + github_api::String + github_pages_url::String end + function GitHubActions() + # Try to deduce GitHub Pages URL using GitHub API + github_repository = get(ENV, "GITHUB_REPOSITORY", "") # "JuliaDocs/Documenter.jl" + github_api = get(ENV, "GITHUB_API_URL", "") # https://api.github.com + github_token = get(ENV, "GITHUB_TOKEN", "") + + try + # Construct the curl call + cmd = `curl -s` + push!(cmd.exec, "-H", "Authorization: token $(github_token)") + push!(cmd.exec, "-H", "User-Agent: Documenter.jl") + push!(cmd.exec, "--fail") + push!(cmd.exec, "$(github_api)/repos/$(github_repository)/pages") + + # Run the command (silently) + response = run_and_capture(cmd) + response = JSON.parse(response.stdout) + + return GitHubActions(response["html_url"]) + catch + @warn "Unable to deduce GitHub Pages URL using GitHub API; falling back guess from the repository name." + parts = split(github_repository, "/") + if length(parts) == 2 + owner, repo = parts + return GitHubActions("https://$(owner).github.io/$(repo)/") + else + return GitHubActions("") + end + end +end + +function GitHubActions(pages_url) github_repository = get(ENV, "GITHUB_REPOSITORY", "") # "JuliaDocs/Documenter.jl" github_event_name = get(ENV, "GITHUB_EVENT_NAME", "") # "push", "pull_request" or "cron" (?) github_ref = get(ENV, "GITHUB_REF", "") # "refs/heads/$(branchname)" for branch, "refs/tags/$(tagname)" for tags - return GitHubActions(github_repository, github_event_name, github_ref) + github_api = get(ENV, "GITHUB_API_URL", "") # https://api.github.com + return GitHubActions(github_repository, github_event_name, github_ref, Remotes.github_host(), github_api, pages_url) end # Check criteria for deployment @@ -393,7 +438,7 @@ function deploy_folder( all_ok &= pr_ok println(io, "- $(marker(pr_ok)) ENV[\"GITHUB_REF\"] corresponds to a PR number") if pr_ok - pr_origin_matches_repo = verify_github_pull_repository(cfg.github_repository, pr_number) + pr_origin_matches_repo = verify_github_pull_repository(cfg, pr_number) all_ok &= pr_origin_matches_repo println(io, "- $(marker(pr_origin_matches_repo)) PR originates from the same repository") end @@ -452,7 +497,7 @@ end authentication_method(::GitHubActions) = env_nonempty("DOCUMENTER_KEY") ? SSH : HTTPS function authenticated_repo_url(cfg::GitHubActions) - return "https://$(ENV["GITHUB_ACTOR"]):$(ENV["GITHUB_TOKEN"])@github.com/$(cfg.github_repository).git" + return "https://$(ENV["GITHUB_ACTOR"]):$(ENV["GITHUB_TOKEN"])@$(cfg.github_host)/$(cfg.github_repository).git" end function version_tag_strip_build(tag; tag_prefix = "") @@ -469,7 +514,7 @@ function version_tag_strip_build(tag; tag_prefix = "") return "$s0$s1$s2$s3$s4" end -function post_status(gha::GitHubActions; type, repo::String, subfolder = nothing, kwargs...) +function post_status(gha::GitHubActions; type, subfolder = nothing, kwargs...) try # make this non-fatal and silent # If we got this far it usually means everything is in # order so no need to check everything again. @@ -489,21 +534,20 @@ function post_status(gha::GitHubActions; type, repo::String, subfolder = nothing sha = get(ENV, "GITHUB_SHA", nothing) end sha === nothing && return - return post_github_status(type, gha.github_repository, repo, sha, subfolder) - catch - @debug "Failed to post status" + return post_github_status(gha, type, sha, subfolder) + catch e + @debug "Failed to post status" exception = e end end -function post_github_status(type::S, source::S, deploydocs_repo::S, sha::S, subfolder = nothing) where {S <: String} +function post_github_status(gha::GitHubActions, type::S, sha::S, subfolder = nothing) where {S <: String} try - Sys.which("curl") === nothing && return + if Sys.which("curl") === nothing + @warn "curl not found in PATH, cannot post status" + return + end ## Extract owner and repository names - source_owner, source_repo = split(source, '/') - m = match(r"^github.com\/(.+?)\/(.+?)(.git)?$", deploydocs_repo) - m === nothing && return - deploy_owner = String(m.captures[1]) - deploy_repo = String(m.captures[2]) + source_owner, source_repo = split(gha.github_repository, '/') ## Need an access token for this auth = get(ENV, "GITHUB_TOKEN", nothing) @@ -518,9 +562,9 @@ function post_github_status(type::S, source::S, deploydocs_repo::S, sha::S, subf json["description"] = "Documentation build in progress" elseif type == "success" json["description"] = "Documentation build succeeded" - target_url = "https://$(deploy_owner).github.io/$(deploy_repo)/" - if subfolder !== nothing - target_url *= "$(subfolder)/" + target_url = gha.github_pages_url + if !isempty(target_url) && subfolder !== nothing + target_url = rstrip(target_url, '/') * "/$(subfolder)/" end json["target_url"] = target_url elseif type == "error" @@ -531,18 +575,19 @@ function post_github_status(type::S, source::S, deploydocs_repo::S, sha::S, subf error("unsupported type: $type") end push!(cmd.exec, "-d", JSON.json(json)) - push!(cmd.exec, "https://api.github.com/repos/$(source_owner)/$(source_repo)/statuses/$(sha)") + push!(cmd.exec, "$(gha.github_api)/repos/$(source_owner)/$(source_repo)/statuses/$(sha)") # Run the command (silently) io = IOBuffer() res = run(pipeline(cmd; stdout = io, stderr = devnull)) @debug "Response of curl POST request" response = String(take!(io)) - catch - @debug "Failed to post status" + catch e + @debug "Failed to post status" exception = e end return nothing end -function verify_github_pull_repository(repo, prnr) +function verify_github_pull_repository(cfg::GitHubActions, prnr) + repo = cfg.github_repository github_token = get(ENV, "GITHUB_TOKEN", nothing) if github_token === nothing @warn "GITHUB_TOKEN is missing, unable to verify if PR comes from destination repository -- assuming it doesn't." @@ -553,7 +598,7 @@ function verify_github_pull_repository(repo, prnr) push!(cmd.exec, "-H", "Authorization: token $(github_token)") push!(cmd.exec, "-H", "User-Agent: Documenter.jl") push!(cmd.exec, "--fail") - push!(cmd.exec, "https://api.github.com/repos/$(repo)/pulls/$(prnr)") + push!(cmd.exec, "$(cfg.github_api)/repos/$(repo)/pulls/$(prnr)") try # Run the command (silently) response = run_and_capture(cmd) diff --git a/src/deploydocs.jl b/src/deploydocs.jl index 03b3fd3547..6783a52bcd 100644 --- a/src/deploydocs.jl +++ b/src/deploydocs.jl @@ -194,28 +194,18 @@ GitHub. function deploydocs(; root = currentdir(), target = "build", - dirname = "", - - repo = error("no 'repo' keyword provided."), + dirname = "", repo = error("no 'repo' keyword provided."), branch = "gh-pages", - deploy_repo = nothing, - - repo_previews = nothing, - branch_previews = branch, - - deps = nothing, - make = nothing, - - cname = nothing, + deploy_repo = nothing, repo_previews = nothing, + branch_previews = branch, deps = nothing, + make = nothing, cname = nothing, devbranch = nothing, devurl = "dev", versions = ["stable" => "v^", "v#.#", devurl => devurl], forcepush::Bool = false, deploy_config = auto_detect_deploy_system(), push_preview::Bool = false, - tag_prefix = "", - - archive = nothing, # experimental and undocumented + tag_prefix = "", archive = nothing, # experimental and undocumented ) # Try to figure out default branch (see #1443 and #1727) @@ -493,10 +483,10 @@ function git_push( cd(() -> git_commands(sshconfig), temp) end end - post_status(deploy_config; repo = repo, type = "success", subfolder = subfolder) + post_status(deploy_config; type = "success", subfolder = subfolder) catch e @error "Failed to push:" exception = (e, catch_backtrace()) - post_status(deploy_config; repo = repo, type = "error") + post_status(deploy_config; type = "error") rethrow(e) finally # Remove the unencrypted private key. @@ -507,10 +497,10 @@ function git_push( upstream = authenticated_repo_url(deploy_config) try cd(() -> withenv(git_commands, NO_KEY_ENV...), temp) - post_status(deploy_config; repo = repo, type = "success", subfolder = subfolder) + post_status(deploy_config; type = "success", subfolder = subfolder) catch e @error "Failed to push:" exception = (e, catch_backtrace()) - post_status(deploy_config; repo = repo, type = "error") + post_status(deploy_config; type = "error") rethrow(e) end end diff --git a/src/utilities/Remotes.jl b/src/utilities/Remotes.jl index d7e8ef1ee6..1592171caf 100644 --- a/src/utilities/Remotes.jl +++ b/src/utilities/Remotes.jl @@ -3,6 +3,7 @@ Types and functions for handling repository remotes. """ module Remotes +using URIs: URI """ abstract type Remote @@ -103,7 +104,44 @@ function repofile(remote::Remote, ref, filename, linerange = nothing) end """ - GitHub(user :: AbstractString, repo :: AbstractString) + parse_url(url) + +Return tuple with values `authority` and `path` fragments of the given URL string. +This function is not strictly following URI specification as it will parse "github.com/user/repo" into `("github.com", "/user/repo")` + returning authority even if scheme is missing. +""" +function parse_url(url)::Tuple{String, String} + if contains(url, "://") == false + url = "https://$(url)" + end + u = URI(url) + + authority = u.host + if u.userinfo != "" + authority = "$(u.userinfo)@$(authority)" + end + + if u.port != "" + authority = "$(authority):$(u.port)" + end + return (authority, u.path) +end + +""" + github_host() + +Returns hostname of the GitHub installation where this code is running on at the moment. +This is derived from the `ENV[GITHUB_SERVER_URL]` variable which is set in every GitHub Actions workflow. +If this variable is not set, return "github.com". +""" +function github_host() + haskey(ENV, "GITHUB_SERVER_URL") || return "github.com" + url = ENV["GITHUB_SERVER_URL"] + return parse_url(url)[1] +end + +""" + GitHub(user :: AbstractString, repo :: AbstractString, [host :: AbstractString]) GitHub(remote :: AbstractString) Represents a remote Git repository hosted on GitHub. The repository is identified by the @@ -117,16 +155,28 @@ makedocs( The single-argument constructor assumes that the user and repository parts are separated by a slash (e.g. `JuliaDocs/Documenter.jl`). + +A `host` can be provided to point to the location of the self-hosted GitHub installation. """ struct GitHub <: Remote user::String repo::String + host::String + + GitHub(user::AbstractString, repo::AbstractString, host::AbstractString = github_host()) = new(user, repo, host) end function GitHub(remote::AbstractString) - user, repo = split(remote, '/') - return GitHub(user, repo) + url_authority, url_path = parse_url(remote) + path = url_path[1] == '/' ? url_path[2:end] : url_path + + if occursin("/", path) + user, repo = split(path, "/") + return GitHub(user, repo, url_authority) + else + return GitHub(url_authority, path) + end end -repourl(remote::GitHub) = "https://github.com/$(remote.user)/$(remote.repo)" +repourl(remote::GitHub) = "https://$(remote.host)/$(remote.user)/$(remote.repo)" function fileurl(remote::GitHub, ref::AbstractString, filename::AbstractString, linerange) url = "$(repourl(remote))/blob/$(ref)/$(filename)" isnothing(linerange) && return url diff --git a/test/deployconfig.jl b/test/deployconfig.jl index 49d0747222..be4d1c6099 100644 --- a/test/deployconfig.jl +++ b/test/deployconfig.jl @@ -141,6 +141,7 @@ end # Regular tag build with GITHUB_TOKEN withenv( "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.com", "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", "GITHUB_REF" => "refs/tags/v1.2.3", "GITHUB_ACTOR" => "github-actions", @@ -162,6 +163,7 @@ end # Regular tag build with SSH deploy key (SSH key prioritized) withenv( "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.com", "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", "GITHUB_REF" => "refs/tags/v1.2.3", "GITHUB_ACTOR" => "github-actions", @@ -183,6 +185,7 @@ end # External Repo: Regular tag build with SSH deploy key (SSH key prioritized) withenv( "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.com", "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", "GITHUB_REF" => "refs/tags/v1.2.3", "GITHUB_ACTOR" => "github-actions", @@ -224,6 +227,7 @@ end # Regular tag build with GITHUB_TOKEN and with tag prefix withenv( "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.com", "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", "GITHUB_REF" => "refs/tags/MySubPackage-v1.2.3", "GITHUB_ACTOR" => "github-actions", @@ -246,6 +250,7 @@ end # Broken tag build withenv( "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.com", "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", "GITHUB_REF" => "refs/tags/not-a-version", "GITHUB_ACTOR" => "github-actions", @@ -262,6 +267,7 @@ end # Regular devbranch build with GITHUB_TOKEN withenv( "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.com", "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", "GITHUB_REF" => "refs/heads/master", "GITHUB_ACTOR" => "github-actions", @@ -286,6 +292,7 @@ end # Regular devbranch build with SSH deploy key (SSH key prioritized) withenv( "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.com", "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", "GITHUB_REF" => "refs/heads/master", "GITHUB_ACTOR" => "github-actions", @@ -480,6 +487,7 @@ end # Missing environment variables withenv( "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.com", "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", "GITHUB_REF" => "refs/tags/v1.2.3", "GITHUB_ACTOR" => "github-actions", @@ -493,6 +501,29 @@ end ) @test !d.all_ok end + # Self-hosted GitHub installation + # Regular tag build with GITHUB_TOKEN + withenv( + "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.selfhosted", + "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", + "GITHUB_REF" => "refs/tags/v1.2.3", + "GITHUB_ACTOR" => "github-actions", + "GITHUB_TOKEN" => "SGVsbG8sIHdvcmxkLg==", + "DOCUMENTER_KEY" => nothing, + ) do + cfg = Documenter.GitHubActions("pages.selfhosted/something/JuliaDocs/Documenter.jl") + d = Documenter.deploy_folder( + cfg; repo = "github.selfhosted/JuliaDocs/Documenter.jl.git", + devbranch = "master", devurl = "dev", push_preview = true + ) + @test d.all_ok + @test d.subfolder == "v1.2.3" + @test d.repo == "github.selfhosted/JuliaDocs/Documenter.jl.git" + @test d.branch == "gh-pages" + @test Documenter.authentication_method(cfg) === Documenter.HTTPS + @test Documenter.authenticated_repo_url(cfg) === "https://github-actions:SGVsbG8sIHdvcmxkLg==@github.selfhosted/JuliaDocs/Documenter.jl.git" + end end end @@ -1396,7 +1427,7 @@ end "CI" => "woodpecker", "GITHUB_REPOSITORY" => nothing ) do - @test_throws KeyError cfg = Documenter.auto_detect_deploy_system() + @test_throws KeyError cfg = Documenter.auto_detect_deploy_system() end # Drone compatibility ends post-1.0.0 withenv( @@ -1494,3 +1525,92 @@ end @test length(r.stdout) > 0 end end + +@testset "post_status" begin + if Sys.which("curl") === nothing + @warn "'curl' binary not found, skipping related tests." + else + @testset "Default GitHubActions push" begin + buffer = IOBuffer() + logger = SimpleLogger(buffer, Logging.Debug) + with_logger(logger) do + withenv( + "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.com", + "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", + "GITHUB_REF" => "refs/tags/v1.2.3", + "GITHUB_ACTOR" => "github-actions", + "GITHUB_SHA" => "407d4b94", + "GITHUB_TOKEN" => "SGVsbG8sIHdvcmxkLg==", + "GITHUB_API_URL" => "badurl://api.github.com" # use bad url protocol to trigger CURL failure + ) do + cfg = Documenter.GitHubActions() + Documenter.post_status(cfg; type = "success", repo = "github.com/JuliaDocs/Documenter.jl") + end + end + logged = read(seek(buffer, 0), String) + @test occursin(r"""`curl -sX POST -H 'Authorization: token SGVsbG8sIHdvcmxkLg==' -H 'User-Agent: Documenter.jl' -H 'Content-Type: application/json' -d '{.+?}' badurl://api.github.com/repos/JuliaDocs/Documenter.jl/statuses/407d4b94`""", logged) + @test occursin(r"""`.+?{.*?\"target_url":"https://JuliaDocs.github.io/Documenter.jl/".*?}'.+?`""", logged) + @test occursin(r"""`.+?{.*?\"context\":\"documenter/deploy\".*?}'.+?`""", logged) + @test occursin(r"""`.+?{.*?\"description\":\"Documentation build succeeded\".*?}'.+?`""", logged) + @test occursin(r"""`.+?{.*?\"state\":\"success\".*?}'.+?`""", logged) + end + + @testset "Default GitHubActions pull_request" begin + buffer = IOBuffer() + logger = SimpleLogger(buffer, Logging.Debug) + with_logger(logger) do + mktemp() do path, io + write(io, """{"pull_request":{"head":{"sha":"407d4b94"}}}""") + close(io) + withenv( + "GITHUB_EVENT_NAME" => "pull_request", + "GITHUB_EVENT_PATH" => path, + "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", + "GITHUB_REF" => "refs/tags/v1.2.3", + "GITHUB_ACTOR" => "github-actions", + "GITHUB_TOKEN" => "SGVsbG8sIHdvcmxkLg==", + "GITHUB_API_URL" => "badurl://api.github.com" # use bad url protocol to trigger CURL failure + ) do + cfg = Documenter.GitHubActions() + Documenter.post_status(cfg; type = "success", repo = "github.com/JuliaDocs/Documenter.jl") + end + end + end + logged = read(seek(buffer, 0), String) + @test occursin(r"""`curl -sX POST -H 'Authorization: token SGVsbG8sIHdvcmxkLg==' -H 'User-Agent: Documenter.jl' -H 'Content-Type: application/json' -d '{.+?}' badurl://api.github.com/repos/JuliaDocs/Documenter.jl/statuses/407d4b94`""", logged) + @test occursin(r"""`.+?{.*?\"target_url":"https://JuliaDocs.github.io/Documenter.jl/".*?}'.+?`""", logged) + @test occursin(r"""`.+?{.*?\"context\":\"documenter/deploy\".*?}'.+?`""", logged) + @test occursin(r"""`.+?{.*?\"description\":\"Documentation build succeeded\".*?}'.+?`""", logged) + @test occursin(r"""`.+?{.*?\"state\":\"success\".*?}'.+?`""", logged) + end + + @testset "Self-hosted GitHubActions" begin + buffer = IOBuffer() + logger = SimpleLogger(buffer, Logging.Debug) + with_logger(logger) do + withenv( + "GITHUB_EVENT_NAME" => "push", + "GITHUB_SERVER_URL" => "github.selfhosted", + "GITHUB_REPOSITORY" => "JuliaDocs/Documenter.jl", + "GITHUB_REF" => "refs/tags/v1.2.3", + "GITHUB_ACTOR" => "github-actions", + "GITHUB_SHA" => "407d4b94", + "GITHUB_TOKEN" => "SGVsbG8sIHdvcmxkLg==", + "GITHUB_API_URL" => "badurl://api.github.selfhosted" # use bad url protocol to trigger CURL failure + ) do + cfg = Documenter.GitHubActions("pages.selfhosted/pages/JuliaDocs/Documenter.jl") + Documenter.post_status(cfg; type = "success", repo = "github.selfhosted/JuliaDocs/Documenter.jl") + end + end + logged = read(seek(buffer, 0), String) + @test occursin(r"""`curl -sX POST -H 'Authorization: token SGVsbG8sIHdvcmxkLg==' -H 'User-Agent: Documenter.jl' -H 'Content-Type: application/json' -d '{.+?}' badurl://api.github.selfhosted/repos/JuliaDocs/Documenter.jl/statuses/407d4b94`""", logged) + @test occursin(r"""`.+?{.*?\"target_url\":\"pages.selfhosted/pages/JuliaDocs/Documenter.jl\".*?}'.+?`""", logged) + @test occursin(r"""`.+?{.*?\"context\":\"documenter/deploy\".*?}'.+?`""", logged) + @test occursin(r"""`.+?{.*?\"description\":\"Documentation build succeeded\".*?}'.+?`""", logged) + @test occursin(r"""`.+?{.*?\"state\":\"success\".*?}'.+?`""", logged) + + end + + end +end diff --git a/test/remotes.jl b/test/remotes.jl index c3ee9fa30f..9b08355270 100644 --- a/test/remotes.jl +++ b/test/remotes.jl @@ -1,7 +1,7 @@ module RemoteTests using Test using Documenter -using .Remotes: repofile, repourl, issueurl, URL, GitHub, GitLab +using .Remotes: repofile, repourl, issueurl, URL, GitHub, GitLab, parse_url @testset "RepositoryRemote" begin let r = URL("https://github.com/FOO/BAR/blob/{commit}{path}#{line}") @@ -68,6 +68,17 @@ using .Remotes: repofile, repourl, issueurl, URL, GitHub, GitLab @test issueurl(r, "123") == "https://github.com/JuliaDocs/Documenter.jl/issues/123" end + withenv("GITHUB_SERVER_URL" => "github.selfhosted") do + let r = GitHub("JuliaDocs/Documenter.jl") + @test repourl(r) == "https://github.selfhosted/JuliaDocs/Documenter.jl" + @test repofile(r, "mybranch", "src/foo.jl") == "https://github.selfhosted/JuliaDocs/Documenter.jl/blob/mybranch/src/foo.jl" + @test repofile(r, "mybranch", "src/foo.jl", 5) == "https://github.selfhosted/JuliaDocs/Documenter.jl/blob/mybranch/src/foo.jl#L5" + @test repofile(r, "mybranch", "src/foo.jl", 5:5) == "https://github.selfhosted/JuliaDocs/Documenter.jl/blob/mybranch/src/foo.jl#L5" + @test repofile(r, "mybranch", "src/foo.jl", 5:8) == "https://github.selfhosted/JuliaDocs/Documenter.jl/blob/mybranch/src/foo.jl#L5-L8" + @test issueurl(r, "123") == "https://github.selfhosted/JuliaDocs/Documenter.jl/issues/123" + end + end + # GitLab remote let r = GitLab("JuliaDocs", "Documenter.jl") @test repourl(r) == "https://gitlab.com/JuliaDocs/Documenter.jl" @@ -95,6 +106,12 @@ using .Remotes: repofile, repourl, issueurl, URL, GitHub, GitLab @test repofile(r, "mybranch", "src/foo.jl", 5:8) == "https://gitlab.com/JuliaDocs/Documenter.jl/-/tree/mybranch/src/foo.jl#L5-L8" @test issueurl(r, "123") == "https://gitlab.com/JuliaDocs/Documenter.jl/-/issues/123" end + + @test parse_url("github.com") == ("github.com", "") + @test parse_url("a:b@github.com") == ("a:b@github.com", "") + @test parse_url("a:b@github.com:5") == ("a:b@github.com:5", "") + @test parse_url("github.com/page/subpage") == ("github.com", "/page/subpage") + @test parse_url("http://github.com/page/subpage") == ("github.com", "/page/subpage") end end # module diff --git a/test/utilities.jl b/test/utilities.jl index f1e8b27023..77581aa0b3 100644 --- a/test/utilities.jl +++ b/test/utilities.jl @@ -1,7 +1,8 @@ module UtilitiesTests using Test using Logging: Info -include("TestUtilities.jl"); using Main.TestUtilities +include("TestUtilities.jl") +using Main.TestUtilities import Documenter using Documenter: git @@ -443,12 +444,12 @@ end ] @test mdparse("x\n\ny", mode = :blocks) == [ MarkdownAST.@ast( - MarkdownAST.Paragraph() do; + MarkdownAST.Paragraph() do "x" end ), MarkdownAST.@ast( - MarkdownAST.Paragraph() do; + MarkdownAST.Paragraph() do "y" end ), @@ -745,6 +746,19 @@ end @test Documenter.slugify(test) == answer end end + + @testset "GitHub constructors" begin + gh = Documenter.Remotes.GitHub("user/project") + @test gh.user == "user" + @test gh.repo == "project" + @test gh.host == "github.com" + + + gh = Documenter.Remotes.GitHub("selfhosted.com/user/project") + @test gh.user == "user" + @test gh.repo == "project" + @test gh.host == "selfhosted.com" + end end end