Skip to content

Commit

Permalink
Safety check before running File.rm_rf! in doc gen (#1707)
Browse files Browse the repository at this point in the history
  • Loading branch information
viniciusmuller committed May 29, 2023
1 parent 939da1a commit 9766ed7
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 7 deletions.
19 changes: 17 additions & 2 deletions lib/ex_doc/formatter/epub.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ defmodule ExDoc.Formatter.EPUB do
"""
@spec run(list, ExDoc.Config.t()) :: String.t()
def run(project_nodes, config) when is_map(config) do
parent = config.output
config = normalize_config(config)
File.rm_rf!(config.output)
File.mkdir_p!(Path.join(config.output, "OEBPS"))

HTML.setup_output(
parent,
&cleanup_output_dir(&1, config),
&create_output_dir(&1, config)
)

project_nodes = HTML.render_all(project_nodes, ".xhtml", config, highlight_tag: "samp")

Expand Down Expand Up @@ -44,6 +49,16 @@ defmodule ExDoc.Formatter.EPUB do
Path.relative_to_cwd(epub)
end

defp create_output_dir(root, config) do
File.mkdir_p!(Path.join(config.output, "OEBPS"))
File.touch!(Path.join(root, ".ex_doc"))
end

defp cleanup_output_dir(docs_root, config) do
File.rm_rf!(config.output)
create_output_dir(docs_root, config)
end

defp normalize_config(config) do
output =
config.output
Expand Down
43 changes: 38 additions & 5 deletions lib/ex_doc/formatter/html.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule ExDoc.Formatter.HTML do
config = %{config | output: Path.expand(config.output)}

build = Path.join(config.output, ".build")
output_setup(build, config)
setup_output(config.output, &cleanup_output_dir(&1, config), &create_output_dir(&1, config))

project_nodes = render_all(project_nodes, ".html", config, [])
extras = build_extras(config, ".html")
Expand Down Expand Up @@ -146,18 +146,51 @@ defmodule ExDoc.Formatter.HTML do
|> ExDoc.DocAST.highlight(language, opts)
end

defp output_setup(build, config) do
def setup_output(root, cleanup, create) do
safety_path = Path.join(root, ".ex_doc")

cond do
File.exists?(safety_path) and File.exists?(root) ->
cleanup.(root)

not File.exists?(root) ->
create.(root)

File.ls!(root) == [] ->
add_safety_file(root)
:ok

true ->
IO.warn(
"ExDoc is outputting to an existing directory. " <>
"Beware documentation output may be mixed with other entries"
)
end
end

defp create_output_dir(root, _config) do
File.mkdir_p!(root)
add_safety_file(root)
end

defp add_safety_file(root) do
File.touch!(Path.join(root, ".ex_doc"))
end

defp cleanup_output_dir(docs_root, config) do
build = Path.join(docs_root, ".build")

if File.exists?(build) do
build
|> File.read!()
|> String.split("\n", trim: true)
|> Enum.map(&Path.join(config.output, &1))
|> Enum.map(&Path.join(docs_root, &1))
|> Enum.each(&File.rm/1)

File.rm(build)
else
File.rm_rf!(config.output)
File.mkdir_p!(config.output)
File.rm_rf!(docs_root)
create_output_dir(docs_root, config)
end
end

Expand Down
27 changes: 27 additions & 0 deletions test/ex_doc/formatter/epub_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,33 @@ defmodule ExDoc.Formatter.EPUBTest do
assert File.regular?(tmp_dir <> "/epub/another_dir/#{doc_config(context)[:project]}.epub")
end

test "succeeds if trying to write into an empty existing directory", context do
config = doc_config(context)

new_output = config[:output] <> "/new-dir"
File.mkdir_p!(new_output)

new_config = Keyword.put(config, :output, new_output)

refute ExUnit.CaptureIO.capture_io(:stderr, fn ->
generate_docs(new_config)
end) =~ "ExDoc is outputting to an existing directory"
end

test "warns if trying to write into existing directory with files", context do
config = doc_config(context)
new_output = config[:output] <> "/new-dir"

File.mkdir_p!(new_output)
File.touch!(Path.join(new_output, "dummy-file"))

new_config = Keyword.put(config, :output, new_output)

assert ExUnit.CaptureIO.capture_io(:stderr, fn ->
generate_docs(new_config)
end) =~ "ExDoc is outputting to an existing directory"
end

test "generates an EPUB file with a standardized structure", %{tmp_dir: tmp_dir} = context do
generate_docs_and_unzip(context, doc_config(context))

Expand Down
6 changes: 6 additions & 0 deletions test/ex_doc/formatter/html/erlang_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ defmodule ExDoc.Formatter.HTML.ErlangTest do
@moduletag :otp_eep48
@moduletag :tmp_dir

setup %{tmp_dir: tmp_dir} do
output = tmp_dir <> "/doc"
File.mkdir!(output)
File.touch!("#{output}/.ex_doc")
end

test "smoke test", c do
erlc(c, :foo, ~S"""
%% @doc
Expand Down
6 changes: 6 additions & 0 deletions test/ex_doc/formatter/html/search_items_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ defmodule ExDoc.Formatter.HTML.SearchItemsTest do

@moduletag :tmp_dir

setup %{tmp_dir: tmp_dir} do
output = tmp_dir <> "/doc"
File.mkdir!(output)
File.touch!("#{output}/.ex_doc")
end

test "Elixir module", c do
modules =
elixirc(c, ~S'''
Expand Down
33 changes: 33 additions & 0 deletions test/ex_doc/formatter/html_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ defmodule ExDoc.Formatter.HTMLTest do

@moduletag :tmp_dir

setup %{tmp_dir: tmp_dir} do
output = tmp_dir <> "/html"
File.mkdir_p!(output)
File.touch!(output <> "/.ex_doc")
end

defp read_wildcard!(path) do
[file] = Path.wildcard(path)
File.read!(file)
Expand Down Expand Up @@ -174,6 +180,33 @@ defmodule ExDoc.Formatter.HTMLTest do
refute content_module =~ re[:index][:refresh]
end

test "succeeds if trying to write into an empty existing directory", context do
config = doc_config(context)

new_output = config[:output] <> "/new-dir"
File.mkdir_p!(new_output)

new_config = Keyword.put(config, :output, new_output)

refute ExUnit.CaptureIO.capture_io(:stderr, fn ->
generate_docs(new_config)
end) =~ "ExDoc is outputting to an existing directory"
end

test "warns if trying to write into existing directory with files", context do
config = doc_config(context)
new_output = config[:output] <> "/new-dir"

File.mkdir_p!(new_output)
File.touch!(Path.join(new_output, "dummy-file"))

new_config = Keyword.put(config, :output, new_output)

assert ExUnit.CaptureIO.capture_io(:stderr, fn ->
generate_docs(new_config)
end) =~ "ExDoc is outputting to an existing directory"
end

test "allows to set the authors of the document", %{tmp_dir: tmp_dir} = context do
generate_docs(doc_config(context, authors: ["John Doe", "Jane Doe"]))
content_index = File.read!(tmp_dir <> "/html/api-reference.html")
Expand Down
4 changes: 4 additions & 0 deletions test/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ defmodule TestHelper do
def elixirc(context, filename \\ "nofile", code) do
dir = context.tmp_dir

output_dir = context.tmp_dir <> "/html"
File.mkdir_p!(output_dir)
File.write!(output_dir <> "/.ex_doc", "")

src_path = Path.join([dir, filename])
src_path |> Path.dirname() |> File.mkdir_p!()
File.write!(src_path, code)
Expand Down

0 comments on commit 9766ed7

Please sign in to comment.