Transform into GitLab Push Triggerer

Takes in GitLab push webhook events instead of normal triggers
This commit is contained in:
Mikko Ahlroth 2018-02-25 11:25:07 +02:00
parent ac1eb372a4
commit 04f0bbb05b
5 changed files with 64 additions and 31 deletions

View file

@ -1,8 +1,8 @@
# GitLabPushTriggerer
GitLabPushTriggerer is a small application that triggers execution of scripts when given endpoints are hit
with GET requests. It contains no permissions handling, no authentication, no request limiting. So
these must be handled in some other way, like with an Nginx reverse proxy.
GitLabPushTriggerer is a small application that triggers execution of scripts when given endpoints
are hit with GitLab push webhooks. It contains no permissions handling, no authentication, no
request limiting. So these must be handled in some other way, like with an Nginx reverse proxy.
Should work on any \*nix system that has `sh`.
@ -21,12 +21,17 @@ Configuration of endpoints and scripts is done via environment variables. Enviro
are set in the following format:
```
TRIGGERER_FOO=/path/to/bar.sh
GLPT_FOO='master;/path/to/bar.sh'
```
The variable consists of `TRIGGERER_` prefix, after which is the name of the endpoint. The value is
the path to the script to execute. The above variable would result in an endpoint `/foo`, which would
execute `/path/to/bar.sh`.
The variable consists of `GLPT_` prefix, after which is the name of the endpoint. The value is
the branch to match and path to the script to execute. The above variable would result in
an endpoint `/foo`, which would execute `/path/to/bar.sh` if a push to branch `master` was
called on the endpoint.
**NOTE:** Add the single quotes `'` to encase the value to prevent the shell messing up.
**LIMITATION:** The branch name cannot contain `;`.
The endpoint path is transformed to lowercase and underscores are replaced with dashes. See the
following examples:
@ -36,10 +41,13 @@ following examples:
* `TRIGGERER_AAA_``/aaa-`
The path to the script can be absolute or relative, as long as it can be reached from the current
working dir.
working dir (for releases that can be the release dir; it is easier to use absolute paths). Scripts
are executed with `sh -c script`.
## Return value
If the script was triggered, 200 OK is returned. If the script is not found or the request path is
wrong, 404 File not found is returned. If there is an error in script execution, 500 Internal server
error is returned and the error is logged.
* If the script was triggered, `200 OK` is returned.
* If the script is not found or the request path is wrong, `404 File not found` is returned.
* If the branch does not match, `400 Bad request` is returned.
* If there is an error in script execution, `500 Internal server error` is returned and the error
is logged.

View file

@ -9,6 +9,14 @@ defmodule GitLabPushTriggerer.Router do
plug(Plug.Logger)
plug(:match)
plug(
Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Jason
)
plug(:dispatch)
paths =
@ -16,23 +24,25 @@ defmodule GitLabPushTriggerer.Router do
GitLabPushTriggerer.get_envs()
end
match "/:path" do
case Map.get(unquote(paths), path) do
post "/:path" do
with {branch, script} <- Map.get(unquote(paths), path),
:ok <- check_branch(branch, conn.body_params),
:ok <- GitLabPushTriggerer.run(script) do
send_resp(conn, 200, "OK")
else
# Nil returned by Map.get
nil ->
route_not_found(conn)
script ->
case GitLabPushTriggerer.run(script) do
:ok ->
send_resp(conn, 200, "OK")
{:error, :file_not_found} ->
send_resp(conn, 404, "File not found.")
{:error, :file_not_found} ->
send_resp(conn, 404, "File not found.")
{:error, :wrong_branch} ->
send_resp(conn, 400, "Branch not matching.")
error ->
Logger.error("Unknown error: #{inspect(error)}")
send_resp(conn, 500, "Unknown error.")
end
error ->
Logger.error("Unknown error: #{inspect(error)}")
send_resp(conn, 500, "Unknown error.")
end
end
@ -46,4 +56,14 @@ defmodule GitLabPushTriggerer.Router do
Logger.error(inspect(data))
send_resp(conn, conn.status, "Something went wrong.")
end
# Check that branch in data matches branch in configuration
defp check_branch(branch, data) when is_binary(branch) and is_map(data) do
with "refs/heads/" <> data_branch <- Map.get(data, "ref"),
^branch <- data_branch do
:ok
else
_ -> {:error, :wrong_branch}
end
end
end

View file

@ -1,6 +1,8 @@
defmodule GitLabPushTriggerer do
require Logger
@split_char ";"
@doc """
Get all matching environment variables and their values. Returns a map where keys are the transformed
paths and values are the given script paths.
@ -9,14 +11,14 @@ defmodule GitLabPushTriggerer do
In these examples, sample env is given as argument. If no argument is given, system env is used.
iex> GitLabPushTriggerer.get_envs(%{"TRIGGERER_FOO" => "./test.sh"})
%{"foo" => "./test.sh"}
iex> GitLabPushTriggerer.get_envs(%{"GLPT_FOO" => "master;./test.sh"})
%{"foo" => {"master", "./test.sh"}}
iex> GitLabPushTriggerer.get_envs(%{})
%{}
iex> GitLabPushTriggerer.get_envs(%{"WRONG_FORMAT" => "no", "TRIGGERER_BAR_" => "baz", "TRIGGERER_" => "no", "TRIGGERER_GO_WILD" => "/bin/true"})
%{"bar-" => "baz", "go-wild" => "/bin/true"}
iex> GitLabPushTriggerer.get_envs(%{"WRONG_FORMAT" => "no", "GLPT_BAR_" => "um;baz", "GLPT_" => "no", "GLPT_GO_WILD" => "jeffum;/bin/true"})
%{"bar-" => {"um", "baz"}, "go-wild" => {"jeffum", "/bin/true"}}
"""
@spec get_envs(%{optional(String.t()) => String.t()}) :: map
def get_envs(envs \\ nil) do
@ -25,9 +27,10 @@ defmodule GitLabPushTriggerer do
envs
|> Map.keys()
|> Enum.reduce(%{}, fn
"TRIGGERER_" <> rest = key, acc when rest != "" ->
"GLPT_" <> rest = key, acc when rest != "" ->
path = transform_path(rest)
Map.put(acc, path, envs[key])
[branch | rest] = String.split(envs[key], @split_char)
Map.put(acc, path, {branch, Enum.join(rest, @split_char)})
_, acc ->
acc

View file

@ -3,7 +3,7 @@ defmodule GitLabPushTriggerer.MixProject do
def project do
[
app: :triggerer,
app: :gitlab_push_triggerer,
version: "1.0.0",
elixir: "~> 1.6",
start_permanent: Mix.env() == :prod,
@ -24,7 +24,8 @@ defmodule GitLabPushTriggerer.MixProject do
[
{:plug, "~> 1.4.5"},
{:distillery, "~> 1.5"},
{:cowboy, "~> 1.1"}
{:cowboy, "~> 1.1"},
{:jason, "~> 1.0.0"}
]
end
end

View file

@ -2,6 +2,7 @@
"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"},
"distillery": {:hex, :distillery, "1.5.2", "eec18b2d37b55b0bcb670cf2bcf64228ed38ce8b046bb30a9b636a6f5a4c0080", [:mix], [], "hexpm"},
"jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"mime": {:hex, :mime, "1.2.0", "78adaa84832b3680de06f88f0997e3ead3b451a440d183d688085be2d709b534", [:mix], [], "hexpm"},
"plug": {:hex, :plug, "1.4.5", "7b13869283fff6b8b21b84b8735326cc012c5eef8607095dc6ee24bd0a273d8e", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"},