diff --git a/README.md b/README.md index 0f5658f3..95c11d47 100644 --- a/README.md +++ b/README.md @@ -84,3 +84,9 @@ Check [`.github/workflows/deploy.yml`](.github/workflows/deploy.yml) and [`docs/ The result of that script is available at [https://juliacomputing.github.io/MultiDocumenter.jl/](https://juliacomputing.github.io/MultiDocumenter.jl/). You can of course also just push the output artefact directly to S3 or some other hosting service. + +> **Warning** +> MultiDocumenter sites can not be deployed on Windows right now, and the `make()` function will throw an error. +> See [#70](https://github.com/JuliaComputing/MultiDocumenter.jl/issues/70). +> +> It is still possible to develop and debug MultiDocumenter sites on Windows if the build script is run interactively (e.g. by `include`-ing it into a REPL session). diff --git a/src/MultiDocumenter.jl b/src/MultiDocumenter.jl index b1237155..f5d69232 100644 --- a/src/MultiDocumenter.jl +++ b/src/MultiDocumenter.jl @@ -140,7 +140,26 @@ function make( canonical_domain::Union{AbstractString,Nothing} = nothing, sitemap::Bool = false, sitemap_filename::AbstractString = "sitemap.xml", + # This keyword is for internal test use only: + _override_windows_isinteractive_check::Bool = false, ) + if Sys.iswindows() && !isinteractive() + if _override_windows_isinteractive_check || isinteractive() + @warn """ + Running a MultiDocumenter build interactively in Windows. + This should only be used for development and testing, as it will lead to partial + and broken builds. See https://github.com/JuliaComputing/MultiDocumenter.jl/issues/70 + """ + else + msg = """ + MultiDocumenter deployments are disabled on Windows due to difficulties + with handling symlinks in documentation sources. + You _can_ test this build locally by running it interactively (i.e. in the REPL). + See also: https://github.com/JuliaComputing/MultiDocumenter.jl/issues/70 + """ + error(msg) + end + end if isnothing(canonical_domain) (sitemap === true) && throw(ArgumentError("When sitemap=true, canonical_domain must also be set")) @@ -249,7 +268,8 @@ function maybe_clone(docs::Vector{MultiDocRef}) `$(git()) clone --depth 1 $(doc.giturl) --branch $(doc.branch) --single-branch --no-tags $(doc.upstream)`, ) else - git_dir, git_worktree = abspath(joinpath(doc.upstream, ".git")), abspath(doc.upstream) + git_dir, git_worktree = + abspath(joinpath(doc.upstream, ".git")), abspath(doc.upstream) if !isdir(git_dir) @warn "Unable to update existing clone at $(doc.upstream): .git/ directory missing" continue @@ -266,7 +286,8 @@ function maybe_clone(docs::Vector{MultiDocRef}) catch e # We're only interested in catching `git` errors here isa(e, ProcessFailedException) || rethrow() - @error "Unable to update existing clone at $(doc.upstream)" exception = (e, catch_backtrace()) + @error "Unable to update existing clone at $(doc.upstream)" exception = + (e, catch_backtrace()) end end end diff --git a/src/documentertools/canonical_urls.jl b/src/documentertools/canonical_urls.jl index ea9ca040..fae84d0c 100644 --- a/src/documentertools/canonical_urls.jl +++ b/src/documentertools/canonical_urls.jl @@ -101,7 +101,9 @@ function update_canonical_links(docs_directory::AbstractString; canonical::Abstr # We'll skip all files. This includes files such as index.html, which in this # directory will likely be the redirect. Also, links should be pointing to other # versions, so we'll skip them too. - if !isdir(path) || islink(path) + # Note: we need to check islink() first, because on windows, calling isdir() on a + # symlink can make it throw a permissions IOError. + if islink(path) || !isdir(path) continue end # Preview directory is should contain other Documenter directories, so we just add diff --git a/test/documentertools.jl b/test/documentertools.jl index 3b44a85d..bb55b497 100644 --- a/test/documentertools.jl +++ b/test/documentertools.jl @@ -3,6 +3,8 @@ import MultiDocumenter: DocumenterTools FIXTURES = joinpath(@__DIR__, "fixtures") +normalize_newlines(s::AbstractString) = replace(s, "\r\n" => "\n") + @testset "walkdocs" begin let fileinfos = DocumenterTools.FileInfo[] rs = DocumenterTools.walkdocs(joinpath(FIXTURES, "pre")) do fileinfo @@ -123,8 +125,8 @@ end canonical = "https://example.org/this-is-test", ) DocumenterTools.walkdocs(joinpath(FIXTURES, "post")) do fileinfo - post = read(fileinfo.fullpath, String) - changed = read(joinpath(out, fileinfo.relpath), String) + post = normalize_newlines(read(fileinfo.fullpath, String)) + changed = normalize_newlines(read(joinpath(out, fileinfo.relpath), String)) if changed != post @error "update_canonical_links: change and post not matching" out fileinfo end diff --git a/test/runtests.jl b/test/runtests.jl index 5c3b8de9..cfdae536 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -84,6 +84,30 @@ docs = [ # giturl = "git@github.com:JuliaComputing/DataSets.jl.git", ), ] + +# We do not support deploying docs on Windows at the moment, and MultiDocumenter +# should throw an error if it's being run on Windows (in a non-interactive session). +# See also: https://github.com/JuliaComputing/MultiDocumenter.jl/issues/70 +if Sys.iswindows() && !isinteractive() + @test_throws ErrorException MultiDocumenter.make( + outpath, + docs; + search_engine = MultiDocumenter.SearchConfig( + index_versions = ["stable", "dev"], + engine = MultiDocumenter.FlexSearch, + ), + custom_scripts = [ + "foo/bar.js", + "https://foo.com/bar.js", + Docs.HTML("const foo = 'bar';"), + ], + rootpath = rootpath, + canonical_domain = "https://example.org/", + sitemap = true, + sitemap_filename = "sitemap-mydocs.xml", + ) +end + MultiDocumenter.make( outpath, docs; @@ -100,6 +124,8 @@ MultiDocumenter.make( canonical_domain = "https://example.org/", sitemap = true, sitemap_filename = "sitemap-mydocs.xml", + # The following keyword is not standard: + _override_windows_isinteractive_check = Sys.iswindows(), ) @testset "MultiDocumenter.jl" begin @@ -107,8 +133,16 @@ MultiDocumenter.make( @testset "structure" begin @test isdir(outpath, "inf") @test !isdir(outpath, "inf", "previews") - @test isdir(outpath, "inf", "stable") - @test isfile(outpath, "inf", "stable", "index.html") + if Sys.iswindows() + # On Windows, symlinks are either kept as simple files, or are in fact + # symlinks, but then you would run into permission errors with isdir(). + # So we need to have platform-specific test logic here. + path = joinpath(outpath, "inf", "stable") + @test islink(path) || isfile(path) + else + @test isdir(outpath, "inf", "stable") + @test isfile(outpath, "inf", "stable", "index.html") + end @test read(joinpath(outpath, "inf", "index.html"), String) == """ @@ -122,8 +156,7 @@ MultiDocumenter.make( @testset "custom scripts" begin - index = read(joinpath(outpath, "inf", "stable", "index.html"), String) - + index = read(joinpath(outpath, "inf", "v1.6.4", "index.html"), String) @test occursin( """""", index, @@ -140,12 +173,36 @@ MultiDocumenter.make( """""", index, ) + + if !Sys.iswindows() + # Going through symlinks does not work on Windows + index = read(joinpath(outpath, "inf", "stable", "index.html"), String) + @test occursin( + """""", + index, + ) + @test occursin( + """""", + index, + ) + @test occursin( + """""", + index, + ) + @test occursin( + """""", + index, + ) + end end @testset "canonical URLs" begin - index = read(joinpath(outpath, "inf", "stable", "index.html"), String) - canonical_href = "" - @test occursin(canonical_href, index) + # We can't traverse symlinks on Windows, so we ignore this case + if !Sys.iswindows() + index = read(joinpath(outpath, "inf", "stable", "index.html"), String) + canonical_href = "" + @test occursin(canonical_href, index) + end index = read(joinpath(outpath, "inf", "v1.6.0", "index.html"), String) canonical_href = "" @@ -158,9 +215,17 @@ MultiDocumenter.make( @test !isempty(store_content) @test occursin("Infiltrator.jl", store_content) @test occursin("@infiltrate", store_content) - @test occursin("$(rootpath)inf/stable/", store_content) - @test occursin("$(rootpath)inf/stable/", store_content) - @test !occursin("/inf/dev/", store_content) + # We can't traverse symlinks on Windows, so stable/ things do not get + # written into the search index. Instead, it looks like we write dev/ + if Sys.iswindows() + @test !occursin("$(rootpath)inf/stable/", store_content) + @test !occursin("$(rootpath)inf/stable/", store_content) + @test occursin("/inf/dev/", store_content) + else + @test occursin("$(rootpath)inf/stable/", store_content) + @test occursin("$(rootpath)inf/stable/", store_content) + @test !occursin("/inf/dev/", store_content) + end end @testset "sitemap" begin