Packages
credo
1.7.2-rc.2
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.7-rc.0
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.2-rc.4
1.7.2-rc.3
1.7.2-rc.2
1.7.2-rc.1
1.7.2-rc.0
1.7.1
1.7.0
1.7.0-rc.2
1.7.0-rc.1
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.5
1.5.0-rc.4
1.5.0-rc.3
1.5.0-rc.2
1.5.0-rc.1
1.4.1
1.4.0
1.4.0-rc.2
1.4.0-rc.1
1.3.2
1.3.1
1.3.0
1.3.0-rc3
1.3.0-rc2
1.3.0-rc1
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc4
1.2.0-rc3
1.2.0-rc2
1.2.0-rc1
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.1.0-rc3
1.1.0-rc2
1.1.0-rc1
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.1-rc1
1.0.0
1.0.0-rc1
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.9.0-rc8
0.9.0-rc7
0.9.0-rc6
0.9.0-rc5
0.9.0-rc4
0.9.0-rc3
0.9.0-rc2
0.9.0-rc1
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.8.0-rc7
0.8.0-rc6
0.8.0-rc5
0.8.0-rc4
0.8.0-rc3
0.8.0-rc2
0.8.0-rc1
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.6.0-rc2
0.6.0-rc1
0.5.3
0.5.2
0.5.1
0.5.0
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.10-dev
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.4.0-beta5
0.4.0-beta4
0.4.0-beta3
0.4.0-beta2
0.4.0-beta1
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.3.0-dev2
0.3.0-dev
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
0.0.1-dev
A static code analysis tool with a focus on code consistency and teaching.
Current section
Files
Jump to
Current section
Files
lib/credo/cli/command/diff/task/get_git_diff.ex
defmodule Credo.CLI.Command.Diff.Task.GetGitDiff do
use Credo.Execution.Task
alias Credo.CLI.Command.Diff.DiffCommand
alias Credo.CLI.Output.Shell
alias Credo.CLI.Output.UI
def call(exec, _opts) do
case Execution.get_assign(exec, "credo.diff.previous_exec") do
%Execution{} -> exec
_ -> run_credo_and_store_resulting_execution(exec)
end
end
def error(exec, _opts) do
exec
|> Execution.get_halt_message()
|> puts_error_message()
exec
end
defp puts_error_message(halt_message) do
UI.warn([:red, "** (diff) ", halt_message])
UI.warn("")
end
defp run_credo_and_store_resulting_execution(exec) do
case DiffCommand.previous_ref(exec) do
{:git, git_ref} ->
run_credo_on_git_ref(exec, git_ref, {:git, git_ref})
{:git_merge_base, git_merge_base} ->
run_credo_on_git_merge_base(exec, git_merge_base, {:git_merge_base, git_merge_base})
{:git_datetime, datetime} ->
run_credo_on_datetime(exec, datetime, {:git_datetime, datetime})
{:path, path} ->
run_credo_on_path_ref(exec, path, {:path, path})
{:error, error} ->
Execution.halt(exec, error)
end
end
defp run_credo_on_git_ref(exec, git_ref, given_ref) do
working_dir = Execution.working_dir(exec)
previous_dirname = run_git_clone_and_checkout(working_dir, git_ref)
run_credo_on_dir(exec, previous_dirname, git_ref, given_ref)
end
defp run_credo_on_git_merge_base(exec, git_merge_base, given_ref) do
# git merge-base master HEAD
case System.cmd("git", ["merge-base", git_merge_base, "HEAD"], stderr_to_stdout: true) do
{output, 0} ->
git_ref = String.trim(output)
working_dir = Execution.working_dir(exec)
previous_dirname = run_git_clone_and_checkout(working_dir, git_ref)
run_credo_on_dir(exec, previous_dirname, git_ref, given_ref)
{output, _} ->
Execution.halt(
exec,
"Could not determine merge base for `#{git_merge_base}`: #{inspect(output)}"
)
end
end
defp run_credo_on_datetime(exec, datetime, given_ref) do
git_ref =
case get_git_ref_for_datetime(datetime) do
nil -> "HEAD"
git_ref -> git_ref
end
working_dir = Execution.working_dir(exec)
previous_dirname = run_git_clone_and_checkout(working_dir, git_ref)
run_credo_on_dir(exec, previous_dirname, git_ref, given_ref)
end
defp get_git_ref_for_datetime(datetime) do
case System.cmd("git", ["rev-list", "--reverse", "--after", datetime, "HEAD"]) do
{"", 0} ->
nil
{output, 0} ->
output
|> String.split(~r/\n/)
|> List.first()
_ ->
nil
end
end
defp run_credo_on_path_ref(exec, path, given_ref) do
run_credo_on_dir(exec, path, path, given_ref)
end
defp run_credo_on_dir(exec, dirname, previous_git_ref, given_ref) do
{previous_argv, _last_arg} =
exec.argv
|> Credo.Backports.Enum.slice(1..-1)
|> Enum.reduce({[], nil}, fn
_, {argv, "--working-dir"} -> {Credo.Backports.Enum.slice(argv, 1..-2), nil}
_, {argv, "--from-git-merge-base"} -> {Credo.Backports.Enum.slice(argv, 1..-2), nil}
_, {argv, "--from-git-ref"} -> {Credo.Backports.Enum.slice(argv, 1..-2), nil}
_, {argv, "--from-dir"} -> {Credo.Backports.Enum.slice(argv, 1..-2), nil}
_, {argv, "--since"} -> {Credo.Backports.Enum.slice(argv, 1..-2), nil}
"--show-fixed", {argv, _last_arg} -> {argv, nil}
"--show-kept", {argv, _last_arg} -> {argv, nil}
^previous_git_ref, {argv, _last_arg} -> {argv, nil}
arg, {argv, _last_arg} -> {argv ++ [arg], arg}
end)
run_credo(exec, previous_git_ref, dirname, previous_argv, given_ref)
end
defp run_credo(exec, previous_git_ref, previous_dirname, previous_argv, given_ref) do
parent_pid = self()
spawn(fn ->
Shell.suppress_output(fn ->
argv = previous_argv ++ ["--working-dir", previous_dirname]
previous_exec = Credo.run(argv)
send(parent_pid, {:previous_exec, previous_exec})
end)
end)
receive do
{:previous_exec, previous_exec} ->
store_resulting_execution(
exec,
previous_git_ref,
previous_dirname,
previous_exec,
given_ref
)
end
end
def store_resulting_execution(
%Execution{debug: true} = exec,
previous_git_ref,
previous_dirname,
previous_exec,
given_ref
) do
exec =
perform_store_resulting_execution(
exec,
previous_git_ref,
previous_dirname,
previous_exec,
given_ref
)
previous_dirname = Execution.get_assign(exec, "credo.diff.previous_dirname")
require Logger
Logger.debug("Git ref checked out to: #{previous_dirname}")
exec
end
def store_resulting_execution(
exec,
previous_git_ref,
previous_dirname,
previous_exec,
given_ref
) do
perform_store_resulting_execution(
exec,
previous_git_ref,
previous_dirname,
previous_exec,
given_ref
)
end
defp perform_store_resulting_execution(
exec,
previous_git_ref,
previous_dirname,
previous_exec,
given_ref
) do
if previous_exec.halted do
halt_execution(exec, previous_git_ref, previous_dirname, previous_exec)
else
exec
|> Execution.put_assign("credo.diff.given_ref", given_ref)
|> Execution.put_assign("credo.diff.previous_git_ref", previous_git_ref)
|> Execution.put_assign("credo.diff.previous_dirname", previous_dirname)
|> Execution.put_assign("credo.diff.previous_exec", previous_exec)
end
end
defp halt_execution(exec, previous_git_ref, previous_dirname, previous_exec) do
message =
case Execution.get_halt_message(previous_exec) do
{:config_name_not_found, message} -> message
halt_message -> inspect(halt_message)
end
Execution.halt(
exec,
[
:bright,
"Running Credo on `#{previous_git_ref}` (checked out to #{previous_dirname}) resulted in the following error:\n\n",
:faint,
message
]
)
end
defp run_git_clone_and_checkout(working_dir, git_ref) do
now = DateTime.utc_now() |> to_string |> String.replace(~r/\D/, "")
tmp_clone_dir = Path.join(System.tmp_dir!(), "credo-diff-#{now}")
git_root_path = git_root_path(working_dir)
current_dir = working_dir
tmp_working_dir = tmp_working_dir(tmp_clone_dir, git_root_path, current_dir)
{_output, 0} =
System.cmd("git", ["clone", git_root_path, tmp_clone_dir],
cd: working_dir,
stderr_to_stdout: true
)
{_output, 0} =
System.cmd("git", ["checkout", git_ref], cd: tmp_clone_dir, stderr_to_stdout: true)
tmp_working_dir
end
defp git_root_path(path) do
{output, 0} =
System.cmd("git", ["rev-parse", "--show-toplevel"], cd: path, stderr_to_stdout: true)
String.trim(output)
end
defp tmp_working_dir(tmp_clone_dir, git_root_is_current_dir, git_root_is_current_dir) do
tmp_clone_dir
end
defp tmp_working_dir(tmp_clone_dir, git_root_path, current_dir) do
subdir_to_run_credo_in = Path.relative_to(current_dir, git_root_path)
Path.join(tmp_clone_dir, subdir_to_run_credo_in)
end
end