diff --git a/CHANGELOG.md b/CHANGELOG.md index 5560a1fead..cd3746d92b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * ![Bugfix][badge-bugfix] When including docstrings for an alias, Documenter now correctly tries to include the exactly matching docstring first, before checking for signature subtypes. ([#1842][github-1842]) * ![Bugfix][badge-bugfix] When checking for missing docstrings, Documenter now correctly handles docstrings for methods that extend bindings from other modules that have not been imported into the current module. ([#1695][github-1695], [#1857][github-1857], [#1861][github-1861]) +* ![Bugfix][badge-bugfix] By overriding `GIT_TEMPLATE_DIR`, `git` no longer picks up arbitrary user templates and hooks when internally called by Documenter. ([#1862][github-1862]) ## Version `v0.27.19` @@ -1083,6 +1084,7 @@ [github-1846]: https://github.com/JuliaDocs/Documenter.jl/pull/1846 [github-1857]: https://github.com/JuliaDocs/Documenter.jl/issues/1857 [github-1861]: https://github.com/JuliaDocs/Documenter.jl/pull/1861 +[github-1862]: https://github.com/JuliaDocs/Documenter.jl/pull/1862 [julia-38079]: https://github.com/JuliaLang/julia/issues/38079 diff --git a/src/Documenter.jl b/src/Documenter.jl index c801d279c8..5303e1d452 100644 --- a/src/Documenter.jl +++ b/src/Documenter.jl @@ -61,7 +61,7 @@ include("CrossReferences.jl") include("DocChecks.jl") include("Writers/Writers.jl") -import .Utilities: Selectors +import .Utilities: Selectors, git import .Writers.HTMLWriter: HTML, asset import .Writers.HTMLWriter.RD: KaTeX, MathJax, MathJax2, MathJax3 import .Writers.LaTeXWriter: LaTeX @@ -564,7 +564,7 @@ function deploydocs(; # the working directory has been changed (e.g. if the makedocs' build argument is # outside root). sha = try - readchomp(`git rev-parse --short HEAD`) + readchomp(`$(git()) rev-parse --short HEAD`) catch # git rev-parse will throw an error and return code 128 if it is not being # run in a git repository, which will make run/readchomp throw an exception. @@ -618,17 +618,17 @@ function git_push( # Generate a closure with common commands for ssh and https function git_commands(sshconfig=nothing) # Setup git. - run(`git init`) - run(`git config user.name "Documenter.jl"`) - run(`git config user.email "documenter@juliadocs.github.io"`) + run(`$(git()) init`) + run(`$(git()) config user.name "Documenter.jl"`) + run(`$(git()) config user.email "documenter@juliadocs.github.io"`) if sshconfig !== nothing - run(`git config core.sshCommand "ssh -F $(sshconfig)"`) + run(`$(git()) config core.sshCommand "ssh -F $(sshconfig)"`) end # Fetch from remote and checkout the branch. - run(`git remote add upstream $upstream`) + run(`$(git()) remote add upstream $upstream`) try - run(`git fetch upstream`) + run(`$(git()) fetch upstream`) catch e @error """ Git failed to fetch $upstream @@ -640,7 +640,7 @@ function git_push( end try - run(`git checkout -b $branch upstream/$branch`) + run(`$(git()) checkout -b $branch upstream/$branch`) catch e @info """ Checking out $branch failed, creating a new orphaned branch. @@ -649,8 +649,8 @@ function git_push( from Git in this situation. """ @debug "checking out $branch failed with error: $e" - run(`git checkout --orphan $branch`) - run(`git commit --allow-empty -m "Initial empty commit for docs"`) + run(`$(git()) checkout --orphan $branch`) + run(`$(git()) commit --allow-empty -m "Initial empty commit for docs"`) end # Copy docs to `subfolder` directory. @@ -691,14 +691,14 @@ function git_push( end # Add, commit, and push the docs to the remote. - run(`git add -A .`) - if !success(`git diff --cached --exit-code`) + run(`$(git()) add -A .`) + if !success(`$(git()) diff --cached --exit-code`) if forcepush - run(`git commit --amend --date=now -m "build based on $sha"`) - run(`git push -fq upstream HEAD:$branch`) + run(`$(git()) commit --amend --date=now -m "build based on $sha"`) + run(`$(git()) push -fq upstream HEAD:$branch`) else - run(`git commit -m "build based on $sha"`) - run(`git push -q upstream HEAD:$branch`) + run(`$(git()) commit -m "build based on $sha"`) + run(`$(git()) push -q upstream HEAD:$branch`) end else @debug "new docs identical to the old -- not committing nor pushing." @@ -820,7 +820,7 @@ function gitrm_copy(src, dst) if isdir(dst) for x in filter!(!in((".git", "previews")), readdir(dst)) # --ignore-unmatch so that we wouldn't get errors if dst does not exist - run(`git rm -rf --ignore-unmatch $(joinpath(dst, x))`) + run(`$(git()) rm -rf --ignore-unmatch $(joinpath(dst, x))`) end end # git rm also remove parent directories diff --git a/src/Utilities/Utilities.jl b/src/Utilities/Utilities.jl index 30e45c025c..2bd1f660e2 100644 --- a/src/Utilities/Utilities.jl +++ b/src/Utilities/Utilities.jl @@ -425,7 +425,7 @@ end function repo_commit(file) cd(dirname(file)) do - readchomp(`git rev-parse HEAD`) + readchomp(`$(git()) rev-parse HEAD`) end end @@ -515,7 +515,7 @@ const GIT_REMOTE_CACHE = Dict{String,String}() function getremote(dir::AbstractString) return get!(GIT_REMOTE_CACHE, dir) do remote = try - readchomp(setenv(`git config --get remote.origin.url`; dir=dir)) + readchomp(setenv(`$(git()) config --get remote.origin.url`; dir=dir)) catch "" end @@ -531,7 +531,7 @@ Returns the first 5 characters of the current git commit hash of the directory ` """ function get_commit_short(dir) commit = cd(dir) do - readchomp(`git rev-parse HEAD`) + readchomp(`$(git()) rev-parse HEAD`) end (length(commit) > 5) ? commit[1:5] : commit end @@ -790,13 +790,16 @@ it out automatically. to construct the warning messages. """ function git_remote_head_branch(varname, root; remotename = "origin", fallback = "master") - env = copy(ENV) - env["GIT_TERMINAL_PROMPT"] = "0" - env["GIT_SSH_COMMAND"] = get(env, "GIT_SSH_COMMAND", "ssh -o \"BatchMode yes\"") - cmd = `git remote show $(remotename)` + # We need to do addenv() here to merge the new variables with the environment set by + # Git_jll and the git() function. + cmd = addenv( + setenv(`$(git()) remote show $(remotename)`, dir=root), + "GIT_TERMINAL_PROMPT" => "0", + "GIT_SSH_COMMAND" => get(ENV, "GIT_SSH_COMMAND", "ssh -o \"BatchMode yes\""), + ) stderr_output = IOBuffer() git_remote_output = try - read(pipeline(setenv(cmd, env; dir=root); stderr = stderr_output), String) + read(pipeline(cmd; stderr = stderr_output), String) catch e @warn """ Unable to determine $(varname) from remote HEAD branch, defaulting to "$(fallback)". @@ -846,6 +849,15 @@ dropheaders(h::Markdown.Header) = Markdown.Paragraph([Markdown.Bold(h.text)]) dropheaders(v::Vector) = map(dropheaders, v) dropheaders(other) = other +function git(; kwargs...) + system_git_path = Sys.which("git") + isnothing(system_git_path) && error("Unable to find `git`") + # According to the Git man page, the default GIT_TEMPLATE_DIR is at /usr/share/git-core/templates + # We need to set this to something so that Git wouldn't pick up the user + # templates (e.g. from init.templateDir config). + return addenv(`$(system_git_path)`, "GIT_TEMPLATE_DIR" => "/usr/share/git-core/templates") +end + include("DOM.jl") include("MDFlatten.jl") include("TextDiff.jl") diff --git a/test/doctests/doctests.jl b/test/doctests/doctests.jl index 1285098973..ec06c4e15b 100644 --- a/test/doctests/doctests.jl +++ b/test/doctests/doctests.jl @@ -34,13 +34,17 @@ function run_makedocs(f, mdfiles, modules=Module[]; kwargs...) touch(joinpath(srcdir, "index.md")) c = IOCapture.capture(rethrow = InterruptException) do - makedocs( - sitename = " ", - format = Documenter.HTML(edit_link = "master"), - root = dir, - modules = modules; - kwargs... - ) + # In case JULIA_DEBUG is set to something, we'll override that, so that we wouldn't + # get some unexpected debug output from makedocs. + withenv("JULIA_DEBUG" => "") do + makedocs( + sitename = " ", + format = Documenter.HTML(edit_link = "master"), + root = dir, + modules = modules; + kwargs... + ) + end end @debug """run_makedocs($mdfiles, modules=$modules) -> $(c.error ? "fail" : "success") diff --git a/test/utilities.jl b/test/utilities.jl index 69d909440b..8a30ff3dfc 100644 --- a/test/utilities.jl +++ b/test/utilities.jl @@ -1,10 +1,12 @@ module UtilitiesTests using Test +using Logging: Info import Base64: stringmime include("TestUtilities.jl"); using .TestUtilities import Documenter +using Documenter.Utilities: git import Markdown module UnitTests @@ -166,15 +168,15 @@ end mkpath(path_repo) cd(path_repo) do # Create a simple mock repo in a temporary directory with a single file. - @test trun(`git init`) - @test trun(`git config user.email "tester@example.com"`) - @test trun(`git config user.name "Test Committer"`) - @test trun(`git remote add origin git@github.com:JuliaDocs/Documenter.jl.git`) + @test trun(`$(git()) init`) + @test trun(`$(git()) config user.email "tester@example.com"`) + @test trun(`$(git()) config user.name "Test Committer"`) + @test trun(`$(git()) remote add origin git@github.com:JuliaDocs/Documenter.jl.git`) mkpath("src") filepath = abspath(joinpath("src", "SourceFile.jl")) write(filepath, "X") - @test trun(`git add -A`) - @test trun(`git commit -m"Initial commit."`) + @test trun(`$(git()) add -A`) + @test trun(`$(git()) commit -m"Initial commit."`) # Run tests commit = Documenter.Utilities.repo_commit(filepath) @@ -194,7 +196,7 @@ end # Test worktree path_worktree = joinpath(path, "worktree") cd("$(path_repo)") do - @test trun(`git worktree add $(path_worktree)`) + @test trun(`$(git()) worktree add $(path_worktree)`) end cd("$(path_worktree)") do filepath = abspath(joinpath("src", "SourceFile.jl")) @@ -217,15 +219,15 @@ end path_submodule = joinpath(path, "submodule") mkpath(path_submodule) cd(path_submodule) do - @test trun(`git init`) - @test trun(`git config user.email "tester@example.com"`) - @test trun(`git config user.name "Test Committer"`) + @test trun(`$(git()) init`) + @test trun(`$(git()) config user.email "tester@example.com"`) + @test trun(`$(git()) config user.name "Test Committer"`) # NOTE: the target path in the `git submodule add` command is necessary for # Windows builds, since otherwise Git claims that the path is in a .gitignore # file. - @test trun(`git submodule add $(path_repo) repository`) - @test trun(`git add -A`) - @test trun(`git commit -m"Initial commit."`) + @test trun(`$(git()) submodule add $(path_repo) repository`) + @test trun(`$(git()) add -A`) + @test trun(`$(git()) commit -m"Initial commit."`) end path_submodule_repo = joinpath(path, "submodule", "repository") @test isdir(path_submodule_repo) @@ -540,7 +542,7 @@ end function git_create_bare_repo(path; head = nothing) mkdir(path) - @test trun(`git -C $(path) init --bare`) + @test trun(`$(git()) -C $(path) init --bare`) @test isfile(joinpath(path, "HEAD")) if head !== nothing write(joinpath(path, "HEAD"), """ @@ -551,34 +553,36 @@ end # We need to commit something to the non-standard branch to actually # "activate" the non-standard HEAD: head = (head === nothing) ? "master" : head - @test trun(`git clone $(path) $(subdir_path)`) - @test trun(`git -C $(subdir_path) config user.email "tester@example.com"`) - @test trun(`git -C $(subdir_path) config user.name "Test Committer"`) - @test trun(`git -C $(subdir_path) checkout -b $(head)`) - @test trun(`git -C $(subdir_path) commit --allow-empty -m"initial empty commit"`) - @test trun(`git -C $(subdir_path) push --set-upstream origin $(head)`) + @test trun(`$(git()) clone $(path) $(subdir_path)`) + @test trun(`$(git()) -C $(subdir_path) config user.email "tester@example.com"`) + @test trun(`$(git()) -C $(subdir_path) config user.name "Test Committer"`) + @test trun(`$(git()) -C $(subdir_path) checkout -b $(head)`) + @test trun(`$(git()) -C $(subdir_path) commit --allow-empty -m"initial empty commit"`) + @test trun(`$(git()) -C $(subdir_path) push --set-upstream origin $(head)`) end end mktempdir() do path cd(path) do + # Note: running @test_logs with match_mode=:any here so that the tests would + # also pass when e.g. JULIA_DEBUG=Documenter when the tests are being run. # If there is no parent remote repository, we should get a warning and the fallback value: - @test (@test_logs (:warn,) Documenter.Utilities.git_remote_head_branch(".", pwd(); fallback = "fallback")) == "fallback" - @test (@test_logs (:warn,) Documenter.Utilities.git_remote_head_branch(".", pwd())) == "master" + @test (@test_logs (:warn,) match_mode=:any Documenter.Utilities.git_remote_head_branch(".", pwd(); fallback = "fallback")) == "fallback" + @test (@test_logs (:warn,) match_mode=:any Documenter.Utilities.git_remote_head_branch(".", pwd())) == "master" # We'll set up two "remote" bare repositories with non-standard HEADs: git_create_bare_repo("barerepo", head = "maindevbranch") git_create_bare_repo("barerepo_other", head = "main") # Clone barerepo and test git_remote_head_branch: - @test trun(`git clone barerepo/ local/`) + @test trun(`$(git()) clone barerepo/ local/`) @test Documenter.Utilities.git_remote_head_branch(".", "local") == "maindevbranch" # Now, let's add the other repo as another remote, and fetch the HEAD for that: - @test trun(`git -C local/ remote add other ../barerepo_other/`) - @test trun(`git -C local/ fetch other`) + @test trun(`$(git()) -C local/ remote add other ../barerepo_other/`) + @test trun(`$(git()) -C local/ fetch other`) @test Documenter.Utilities.git_remote_head_branch(".", "local") == "maindevbranch" @test Documenter.Utilities.git_remote_head_branch(".", "local"; remotename = "other") == "main" # Asking for a nonsense remote should also warn and drop back to fallback: - @test (@test_logs (:warn,) Documenter.Utilities.git_remote_head_branch(".", pwd(); remotename = "nonsense", fallback = "fallback")) == "fallback" - @test (@test_logs (:warn,) Documenter.Utilities.git_remote_head_branch(".", pwd(); remotename = "nonsense")) == "master" + @test (@test_logs (:warn,) match_mode=:any Documenter.Utilities.git_remote_head_branch(".", pwd(); remotename = "nonsense", fallback = "fallback")) == "fallback" + @test (@test_logs (:warn,) match_mode=:any Documenter.Utilities.git_remote_head_branch(".", pwd(); remotename = "nonsense")) == "master" end end end