Git status for Elixir builds
This is a quick tale of debugging a production issue and a nice solution discovered along the way. Whilst investigating the bug it was not clear what version of the code was running. What was needed was a quick way to associate running code with a git commit.
Fetching a Git commit
A quick search uncovered this stack overflow answer. With a bit of work to handle the error case we ended up with this code to get the current commit hash.
def commit() do
case System.cmd("git", ["rev-parse", "HEAD"]) do
{hash, 0} ->
String.trim(hash)
_ ->
"UNKNOWN"
end
end
Compile time vs Run time
This code will get the commit information from the system when it is run. What was needed was the commit information from when the code was compiled.
Using exactly the same code but this time outside a function will fetch the git commit at compile time. This value can then be exposed in a function that references the compile time value.
@commit (case System.cmd("git", ["rev-parse", "HEAD"]) do
{hash, 0} ->
String.trim(hash)
_ ->
Logger.warn("Could not read git commit hash")
"UNKNOWN"
end)
def commit(), do: @commit
GitStatus
The solution is wrapped up in a compact hex package git_status.
Clean code only, please
The project also defines is_clean?
to check if the git project had any uncommited content at compilation.
This can be used to create a guard test for CI ensuring that a build is always associated with the code in a commit.
@tag :ci
test "is clean" do
assert GitStatus.is_clean?()
end
If practising TDD locally you can add the :ci
tag to excluded tests.
Raxx integration
Our project is a web API that already has endpoints under /sys
with metadata on the service.
All that was required was a simple web action for a /sys/source
endpoint.
defmodule MyApp.API.SourceStatus do
use Raxx.Server
@response response(:ok)
|> set_header("content-type", "application/json")
|> set_body(Poison.encode!(%{
commit: GitStatus.commit(),
clean: GitStatus.is_clean?()
})
def handle_request(
%{method: :GET, path: ["sys", "source"]},
_state)
do
@response
end
end