Skip to content

Commit 81f80da

Browse files
committed
Add --warnings-as-errors flag for non-zero exit code
1 parent 7fa36d5 commit 81f80da

File tree

10 files changed

+128
-33
lines changed

10 files changed

+128
-33
lines changed

lib/ex_doc/application.ex

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ defmodule ExDoc.Application do
1717
match?("makeup_" <> _, Atom.to_string(app)),
1818
do: Application.ensure_all_started(app)
1919

20-
Supervisor.start_link([ExDoc.Refs], strategy: :one_for_one)
20+
children = [
21+
ExDoc.Refs,
22+
ExDoc.WarningCounter
23+
]
24+
25+
Supervisor.start_link(children, strategy: :one_for_one)
2126
end
2227
end

lib/ex_doc/cli.ex

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,25 @@ defmodule ExDoc.CLI do
2929
source_ref: :string,
3030
version: :boolean,
3131
formatter: :keep,
32-
quiet: :boolean
32+
quiet: :boolean,
33+
warnings_as_errors: :boolean
3334
]
3435
)
3536

36-
if List.keymember?(opts, :version, 0) do
37-
print_version()
38-
else
39-
generate(args, opts, generator)
37+
cond do
38+
List.keymember?(opts, :version, 0) ->
39+
print_version()
40+
41+
opts[:warnings_as_errors] == true and ExDoc.WarningCounter.count() > 0 ->
42+
IO.puts(
43+
:stderr,
44+
"Doc generation failed due to warnings while using the --warnings-as-errors option"
45+
)
46+
47+
exit({:shutdown, ExDoc.WarningCounter.count()})
48+
49+
true ->
50+
generate(args, opts, generator)
4051
end
4152
end
4253

@@ -159,29 +170,30 @@ defmodule ExDoc.CLI do
159170
ex_doc "Project" "1.0.0" "_build/dev/lib/project/ebin" -c "docs.exs"
160171
161172
Options:
162-
PROJECT Project name
163-
VERSION Version number
164-
BEAMS Path to compiled beam files
165-
-n, --canonical Indicate the preferred URL with rel="canonical" link element
166-
-c, --config Give configuration through a file instead of a command line.
167-
See "Custom config" section below for more information.
168-
-f, --formatter Docs formatter to use (html or epub), default: html and epub
169-
-p, --homepage-url URL to link to for the site name
170-
--paths Prepends the given path to Erlang code path. The path might contain a glob
171-
pattern but in that case, remember to quote it: --paths "_build/dev/lib/*/ebin".
172-
This option can be given multiple times
173-
--language Identify the primary language of the documents, its value must be
174-
a valid [BCP 47](https://tools.ietf.org/html/bcp47) language tag, default: "en"
175-
-l, --logo Path to the image logo of the project (only PNG or JPEG accepted)
176-
The image size will be 64x64 and copied to the assets directory
177-
-m, --main The entry-point page in docs, default: "api-reference"
178-
--package Hex package name
179-
--proglang The project's programming language, default: "elixir"
180-
--source-ref Branch/commit/tag used for source link inference, default: "master"
181-
-u, --source-url URL to the source code
182-
-o, --output Path to output docs, default: "doc"
183-
-v, --version Print ExDoc version
184-
-q, --quiet Only output warnings and errors
173+
PROJECT Project name
174+
VERSION Version number
175+
BEAMS Path to compiled beam files
176+
-n, --canonical Indicate the preferred URL with rel="canonical" link element
177+
-c, --config Give configuration through a file instead of a command line.
178+
See "Custom config" section below for more information.
179+
-f, --formatter Docs formatter to use (html or epub), default: html and epub
180+
-p, --homepage-url URL to link to for the site name
181+
--paths Prepends the given path to Erlang code path. The path might contain a glob
182+
pattern but in that case, remember to quote it: --paths "_build/dev/lib/*/ebin".
183+
This option can be given multiple times
184+
--language Identify the primary language of the documents, its value must be
185+
a valid [BCP 47](https://tools.ietf.org/html/bcp47) language tag, default: "en"
186+
-l, --logo Path to the image logo of the project (only PNG or JPEG accepted)
187+
The image size will be 64x64 and copied to the assets directory
188+
-m, --main The entry-point page in docs, default: "api-reference"
189+
--package Hex package name
190+
--proglang The project's programming language, default: "elixir"
191+
--source-ref Branch/commit/tag used for source link inference, default: "master"
192+
-u, --source-url URL to the source code
193+
-o, --output Path to output docs, default: "doc"
194+
-v, --version Print ExDoc version
195+
-q, --quiet Only output warnings and errors
196+
--warnings-as-errors Exit with non-zero status if doc generation has one or more warnings
185197
186198
## Custom config
187199

lib/ex_doc/config.ex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ defmodule ExDoc.Config do
4040
version: nil,
4141
authors: nil,
4242
skip_undefined_reference_warnings_on: [],
43-
package: nil
43+
package: nil,
44+
warnings_as_errors: false
4445

4546
@type t :: %__MODULE__{
4647
apps: [atom()],
@@ -75,7 +76,8 @@ defmodule ExDoc.Config do
7576
version: nil | String.t(),
7677
authors: nil | [String.t()],
7778
skip_undefined_reference_warnings_on: [String.t()],
78-
package: :atom | nil
79+
package: :atom | nil,
80+
warnings_as_errors: boolean()
7981
}
8082

8183
@spec build(String.t(), String.t(), Keyword.t()) :: ExDoc.Config.t()

lib/ex_doc/language.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ defmodule ExDoc.Language do
135135
def get(:erlang, _module), do: {:ok, ExDoc.Language.Erlang}
136136

137137
def get(language, module) when is_atom(language) and is_atom(module) do
138+
ExDoc.WarningCounter.increment()
139+
138140
IO.warn(
139141
"skipping module #{module}, reason: unsupported language (#{language})",
140142
[]

lib/ex_doc/markdown/earmark.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ defmodule ExDoc.Markdown.Earmark do
4848
defp print_messages(messages, options) do
4949
for {severity, line, message} <- messages do
5050
file = options[:file]
51+
ExDoc.WarningCounter.increment()
5152
IO.warn("#{inspect(__MODULE__)} (#{severity}) #{file}:#{line} #{message}", [])
5253
end
5354
end

lib/ex_doc/retriever.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ defmodule ExDoc.Retriever do
7777
docs
7878

7979
{:error, reason} ->
80+
ExDoc.WarningCounter.increment()
8081
IO.warn("skipping module #{inspect(module)}, reason: #{reason}", [])
8182
false
8283
end

lib/ex_doc/warning_counter.ex

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
defmodule ExDoc.WarningCounter do
2+
@moduledoc false
3+
4+
use Agent
5+
6+
def start_link(_opts) do
7+
Agent.start_link(fn -> 0 end, name: __MODULE__)
8+
end
9+
10+
def count do
11+
Agent.get(__MODULE__, & &1)
12+
end
13+
14+
def increment do
15+
Agent.update(__MODULE__, &(&1 + 1))
16+
end
17+
18+
def reset do
19+
Agent.update(__MODULE__, fn _ -> 0 end)
20+
end
21+
end

lib/mix/tasks/docs.ex

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ defmodule Mix.Tasks.Docs do
2424
2525
* `--open` - open browser window pointed to the documentation
2626
27+
* `--warnings-as-errors` - Exits with non-zero exit code if any warnings are found
28+
2729
The command line options have higher precedence than the options
2830
specified in your `mix.exs` file below.
2931
@@ -309,7 +311,8 @@ defmodule Mix.Tasks.Docs do
309311
formatter: :keep,
310312
language: :string,
311313
output: :string,
312-
open: :boolean
314+
open: :boolean,
315+
warnings_as_errors: :boolean
313316
]
314317

315318
@aliases [n: :canonical, f: :formatter, o: :output]
@@ -363,7 +366,16 @@ defmodule Mix.Tasks.Docs do
363366
browser_open(index)
364367
end
365368

366-
index
369+
if options[:warnings_as_errors] == true and ExDoc.WarningCounter.count() > 0 do
370+
Mix.shell().info([
371+
:red,
372+
"Doc generation failed due to warnings while using the --warnings-as-errors option"
373+
])
374+
375+
exit({:shutdown, ExDoc.WarningCounter.count()})
376+
else
377+
index
378+
end
367379
end
368380
end
369381

test/ex_doc/cli_test.exs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,41 @@ defmodule ExDoc.CLITest do
3535
assert io == "ExDoc v#{ExDoc.version()}\n"
3636
end
3737

38+
describe "--warnings-as-errors" do
39+
test "exits with code 0 when no warnings" do
40+
{[html, epub], _io} = run(["ExDoc", "1.2.3", @ebin, "--warnings-as-errors"])
41+
42+
assert html ==
43+
{"ExDoc", "1.2.3",
44+
[formatter: "html", apps: [:ex_doc], source_beam: @ebin, warnings_as_errors: true]}
45+
46+
assert epub ==
47+
{"ExDoc", "1.2.3",
48+
[formatter: "epub", apps: [:ex_doc], source_beam: @ebin, warnings_as_errors: true]}
49+
end
50+
51+
test "exits with 1 with warnings" do
52+
ExDoc.WarningCounter.increment()
53+
54+
fun = fn ->
55+
io =
56+
capture_io(:stderr, fn ->
57+
{[{"ExDoc", "1.2.3", opts}, _], _io} =
58+
run(["ExDoc", "1.2.3", @ebin, "--warnings-as-errors"])
59+
60+
assert Keyword.get(opts, :warnings_as_errors)
61+
end)
62+
63+
assert io =~
64+
"Doc generation failed due to warnings while using the --warnings-as-errors option\n"
65+
end
66+
67+
assert catch_exit(fun.()) == {:shutdown, 1}
68+
after
69+
ExDoc.WarningCounter.reset()
70+
end
71+
end
72+
3873
test "too many arguments" do
3974
assert catch_exit(run(["ExDoc", "1.2.3", "/", "kaboom"])) == {:shutdown, 1}
4075
end

test/mix/tasks/docs_test.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,8 @@ defmodule Mix.Tasks.DocsTest do
181181
] = run([], app: :umbrella, apps_path: "apps/", docs: [ignore_apps: [:foo]])
182182
end)
183183
end
184+
185+
test "accepts warnings_as_errors in :warnings_as_errors" do
186+
assert catch_exit(run([], app: :ex_doc, docs: [warnings_as_errors: true])) == {:shutdown, 1}
187+
end
184188
end

0 commit comments

Comments
 (0)