From fe7a713179ff7b8c9bc83eed56b28f1ea7b56a08 Mon Sep 17 00:00:00 2001 From: Douglas Vought Date: Wed, 11 Sep 2024 09:39:37 -0400 Subject: [PATCH] Document using .erlang.crypt with ExDoc Closes #1928. - Update documentation - Update test helper so that debug info options are forwarded to the compiler - Add basic tests - Update changelog - Use "CI" environment variable to skip the encrypted debug info test --- .github/workflows/ci.yml | 1 + CHANGELOG.md | 1 + lib/mix/tasks/docs.ex | 8 ++++ test/ex_doc/retriever/erlang_test.exs | 55 +++++++++++++++++++++++++++ test/fixtures/.erlang.crypt | 2 + test/test_helper.exs | 20 +++++++--- 6 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 test/fixtures/.erlang.crypt 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)