Skip to content

Instantly share code, notes, and snippets.

@wisq
Created October 4, 2017 05:24
Show Gist options
  • Save wisq/58d9d07d5ee2dfe54d6141d21ad87578 to your computer and use it in GitHub Desktop.
Save wisq/58d9d07d5ee2dfe54d6141d21ad87578 to your computer and use it in GitHub Desktop.
Quick and dirty Elixir git pre-commit hook that ensures tests pass
#!/bin/sh
exec 1>&2
MIX_ENV=test exec mix git.test
defmodule Mix.Tasks.Git.Test do
use Mix.Task
def run([]), do: git_test(Mix.env)
def run(_) do
Mix.raise "git.test does not accept arguments"
end
def git_test(:test) do
cwd = File.cwd!
tmpdir = create_tmpdir()
status "Getting local changes ..."
diff_file = Path.join(tmpdir, "apply.patch")
quiet_cmd("sh", ["-c", "git diff --cached --binary > \"#{diff_file}\""])
status "Cloning tree ..."
tree = Path.join(tmpdir, "tree")
quiet_cmd("git", ["clone", cwd, tree])
if (size = File.stat!(diff_file).size) > 0 do
status "Applying changes (#{size} bytes) ..."
quiet_cmd("git", ["apply", "../apply.patch"], cd: tree)
end
deps = ["deps" | dependency_build_dirs()]
status "Linking #{Enum.count(deps)} dependencies ..."
deps |> Enum.each(&symlink(&1, cwd, tree))
status "Running tests ..."
System.cmd("mix", ["test", "--color"], cd: tree, into: IO.stream(:stdio, :line))
|> check_cmd_result("mix test")
status "All tests passed."
end
def git_test(_) do
Mix.raise "Must be run with MIX_ENV=test"
end
defp status(text) do
IO.puts([IO.ANSI.light_green, "* ", text, IO.ANSI.reset])
end
defp create_tmpdir do
{:ok, _started} = Application.ensure_all_started(:briefly)
{:ok, tmpdir} = Briefly.create(directory: true)
tmpdir
end
defp symlink(item, from, to) do
source = Path.join(from, item)
target = Path.join(to, item)
Path.dirname(target) |> File.mkdir_p!
File.ln_s!(source, target)
end
defp dependency_build_dirs do
lib = Path.join(Mix.Project.build_path(), "lib")
lib
|> File.ls!
|> Enum.map(&Path.join(lib, &1))
|> Enum.filter(&File.dir?/1)
|> List.delete(Mix.Project.app_path())
|> Enum.map(&Path.relative_to_cwd/1)
end
defp quiet_cmd(bin, args, opts \\ []) do
opts = Keyword.merge([stderr_to_stdout: true], opts)
System.cmd(bin, args, opts)
|> check_cmd_result([bin | args] |> Enum.join(" "))
end
defp check_cmd_result({_output, 0}, _cmd), do: :ok
defp check_cmd_result({output, code}, cmd) do
IO.puts([
IO.ANSI.light_red,
"---OUTPUT---\n",
output |> String.trim,
"\n---OUTPUT---",
IO.ANSI.reset,
])
Mix.raise "Command #{inspect(cmd)} exited with status #{code}"
end
end
@wisq
Copy link
Author

wisq commented Oct 4, 2017

Needs briefly in your mix.exs dependencies. Can be limited to the test environment if you like:

      {:briefly, "~> 0.3", only: [:test]},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment