diff --git a/CHANGELOG.md b/CHANGELOG.md index 099a5bee8d..eeab630184 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Version `v0.25.3` +* ![Feature][badge-feature] Documenter can now deploy from GitLab CI to GitHub Pages with `Documenter.GitLab`. ([#1448][github-1448]) + * ![Enhancement][badge-enhancement] The URL to the MathJax JS module can now be customized by passing the `url` keyword argument to the constructors (`MathJax2`, `MathJax3`). ([#1428][github-1428], [#1430][github-1430]) * ![Bugfix][badge-bugfix] `Documenter.doctest` now correctly accepts the `doctestfilters` keyword, similar to `Documenter.makedocs`. ([#1364][github-1364], [#1435][github-1435]) @@ -663,6 +665,7 @@ [github-1438]: https://github.com/JuliaDocs/Documenter.jl/issues/1438 [github-1364]: https://github.com/JuliaDocs/Documenter.jl/issues/1364 [github-1435]: https://github.com/JuliaDocs/Documenter.jl/pull/1435 +[github-1448]: https://github.com/JuliaDocs/Documenter.jl/pull/1448 [github-1440]: https://github.com/JuliaDocs/Documenter.jl/pull/1440 [github-1452]: https://github.com/JuliaDocs/Documenter.jl/pull/1452 diff --git a/docs/src/man/hosting.md b/docs/src/man/hosting.md index 55328d749b..226d6248ce 100644 --- a/docs/src/man/hosting.md +++ b/docs/src/man/hosting.md @@ -459,9 +459,10 @@ look at this package's repository for some inspiration. It is possible to customize Documenter to use other systems then the ones described in the sections above. This is done by passing a configuration (a [`DeployConfig`](@ref Documenter.DeployConfig)) to `deploydocs` by the `deploy_config` -keyword argument. Documenter natively supports [`Travis`](@ref Documenter.Travis) and -[`GitHubActions`](@ref Documenter.GitHubActions) natively, but it is easy to define -your own by following the simple interface described below. +keyword argument. Documenter supports [`Travis`](@ref Documenter.Travis), +[`GitHubActions`](@ref Documenter.GitHubActions), and [`GitLab`](@ref Documenter.GitLab) +natively, but it is easy to define your own by following the simple interface +described below. ```@docs Documenter.DeployConfig @@ -473,4 +474,5 @@ Documenter.documenter_key Documenter.documenter_key_previews Documenter.Travis Documenter.GitHubActions +Documenter.GitLab ``` diff --git a/src/deployconfig.jl b/src/deployconfig.jl index 96e65c2522..cab787c035 100644 --- a/src/deployconfig.jl +++ b/src/deployconfig.jl @@ -483,6 +483,159 @@ function post_github_status(type::S, deploydocs_repo::S, sha::S, subfolder=nothi return nothing end +########## +# GitLab # +########## + +""" + GitLab <: DeployConfig + +GitLab implementation of `DeployConfig`. + +The following environment variables influence the build when using the +`GitLab` configuration: + + - `DOCUMENTER_KEY`: must contain the Base64-encoded SSH private key for the + repository. This variable should be set in the GitLab settings. Make sure this + variable is marked **NOT** to be displayed in the build log. + + - `CI_COMMIT_BRANCH`: the name of the commit branch. + + - `CI_EXTERNAL_PULL_REQUEST_IID`: Pull Request ID from GitHub if the pipelines + are for external pull requests. + + - `CI_PROJECT_PATH_SLUG`: The namespace with project name. All letters + lowercased and non-alphanumeric characters replaced with `-`. + + - `CI_COMMIT_TAG`: The commit tag name. Present only when building tags. + + - `CI_PIPELINE_SOURCE`: Indicates how the pipeline was triggered. + +The `CI_*` variables are set automatically on GitLab. More information on how GitLab +sets the `CI_*` variables can be found in the +[GitLab documentation](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html). +""" +struct GitLab <: DeployConfig + commit_branch::String + pull_request_iid::String + repo_slug::String + commit_tag::String + pipeline_source::String +end + +function GitLab() + commit_branch = get(ENV, "CI_COMMIT_BRANCH", "") + pull_request_iid = get(ENV, "CI_EXTERNAL_PULL_REQUEST_IID", "") + repo_slug = get(ENV, "CI_PROJECT_PATH_SLUG", "") + commit_tag = get(ENV, "CI_COMMIT_TAG", "") + pipeline_source = get(ENV, "CI_PIPELINE_SOURCE", "") + GitLab(commit_branch, pull_request_iid, repo_slug, commit_tag, pipeline_source) +end + +function deploy_folder( + cfg::GitLab; + repo, + repo_previews = repo, + devbranch, + push_preview, + devurl, + branch = "gh-pages", + branch_previews = branch, + kwargs..., +) + + marker(x) = x ? "✔" : "✘" + + io = IOBuffer() + all_ok = true + + println(io, "\nGitLab config:") + println(io, " Commit branch: \"", cfg.commit_branch, "\"") + println(io, " Pull request IID: \"", cfg.pull_request_iid, "\"") + println(io, " Repo slug: \"", cfg.repo_slug, "\"") + println(io, " Commit tag: \"", cfg.commit_tag, "\"") + println(io, " Pipeline source: \"", cfg.pipeline_source, "\"") + + build_type = if cfg.pull_request_iid != "" + :preview + elseif cfg.commit_tag != "" + :release + else + :devbranch + end + + println(io, "Detected build type: ", build_type) + + if build_type == :release + tag_nobuild = version_tag_strip_build(cfg.commit_tag) + ## If a tag exist it should be a valid VersionNumber + tag_ok = tag_nobuild !== nothing + + println( + io, + "- $(marker(tag_ok)) ENV[\"CI_COMMIT_TAG\"] contains a valid VersionNumber", + ) + all_ok &= tag_ok + + is_preview = false + subfolder = tag_nobuild + deploy_branch = branch + deploy_repo = repo + elseif build_type == :preview + pr_number = tryparse(Int, cfg.pull_request_iid) + pr_ok = pr_number !== nothing + all_ok &= pr_ok + println( + io, + "- $(marker(pr_ok)) ENV[\"CI_EXTERNAL_PULL_REQUEST_IID\"]=\"$(cfg.pull_request_iid)\" is a number", + ) + btype_ok = push_preview + all_ok &= btype_ok + is_preview = true + println( + io, + "- $(marker(btype_ok)) `push_preview` keyword argument to deploydocs is `true`", + ) + ## deploy to previews/PR + subfolder = "previews/PR$(something(pr_number, 0))" + deploy_branch = branch_previews + deploy_repo = repo_previews + else + branch_ok = !isempty(cfg.commit_tag) || cfg.commit_branch == devbranch + all_ok &= branch_ok + println( + io, + "- $(marker(branch_ok)) ENV[\"CI_COMMIT_BRANCH\"] matches devbranch=\"$(devbranch)\"", + ) + is_preview = false + subfolder = devurl + deploy_branch = branch + deploy_repo = repo + end + + key_ok = haskey(ENV, "DOCUMENTER_KEY") + println(io, "- $(marker(key_ok)) ENV[\"DOCUMENTER_KEY\"] exists") + all_ok &= key_ok + + print(io, "Deploying to folder $(repr(subfolder)): $(marker(all_ok))") + @info String(take!(io)) + + if all_ok + return DeployDecision(; + all_ok = true, + branch = deploy_branch, + repo = deploy_repo, + subfolder = subfolder, + is_preview = is_preview, + ) + else + return DeployDecision(; all_ok = false) + end +end + +authentication_method(::GitLab) = Documenter.SSH + +documenter_key(::GitLab) = ENV["DOCUMENTER_KEY"] ################## # Auto-detection # @@ -492,6 +645,8 @@ function auto_detect_deploy_system() return Travis() elseif haskey(ENV, "GITHUB_REPOSITORY") return GitHubActions() + elseif haskey(ENV, "GITLAB_CI") + return GitLab() else return nothing end diff --git a/test/deployconfig.jl b/test/deployconfig.jl index 7b67a5f782..7d1e119278 100644 --- a/test/deployconfig.jl +++ b/test/deployconfig.jl @@ -289,6 +289,100 @@ end end end end end +@testset "GitLab CI deploy configuration" begin; with_logger(NullLogger()) do + # Regular tag build + withenv("GITLAB_CI" => "true", + "CI_COMMIT_BRANCH" => "master", + "CI_EXTERNAL_PULL_REQUEST_IID" => "", + "CI_PROJECT_PATH_SLUG" => "juliadocs-documenter-jl", + "CI_COMMIT_TAG" => "v1.2.3", + "CI_PIPELINE_SOURCE" => "push", + "DOCUMENTER_KEY" => "SGVsbG8sIHdvcmxkLg==", + ) do + cfg = Documenter.GitLab() + d = Documenter.deploy_folder(cfg; repo="github.com/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.com/JuliaDocs/Documenter.jl.git" + @test d.branch == "gh-pages" + @test Documenter.documenter_key(cfg) === "SGVsbG8sIHdvcmxkLg==" + @test Documenter.authentication_method(cfg) === Documenter.SSH + end + # Broken tag build + withenv("GITLAB_CI" => "true", + "CI_COMMIT_BRANCH" => "master", + "CI_EXTERNAL_PULL_REQUEST_IID" => "", + "CI_PROJECT_PATH_SLUG" => "juliadocs-documenter-jl", + "CI_COMMIT_TAG" => "not-a-version", + "CI_PIPELINE_SOURCE" => "push", + "DOCUMENTER_KEY" => "SGVsbG8sIHdvcmxkLg==", + ) do + cfg = Documenter.GitLab() + d = Documenter.deploy_folder(cfg; repo="github.com/JuliaDocs/Documenter.jl.git", + devbranch="master", devurl="dev", push_preview=true) + @test !d.all_ok + end + # Regular/broken devbranch build + withenv( + "GITLAB_CI" => "true", + "CI_COMMIT_BRANCH" => "master", + "CI_EXTERNAL_PULL_REQUEST_IID" => "", + "CI_PROJECT_PATH_SLUG" => "juliadocs-documenter-jl", + "CI_COMMIT_TAG" => nothing, + "CI_PIPELINE_SOURCE" => "push", + "DOCUMENTER_KEY" => "SGVsbG8sIHdvcmxkLg==", + ) do + cfg = Documenter.GitLab() + d = Documenter.deploy_folder(cfg; repo="github.com/JuliaDocs/Documenter.jl.git", + devbranch="master", devurl="hello-world", push_preview=true) + @test d.all_ok + @test d.subfolder == "hello-world" + @test d.repo == "github.com/JuliaDocs/Documenter.jl.git" + @test d.branch == "gh-pages" + d = Documenter.deploy_folder(cfg; repo="github.com/JuliaDocs/Documenter.jl.git", + devbranch="not-master", devurl="hello-world", push_preview=true) + @test !d.all_ok + @test Documenter.documenter_key(cfg) === "SGVsbG8sIHdvcmxkLg==" + end + # Regular pull request build + withenv("GITLAB_CI" => "true", + "CI_COMMIT_BRANCH" => "something", + "CI_EXTERNAL_PULL_REQUEST_IID" => "42", + "CI_PROJECT_PATH_SLUG" => "juliadocs-documenter-jl", + "CI_COMMIT_TAG" => nothing, + "CI_PIPELINE_SOURCE" => "push", + "DOCUMENTER_KEY" => "SGVsbG8sIHdvcmxkLg==", + ) do + cfg = Documenter.GitLab() + d = Documenter.deploy_folder(cfg; repo="github.com/JuliaDocs/Documenter.jl.git", + devbranch="master", devurl="hello-world", push_preview=true) + @test d.all_ok + @test d.subfolder == "previews/PR42" + @test d.repo == "github.com/JuliaDocs/Documenter.jl.git" + @test d.branch == "gh-pages" + d = Documenter.deploy_folder(cfg; repo="github.com/JuliaDocs/Documenter.jl.git", + devbranch="master", devurl="hello-world", push_preview=false) + @test !d.all_ok + @test Documenter.documenter_key(cfg) === "SGVsbG8sIHdvcmxkLg==" + end + # Missing/broken environment variables + withenv( + "GITLAB_CI" => "true", + "CI_COMMIT_BRANCH" => "master", + "CI_EXTERNAL_PULL_REQUEST_IID" => "", + "CI_PROJECT_PATH_SLUG" => "juliadocs-documenter-jl", + "CI_COMMIT_TAG" => "v1.2.3", + "CI_PIPELINE_SOURCE" => "push", + "DOCUMENTER_KEY" => nothing, + ) do + cfg = Documenter.GitLab() + d = Documenter.deploy_folder(cfg; repo="github.com/JuliaDocs/Documenter.jl.git", + devbranch="master", devurl="hello-world", push_preview=false) + @test !d.all_ok + end +end end + struct CustomConfig <: Documenter.DeployConfig end Documenter.deploy_folder(::CustomConfig; kwargs...) = Documenter.DeployDecision(; all_ok = true, subfolder = "v1.2.3") struct BrokenConfig <: Documenter.DeployConfig end