-
Notifications
You must be signed in to change notification settings - Fork 1
/
check.ex
103 lines (87 loc) · 2.99 KB
/
check.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
defmodule Medic.Check do
@moduledoc "Reusable check functions"
alias Medic.UI
@typedoc """
Valid return values from a check.
* `:ok` - The check succeeded with no problems.
* `:skipped` - Doctor checks for files in `.medic/skipped/` to skip a check. Custom
checks could return this to notify Doctor that they chose internally to skip the check.
* `{:warn, output}` - The check generated warnings, but does not stop Doctor from proceeding.
* `{:error, output, remedy}` - The check failed. Output may be `stdout` and/or `stderr` generated
from shell commands, or custom error output to show to the user. The `remedy` will by copied
into the local paste buffer.
"""
@type check_return_t() ::
:ok
| :skipped
| {:warn, output :: binary}
| {:error, output :: binary, remedy :: binary}
@doc false
def run({module, meta_function}),
do: run({module, meta_function, []})
@doc false
def run({module, function, args}) do
UI.item(
module |> Module.split() |> List.last(),
function |> to_string() |> String.replace("_", " "),
args
)
if skipped?(module, function, args) do
:skipped
else
apply(module, function, wrap(args))
end
end
@doc """
Usable within a check. If the command exits with a 0 status code, then `:ok`, is returned.
If the command returns a non-zero status code, then `{:error, output, remedy}` is returned,
where output is any text generated by the command.
"""
@spec command_succeeds?(binary(), list(binary()), remedy: binary()) :: :ok | {:error, binary(), binary()}
def command_succeeds?(command, args, remedy: remedy) do
case System.cmd(command, args) do
{_output, 0} -> :ok
{output, _} -> {:error, output, remedy}
end
end
def in_list?(item, list, remedy: remedy),
do: if(item in list, do: :ok, else: {:error, "“#{item}” not found in #{inspect(list)}", remedy})
def skipped?(module, function, args),
do:
{module, function, args}
|> skip_file()
|> File.exists?()
def skip_file({module, function}), do: skip_file({module, function, []})
def skip_file({module, function, args}) do
arg_list =
if Keyword.keyword?(args) do
args |> Keyword.values() |> Enum.join("+")
else
args |> Enum.map_join("+", &args_safe/1)
end
filename =
[module, function, arg_list]
|> Enum.filter(fn
"" -> false
_ -> true
end)
|> Enum.join("-")
|> String.replace(~r{[^\w\-_\+\.]+}, "")
Path.join(".medic/skipped", filename)
end
defp wrap(args) do
if Keyword.keyword?(args) and args != [] do
[args]
else
args
end
end
defp args_safe(value) when is_binary(value), do: value
defp args_safe(value) when is_atom(value), do: value
defp args_safe({_key, value}), do: args_safe(value)
defp args_safe(values) when is_list(values) do
if Keyword.keyword?(values),
do: Keyword.values(values),
else: Enum.join(values, "-")
end
end