From e6997ed05dad7146054d7514ee54b1c53b6296ae Mon Sep 17 00:00:00 2001 From: James Wrigley Date: Tue, 9 Apr 2024 23:01:33 +0200 Subject: [PATCH] Allow letting the docs build to fail with servedocs() (#176) Previously `servedocs()` would exit whenever the docs failed to build, and it can be annoying to always restart it on errors. Now when building the docs fails a special status will be set on the `SimpleWatcher` which will get `serve_file()` to return a custom error page (which will be reloaded automatically when the docs build again). --- src/server.jl | 64 ++++++++++++++++++++++++++++++++++---------------- src/utils.jl | 9 ++++++- test/server.jl | 16 +++++++++++++ test/utils.jl | 10 ++++++++ 4 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/server.jl b/src/server.jl index 23a2f1b..6bdd0d3 100644 --- a/src/server.jl +++ b/src/server.jl @@ -349,9 +349,11 @@ function serve_file( ret_code = 200 fs_path, case = get_fs_path(req.target) - if case == :not_found_without_404 - html_404 = pagehtml(title = "404 Not Found") do io - write(io, """ + # Fast paths to execute if building documentation isn't currently failing + if fw.status != :documenter_jl_error + if case == :not_found_without_404 + html_404 = pagehtml(title = "404 Not Found") do io + write(io, """

404 Not Found

@@ -366,26 +368,48 @@ function serve_file(

""" - ) + ) + end + return HTTP.Response(404, html_404) + elseif case == :not_found_with_404 + ret_code = 404 + elseif case == :dir_without_index + index_page = get_dir_list(fs_path) + return HTTP.Response(200, index_page) end - return HTTP.Response(404, html_404) - elseif case == :not_found_with_404 - ret_code = 404 - elseif case == :dir_without_index - index_page = get_dir_list(fs_path) - return HTTP.Response(200, index_page) end - # - # In what follows, fs_path points to a file - # :dir_with_index - # :file - # :not_found_with_404 - # --> html-like: try to inject reload-script - # --> other: just get the browser to show it - # - ext = lstrip(last(splitext(fs_path)), '.') |> string - content = read(fs_path, String) + ext = nothing + content = nothing + + # If building the documentation is failing we return a special error page, + # otherwise we just read the file from disk. + if fw.status == :documenter_jl_error + ret_code = 500 + ext = "html" + content = pagehtml(title = "Documenter.jl error") do io + write(io, """ +
+

Error building docs

+

+ An error occurred when rebuilding the documentation, please check the servedocs() output. +

+
+ """ + ) + end + else + # + # In what follows, fs_path points to a file + # :dir_with_index + # :file + # :not_found_with_404 + # --> html-like: try to inject reload-script + # --> other: just get the browser to show it + # + ext = lstrip(last(splitext(fs_path)), '.') |> string + content = read(fs_path, String) + end # build the response with appropriate mime type (this is inspired from Mux # https://github.com/JuliaWeb/Mux.jl/blob/master/src/examples/files.jl) diff --git a/src/utils.jl b/src/utils.jl index c520ad1..7fd7fca 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -49,7 +49,14 @@ function servedocs_callback!( end # Run a Documenter pass - Main.include(abspath(path2makejl)) + try + Main.include(abspath(path2makejl)) + dw.status = :runnable + catch ex + # If there was an error, record it so that an error is displayed to the user + dw.status = :documenter_jl_error + end + file_changed_callback(fp) return end diff --git a/test/server.jl b/test/server.jl index c14d64d..f82ab56 100644 --- a/test/server.jl +++ b/test/server.jl @@ -127,6 +127,22 @@ tasks that you will try to start. @test !sentinel1.io.io.writable @test !sentinel2.io.io.writable + # Mimic an error when building documentation with Documenter.jl. When this + # happens servedocs_callback!() will set the watcher status to + # :documenter_jl_error. + fw.status = :documenter_jl_error + + # Now any requests should give us the custom error page + response = HTTP.get("http://localhost:$port/"; status_exception=false) + body_str = String(response.body) + @test response.status == 500 + @test occursin("error occurred when rebuilding", body_str) + # And they should include the browser reload script so they automatically + # reload when the docs build again. + @test occursin(LS.BROWSER_RELOAD_SCRIPT, body_str) + # Reset the watcher status for the rest of the tests + fw.status = :runnable + # if we remove the files, it shall stop following it rm("tmp.html") rm("css", recursive=true) diff --git a/test/utils.jl b/test/utils.jl index baf7f16..816ac33 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -49,6 +49,16 @@ @test length(dw.watchedfiles) == 2 @test readmake() == 4 + # Modify make.jl to force an error + write(makejl, "error()") + LS.servedocs_callback!(dw, joinpath("docs", "src", "index.md"), makejl, def...) + @test dw.status == :documenter_jl_error + + # Fixing the error should reset the status + write(makejl, "42") + LS.servedocs_callback!(dw, joinpath("docs", "src", "index.md"), makejl, def...) + @test dw.status == :runnable + cd(bk) end