diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0f9f4e4d1..ef6c279af 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,6 +12,7 @@ jobs:
runs-on: ubuntu-20.04
env:
MIX_ENV: test
+ CI: true
strategy:
fail-fast: false
matrix:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 88c04679b..d61c93213 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
* Improve warning when referencing type from a private module
* Rename "Search HexDocs package" modal to "Go to package docs". Support built-in Erlang/OTP
apps.
+ * Document how to use `.erlang.crypt` with ExDoc
* Bug fixes
* Switch anchor `title` to `aria-label`
diff --git a/lib/mix/tasks/docs.ex b/lib/mix/tasks/docs.ex
index 250d037fb..2cf56a1b9 100644
--- a/lib/mix/tasks/docs.ex
+++ b/lib/mix/tasks/docs.ex
@@ -200,6 +200,14 @@ defmodule Mix.Tasks.Docs do
where path is either an relative path from the cwd, or an absolute path. The function
must return the full URI as it should be placed in the documentation.
+ ## Encrypted debug info
+
+ If a module is compiled with [encrypted debug info](`:compile.file/2`), ExDoc will not be able to
+ extract its documentation without preparation. ExDoc supports using `.erlang.crypt` to decrypt
+ debug information. Consult the
+ [`.erlang.crypt` section in the `:beam_lib` documentation](`m::beam_lib#module-erlang-crypt`)
+ for more information.
+
## Groups
ExDoc content can be organized in groups. This is done via the `:groups_for_extras`
diff --git a/test/ex_doc/retriever/erlang_test.exs b/test/ex_doc/retriever/erlang_test.exs
index bcba5f3c6..761fe4fa1 100644
--- a/test/ex_doc/retriever/erlang_test.exs
+++ b/test/ex_doc/retriever/erlang_test.exs
@@ -354,6 +354,61 @@ defmodule ExDoc.Retriever.ErlangTest do
|> Erlang.autolink_spec(current_module: :mod, current_kfa: {:type, :type, 0}) ==
"type() :: #a{a :: pos_integer(), b :: non_neg_integer(), c :: atom(), d :: term(), e :: term()}."
end
+
+ @tag :ci
+ test "modules with encrypted debug info", c do
+ File.cp!("test/fixtures/.erlang.crypt", ".erlang.crypt")
+
+ erlc(
+ c,
+ :debug_info_mod,
+ ~S"""
+ -module(debug_info_mod).
+ -moduledoc("mod docs.").
+ -export([function1/0]).
+ -export_type([foo/0]).
+
+ -doc("foo/0 docs.").
+ -type foo() :: atom().
+
+ -doc("function1/0 docs.").
+ -spec function1() -> atom().
+ function1() -> ok.
+ """,
+ debug_info_key: ~c"SECRET"
+ )
+
+ {[mod], []} = Retriever.docs_from_modules([:debug_info_mod], %ExDoc.Config{})
+
+ assert %ExDoc.ModuleNode{
+ moduledoc_file: moduledoc_file,
+ docs: [function1],
+ id: "debug_info_mod",
+ module: :debug_info_mod,
+ title: "debug_info_mod",
+ typespecs: [foo]
+ } = mod
+
+ assert DocAST.to_string(mod.doc) =~ "mod docs."
+ assert DocAST.to_string(function1.doc) =~ "function1/0 docs."
+ assert DocAST.to_string(foo.doc) =~ "foo/0 docs."
+ assert moduledoc_file =~ "debug_info_mod.erl"
+
+ erlc(
+ c,
+ :debug_info_mod2,
+ ~S"""
+ -module(debug_info_mod2).
+ -moduledoc("mod docs.").
+ """,
+ debug_info_key: {:des3_cbc, ~c"PASSWORD"}
+ )
+
+ assert {[%ExDoc.ModuleNode{module: :debug_info_mod2}], []} =
+ Retriever.docs_from_modules([:debug_info_mod2], %ExDoc.Config{})
+
+ File.rm!(".erlang.crypt")
+ end
end
describe "docs_from_modules/2 edoc" do
diff --git a/test/fixtures/.erlang.crypt b/test/fixtures/.erlang.crypt
new file mode 100644
index 000000000..befada42a
--- /dev/null
+++ b/test/fixtures/.erlang.crypt
@@ -0,0 +1,2 @@
+[{debug_info, des3_cbc, debug_info_mod, "SECRET"},
+ {debug_info, des3_cbc, [], "PASSWORD"}].
\ No newline at end of file
diff --git a/test/test_helper.exs b/test/test_helper.exs
index a555ba53f..0b6e5a613 100644
--- a/test/test_helper.exs
+++ b/test/test_helper.exs
@@ -1,10 +1,12 @@
otp_eep48? = Code.ensure_loaded?(:edoc_doclet_chunks)
otp_eep59? = Code.ensure_loaded?(:beam_doc)
+ci? = System.get_env("CI") == "true"
exclude = [
otp_eep48: not otp_eep48?,
otp_eep59: not otp_eep59?,
- otp_has_docs: not match?({:docs_v1, _, _, _, _, _, _}, Code.fetch_docs(:array))
+ otp_has_docs: not match?({:docs_v1, _, _, _, _, _, _}, Code.fetch_docs(:array)),
+ ci: not ci?
]
ExUnit.start(exclude: Enum.filter(exclude, &elem(&1, 1)))
@@ -58,14 +60,20 @@ defmodule TestHelper do
beam_docs = docstrings(docs, context)
+ # not to be confused with the regular :debug_info opt
+ debug_info_opts =
+ Enum.filter(opts, fn
+ {:debug_info, _debug_info} -> true
+ {:debug_info_key, _debug_info_key} -> true
+ :encrypt_debug_info -> true
+ _ -> false
+ end)
+
{:ok, module} =
:compile.file(
String.to_charlist(src_path),
- [
- :return_errors,
- :debug_info,
- outdir: String.to_charlist(ebin_dir)
- ] ++ beam_docs
+ [:return_errors, :debug_info, outdir: String.to_charlist(ebin_dir)] ++
+ beam_docs ++ debug_info_opts
)
true = Code.prepend_path(ebin_dir)