zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

get_git_diff.ex (7350B)


      1 defmodule Credo.CLI.Command.Diff.Task.GetGitDiff do
      2   use Credo.Execution.Task
      3 
      4   alias Credo.CLI.Command.Diff.DiffCommand
      5   alias Credo.CLI.Output.Shell
      6   alias Credo.CLI.Output.UI
      7 
      8   def call(exec, _opts) do
      9     case Execution.get_assign(exec, "credo.diff.previous_exec") do
     10       %Execution{} -> exec
     11       _ -> run_credo_and_store_resulting_execution(exec)
     12     end
     13   end
     14 
     15   def error(exec, _opts) do
     16     exec
     17     |> Execution.get_halt_message()
     18     |> puts_error_message()
     19 
     20     exec
     21   end
     22 
     23   defp puts_error_message(halt_message) do
     24     UI.warn([:red, "** (diff) ", halt_message])
     25     UI.warn("")
     26   end
     27 
     28   defp run_credo_and_store_resulting_execution(exec) do
     29     case DiffCommand.previous_ref(exec) do
     30       {:git, git_ref} ->
     31         run_credo_on_git_ref(exec, git_ref, {:git, git_ref})
     32 
     33       {:git_merge_base, git_merge_base} ->
     34         run_credo_on_git_merge_base(exec, git_merge_base, {:git_merge_base, git_merge_base})
     35 
     36       {:git_datetime, datetime} ->
     37         run_credo_on_datetime(exec, datetime, {:git_datetime, datetime})
     38 
     39       {:path, path} ->
     40         run_credo_on_path_ref(exec, path, {:path, path})
     41 
     42       {:error, error} ->
     43         Execution.halt(exec, error)
     44     end
     45   end
     46 
     47   defp run_credo_on_git_ref(exec, git_ref, given_ref) do
     48     working_dir = Execution.working_dir(exec)
     49     previous_dirname = run_git_clone_and_checkout(working_dir, git_ref)
     50 
     51     run_credo_on_dir(exec, previous_dirname, git_ref, given_ref)
     52   end
     53 
     54   defp run_credo_on_git_merge_base(exec, git_merge_base, given_ref) do
     55     # git merge-base master HEAD
     56     case System.cmd("git", ["merge-base", git_merge_base, "HEAD"], stderr_to_stdout: true) do
     57       {output, 0} ->
     58         git_ref = String.trim(output)
     59         working_dir = Execution.working_dir(exec)
     60         previous_dirname = run_git_clone_and_checkout(working_dir, git_ref)
     61 
     62         run_credo_on_dir(exec, previous_dirname, git_ref, given_ref)
     63 
     64       {output, _} ->
     65         Execution.halt(
     66           exec,
     67           "Could not determine merge base for `#{git_merge_base}`: #{inspect(output)}"
     68         )
     69     end
     70   end
     71 
     72   defp run_credo_on_datetime(exec, datetime, given_ref) do
     73     git_ref =
     74       case get_git_ref_for_datetime(datetime) do
     75         nil -> "HEAD"
     76         git_ref -> git_ref
     77       end
     78 
     79     working_dir = Execution.working_dir(exec)
     80     previous_dirname = run_git_clone_and_checkout(working_dir, git_ref)
     81 
     82     run_credo_on_dir(exec, previous_dirname, git_ref, given_ref)
     83   end
     84 
     85   defp get_git_ref_for_datetime(datetime) do
     86     case System.cmd("git", ["rev-list", "--reverse", "--after", datetime, "HEAD"]) do
     87       {"", 0} ->
     88         nil
     89 
     90       {output, 0} ->
     91         output
     92         |> String.split(~r/\n/)
     93         |> List.first()
     94 
     95       _ ->
     96         nil
     97     end
     98   end
     99 
    100   defp run_credo_on_path_ref(exec, path, given_ref) do
    101     run_credo_on_dir(exec, path, path, given_ref)
    102   end
    103 
    104   defp run_credo_on_dir(exec, dirname, previous_git_ref, given_ref) do
    105     {previous_argv, _last_arg} =
    106       exec.argv
    107       |> Enum.slice(1..-1)
    108       |> Enum.reduce({[], nil}, fn
    109         _, {argv, "--working-dir"} -> {Enum.slice(argv, 1..-2), nil}
    110         _, {argv, "--from-git-merge-base"} -> {Enum.slice(argv, 1..-2), nil}
    111         _, {argv, "--from-git-ref"} -> {Enum.slice(argv, 1..-2), nil}
    112         _, {argv, "--from-dir"} -> {Enum.slice(argv, 1..-2), nil}
    113         _, {argv, "--since"} -> {Enum.slice(argv, 1..-2), nil}
    114         "--show-fixed", {argv, _last_arg} -> {argv, nil}
    115         "--show-kept", {argv, _last_arg} -> {argv, nil}
    116         ^previous_git_ref, {argv, _last_arg} -> {argv, nil}
    117         arg, {argv, _last_arg} -> {argv ++ [arg], arg}
    118       end)
    119 
    120     run_credo(exec, previous_git_ref, dirname, previous_argv, given_ref)
    121   end
    122 
    123   defp run_credo(exec, previous_git_ref, previous_dirname, previous_argv, given_ref) do
    124     parent_pid = self()
    125 
    126     spawn(fn ->
    127       Shell.suppress_output(fn ->
    128         argv = previous_argv ++ ["--working-dir", previous_dirname]
    129 
    130         previous_exec = Credo.run(argv)
    131 
    132         send(parent_pid, {:previous_exec, previous_exec})
    133       end)
    134     end)
    135 
    136     receive do
    137       {:previous_exec, previous_exec} ->
    138         store_resulting_execution(
    139           exec,
    140           previous_git_ref,
    141           previous_dirname,
    142           previous_exec,
    143           given_ref
    144         )
    145     end
    146   end
    147 
    148   def store_resulting_execution(
    149         %Execution{debug: true} = exec,
    150         previous_git_ref,
    151         previous_dirname,
    152         previous_exec,
    153         given_ref
    154       ) do
    155     exec =
    156       perform_store_resulting_execution(
    157         exec,
    158         previous_git_ref,
    159         previous_dirname,
    160         previous_exec,
    161         given_ref
    162       )
    163 
    164     previous_dirname = Execution.get_assign(exec, "credo.diff.previous_dirname")
    165     require Logger
    166     Logger.debug("Git ref checked out to: #{previous_dirname}")
    167 
    168     exec
    169   end
    170 
    171   def store_resulting_execution(
    172         exec,
    173         previous_git_ref,
    174         previous_dirname,
    175         previous_exec,
    176         given_ref
    177       ) do
    178     perform_store_resulting_execution(
    179       exec,
    180       previous_git_ref,
    181       previous_dirname,
    182       previous_exec,
    183       given_ref
    184     )
    185   end
    186 
    187   defp perform_store_resulting_execution(
    188          exec,
    189          previous_git_ref,
    190          previous_dirname,
    191          previous_exec,
    192          given_ref
    193        ) do
    194     if previous_exec.halted do
    195       halt_execution(exec, previous_git_ref, previous_dirname, previous_exec)
    196     else
    197       exec
    198       |> Execution.put_assign("credo.diff.given_ref", given_ref)
    199       |> Execution.put_assign("credo.diff.previous_git_ref", previous_git_ref)
    200       |> Execution.put_assign("credo.diff.previous_dirname", previous_dirname)
    201       |> Execution.put_assign("credo.diff.previous_exec", previous_exec)
    202     end
    203   end
    204 
    205   defp halt_execution(exec, previous_git_ref, previous_dirname, previous_exec) do
    206     message =
    207       case Execution.get_halt_message(previous_exec) do
    208         {:config_name_not_found, message} -> message
    209         halt_message -> inspect(halt_message)
    210       end
    211 
    212     Execution.halt(
    213       exec,
    214       [
    215         :bright,
    216         "Running Credo on `#{previous_git_ref}` (checked out to #{previous_dirname}) resulted in the following error:\n\n",
    217         :faint,
    218         message
    219       ]
    220     )
    221   end
    222 
    223   defp run_git_clone_and_checkout(working_dir, git_ref) do
    224     now = DateTime.utc_now() |> to_string |> String.replace(~r/\D/, "")
    225     tmp_clone_dir = Path.join(System.tmp_dir!(), "credo-diff-#{now}")
    226     git_root_path = git_root_path(working_dir)
    227     current_dir = working_dir
    228     tmp_working_dir = tmp_working_dir(tmp_clone_dir, git_root_path, current_dir)
    229 
    230     {_output, 0} =
    231       System.cmd("git", ["clone", git_root_path, tmp_clone_dir],
    232         cd: working_dir,
    233         stderr_to_stdout: true
    234       )
    235 
    236     {_output, 0} =
    237       System.cmd("git", ["checkout", git_ref], cd: tmp_clone_dir, stderr_to_stdout: true)
    238 
    239     tmp_working_dir
    240   end
    241 
    242   defp git_root_path(path) do
    243     {output, 0} =
    244       System.cmd("git", ["rev-parse", "--show-toplevel"], cd: path, stderr_to_stdout: true)
    245 
    246     String.trim(output)
    247   end
    248 
    249   defp tmp_working_dir(tmp_clone_dir, git_root_is_current_dir, git_root_is_current_dir) do
    250     tmp_clone_dir
    251   end
    252 
    253   defp tmp_working_dir(tmp_clone_dir, git_root_path, current_dir) do
    254     subdir_to_run_credo_in = Path.relative_to(current_dir, git_root_path)
    255 
    256     Path.join(tmp_clone_dir, subdir_to_run_credo_in)
    257   end
    258 end