mirror of
https://github.com/adnanh/webhook.git
synced 2026-07-27 01:29:16 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c51971fd37 | |||
| 93e5fe7712 | |||
| 058f820cbd | |||
| ddb1f2441a | |||
| 8226d5e50f | |||
| e149c99724 | |||
| 6ea2d68b44 | |||
| 5789362186 | |||
| 07f166616c | |||
| b66216675a | |||
| ecbcf11153 | |||
| 7d525cf317 | |||
| e83d7029ff | |||
| 36c2c692d6 | |||
| 86cef3e421 | |||
| 75cf8952be | |||
| c53596df59 | |||
| cc0d9b2cba | |||
| c6530b17e7 | |||
| 1943c5311f | |||
| 3b59539a33 | |||
| 923b0c6daa | |||
| 8530255ae6 |
@@ -0,0 +1,29 @@
|
|||||||
|
OS = darwin freebsd linux openbsd windows
|
||||||
|
ARCHS = 386 arm amd64 arm64
|
||||||
|
|
||||||
|
all: build release
|
||||||
|
|
||||||
|
build: deps
|
||||||
|
go build
|
||||||
|
|
||||||
|
release: clean deps
|
||||||
|
@for arch in $(ARCHS);\
|
||||||
|
do \
|
||||||
|
for os in $(OS);\
|
||||||
|
do \
|
||||||
|
echo "Building $$os-$$arch"; \
|
||||||
|
mkdir -p build/webhook-$$os-$$arch/; \
|
||||||
|
GOOS=$$os GOARCH=$$arch go build -o build/webhook-$$os-$$arch/webhook; \
|
||||||
|
tar cz -C build -f build/webhook-$$os-$$arch.tar.gz webhook-$$os-$$arch; \
|
||||||
|
done \
|
||||||
|
done
|
||||||
|
|
||||||
|
test: deps
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
deps:
|
||||||
|
go get -d -v -t ./...
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
|
rm -f webhook
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
[](https://ghit.me/repo/adnanh/webhook)[](https://gitter.im/adnanh/webhook?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://flattr.com/submit/auto?user_id=adnanh&url=https%3A%2F%2Fwww.github.com%2Fadnanh%2Fwebhook)
|
[](https://ghit.me/repo/adnanh/webhook) [](https://gitter.im/adnanh/webhook?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://flattr.com/submit/auto?user_id=adnanh&url=https%3A%2F%2Fwww.github.com%2Fadnanh%2Fwebhook) [Donate via PayPal](https://paypal.me/hookdoo) | [Patreon page](https://www.patreon.com/webhook)
|
||||||
|
|
||||||
# What is webhook?
|
# What is webhook?
|
||||||
[webhook](https://github.com/adnanh/webhook/) is a lightweight configurable tool written in Go, that allows you to easily create HTTP endpoints (hooks) on your server, which you can use to execute configured commands. You can also pass data from the HTTP request (such as headers, payload or query variables) to your commands. [webhook](https://github.com/adnanh/webhook/) also allows you to specify rules which have to be satisfied in order for the hook to be triggered.
|
[webhook](https://github.com/adnanh/webhook/) is a lightweight configurable tool written in Go, that allows you to easily create HTTP endpoints (hooks) on your server, which you can use to execute configured commands. You can also pass data from the HTTP request (such as headers, payload or query variables) to your commands. [webhook](https://github.com/adnanh/webhook/) also allows you to specify rules which have to be satisfied in order for the hook to be triggered.
|
||||||
|
|
||||||
@@ -19,12 +18,19 @@ Everything else is the responsibility of the command's author.
|
|||||||
---
|
---
|
||||||
|
|
||||||
# Getting started
|
# Getting started
|
||||||
|
## Installation
|
||||||
|
### Building from source
|
||||||
To get started, first make sure you've properly set up your [Golang](http://golang.org/doc/install) environment and then run the
|
To get started, first make sure you've properly set up your [Golang](http://golang.org/doc/install) environment and then run the
|
||||||
```bash
|
```bash
|
||||||
$ go get github.com/adnanh/webhook
|
$ go get github.com/adnanh/webhook
|
||||||
```
|
```
|
||||||
to get the latest version of the [webhook](https://github.com/adnanh/webhook/).
|
to get the latest version of the [webhook](https://github.com/adnanh/webhook/).
|
||||||
|
|
||||||
|
### Using package manager
|
||||||
|
#### Debian "sid"
|
||||||
|
If you are using unstable version of Debian linux ("sid"), you can install webhook using `apt-get install webhook` which will install community packaged version (thanks [@freeekanayaka](https://github.com/freeekanayaka)) from https://packages.debian.org/sid/webhook
|
||||||
|
|
||||||
|
## Configuration
|
||||||
Next step is to define some hooks you want [webhook](https://github.com/adnanh/webhook/) to serve. Begin by creating an empty file named `hooks.json`. This file will contain an array of hooks the [webhook](https://github.com/adnanh/webhook/) will serve. Check [Hook definition page](https://github.com/adnanh/webhook/wiki/Hook-Definition) to see the detailed description of what properties a hook can contain, and how to use them.
|
Next step is to define some hooks you want [webhook](https://github.com/adnanh/webhook/) to serve. Begin by creating an empty file named `hooks.json`. This file will contain an array of hooks the [webhook](https://github.com/adnanh/webhook/) will serve. Check [Hook definition page](https://github.com/adnanh/webhook/wiki/Hook-Definition) to see the detailed description of what properties a hook can contain, and how to use them.
|
||||||
|
|
||||||
Let's define a simple hook named `redeploy-webhook` that will run a redeploy script located in `/var/scripts/redeploy.sh`.
|
Let's define a simple hook named `redeploy-webhook` that will run a redeploy script located in `/var/scripts/redeploy.sh`.
|
||||||
|
|||||||
+41
-21
@@ -8,6 +8,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/textproto"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -18,6 +19,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
SourceHeader string = "header"
|
SourceHeader string = "header"
|
||||||
SourceQuery string = "url"
|
SourceQuery string = "url"
|
||||||
|
SourceQueryAlias string = "query"
|
||||||
SourcePayload string = "payload"
|
SourcePayload string = "payload"
|
||||||
SourceString string = "string"
|
SourceString string = "string"
|
||||||
SourceEntirePayload string = "entire-payload"
|
SourceEntirePayload string = "entire-payload"
|
||||||
@@ -205,11 +207,13 @@ type Argument struct {
|
|||||||
// based on the Argument's source
|
// based on the Argument's source
|
||||||
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
|
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
|
||||||
var source *map[string]interface{}
|
var source *map[string]interface{}
|
||||||
|
key := ha.Name
|
||||||
|
|
||||||
switch ha.Source {
|
switch ha.Source {
|
||||||
case SourceHeader:
|
case SourceHeader:
|
||||||
source = headers
|
source = headers
|
||||||
case SourceQuery:
|
key = textproto.CanonicalMIMEHeaderKey(ha.Name)
|
||||||
|
case SourceQuery, SourceQueryAlias:
|
||||||
source = query
|
source = query
|
||||||
case SourcePayload:
|
case SourcePayload:
|
||||||
source = payload
|
source = payload
|
||||||
@@ -242,7 +246,7 @@ func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string
|
|||||||
}
|
}
|
||||||
|
|
||||||
if source != nil {
|
if source != nil {
|
||||||
return ExtractParameterAsString(ha.Name, *source)
|
return ExtractParameterAsString(key, *source)
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", false
|
return "", false
|
||||||
@@ -296,11 +300,14 @@ type Hook struct {
|
|||||||
PassArgumentsToCommand []Argument `json:"pass-arguments-to-command,omitempty"`
|
PassArgumentsToCommand []Argument `json:"pass-arguments-to-command,omitempty"`
|
||||||
JSONStringParameters []Argument `json:"parse-parameters-as-json,omitempty"`
|
JSONStringParameters []Argument `json:"parse-parameters-as-json,omitempty"`
|
||||||
TriggerRule *Rules `json:"trigger-rule,omitempty"`
|
TriggerRule *Rules `json:"trigger-rule,omitempty"`
|
||||||
|
TriggerRuleMismatchHttpResponseCode int `json:"trigger-rule-mismatch-http-response-code,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
|
// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
|
||||||
// string with the newly created object
|
// string with the newly created object
|
||||||
func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface{}) error {
|
func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface{}) []error {
|
||||||
|
var errors = make([]error, 0)
|
||||||
|
|
||||||
for i := range h.JSONStringParameters {
|
for i := range h.JSONStringParameters {
|
||||||
if arg, ok := h.JSONStringParameters[i].Get(headers, query, payload); ok {
|
if arg, ok := h.JSONStringParameters[i].Get(headers, query, payload); ok {
|
||||||
var newArg map[string]interface{}
|
var newArg map[string]interface{}
|
||||||
@@ -311,7 +318,8 @@ func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface
|
|||||||
err := decoder.Decode(&newArg)
|
err := decoder.Decode(&newArg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &ParseError{err}
|
errors = append(errors, &ParseError{err})
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var source *map[string]interface{}
|
var source *map[string]interface{}
|
||||||
@@ -321,27 +329,38 @@ func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface
|
|||||||
source = headers
|
source = headers
|
||||||
case SourcePayload:
|
case SourcePayload:
|
||||||
source = payload
|
source = payload
|
||||||
case SourceQuery:
|
case SourceQuery, SourceQueryAlias:
|
||||||
source = query
|
source = query
|
||||||
}
|
}
|
||||||
|
|
||||||
if source != nil {
|
if source != nil {
|
||||||
ReplaceParameter(h.JSONStringParameters[i].Name, source, newArg)
|
key := h.JSONStringParameters[i].Name
|
||||||
|
|
||||||
|
if h.JSONStringParameters[i].Source == SourceHeader {
|
||||||
|
key = textproto.CanonicalMIMEHeaderKey(h.JSONStringParameters[i].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplaceParameter(key, source, newArg)
|
||||||
} else {
|
} else {
|
||||||
return &SourceError{h.JSONStringParameters[i]}
|
errors = append(errors, &SourceError{h.JSONStringParameters[i]})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return &ArgumentError{h.JSONStringParameters[i]}
|
errors = append(errors, &ArgumentError{h.JSONStringParameters[i]})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractCommandArguments creates a list of arguments, based on the
|
// ExtractCommandArguments creates a list of arguments, based on the
|
||||||
// PassArgumentsToCommand property that is ready to be used with exec.Command()
|
// PassArgumentsToCommand property that is ready to be used with exec.Command()
|
||||||
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) ([]string, error) {
|
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) ([]string, []error) {
|
||||||
var args = make([]string, 0)
|
var args = make([]string, 0)
|
||||||
|
var errors = make([]error, 0)
|
||||||
|
|
||||||
args = append(args, h.ExecuteCommand)
|
args = append(args, h.ExecuteCommand)
|
||||||
|
|
||||||
@@ -350,33 +369,41 @@ func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]inter
|
|||||||
args = append(args, arg)
|
args = append(args, arg)
|
||||||
} else {
|
} else {
|
||||||
args = append(args, "")
|
args = append(args, "")
|
||||||
return args, &ArgumentError{h.PassArgumentsToCommand[i]}
|
errors = append(errors, &ArgumentError{h.PassArgumentsToCommand[i]})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return args, errors
|
||||||
|
}
|
||||||
|
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractCommandArgumentsForEnv creates a list of arguments in key=value
|
// ExtractCommandArgumentsForEnv creates a list of arguments in key=value
|
||||||
// format, based on the PassEnvironmentToCommand property that is ready to be used
|
// format, based on the PassEnvironmentToCommand property that is ready to be used
|
||||||
// with exec.Command().
|
// with exec.Command().
|
||||||
func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string]interface{}) ([]string, error) {
|
func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string]interface{}) ([]string, []error) {
|
||||||
var args = make([]string, 0)
|
var args = make([]string, 0)
|
||||||
|
var errors = make([]error, 0)
|
||||||
for i := range h.PassEnvironmentToCommand {
|
for i := range h.PassEnvironmentToCommand {
|
||||||
if arg, ok := h.PassEnvironmentToCommand[i].Get(headers, query, payload); ok {
|
if arg, ok := h.PassEnvironmentToCommand[i].Get(headers, query, payload); ok {
|
||||||
if h.PassEnvironmentToCommand[i].EnvName != "" {
|
if h.PassEnvironmentToCommand[i].EnvName != "" {
|
||||||
// first try to use the EnvName if specified
|
// first try to use the EnvName if specified
|
||||||
args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].EnvName+"="+arg)
|
args = append(args, h.PassEnvironmentToCommand[i].EnvName+"="+arg)
|
||||||
} else {
|
} else {
|
||||||
// then fallback on the name
|
// then fallback on the name
|
||||||
args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
|
args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return args, &ArgumentError{h.PassEnvironmentToCommand[i]}
|
errors = append(errors, &ArgumentError{h.PassEnvironmentToCommand[i]})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return args, errors
|
||||||
|
}
|
||||||
|
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,10 +548,3 @@ func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, bod
|
|||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommandStatusResponse type encapsulates the executed command exit code, message, stdout and stderr
|
|
||||||
type CommandStatusResponse struct {
|
|
||||||
ResponseMessage string `json:"message,omitempty"`
|
|
||||||
Output string `json:"output,omitempty"`
|
|
||||||
Error string `json:"error,omitempty"`
|
|
||||||
}
|
|
||||||
|
|||||||
+32
-32
@@ -72,15 +72,15 @@ var argumentGetTests = []struct {
|
|||||||
value string
|
value string
|
||||||
ok bool
|
ok bool
|
||||||
}{
|
}{
|
||||||
{"header", "a", &map[string]interface{}{"a": "z"}, nil, nil, "z", true},
|
{"header", "a", &map[string]interface{}{"A": "z"}, nil, nil, "z", true},
|
||||||
{"url", "a", nil, &map[string]interface{}{"a": "z"}, nil, "z", true},
|
{"url", "a", nil, &map[string]interface{}{"a": "z"}, nil, "z", true},
|
||||||
{"payload", "a", nil, nil, &map[string]interface{}{"a": "z"}, "z", true},
|
{"payload", "a", nil, nil, &map[string]interface{}{"a": "z"}, "z", true},
|
||||||
{"string", "a", nil, nil, &map[string]interface{}{"a": "z"}, "a", true},
|
{"string", "a", nil, nil, &map[string]interface{}{"a": "z"}, "a", true},
|
||||||
// failures
|
// failures
|
||||||
{"header", "a", nil, &map[string]interface{}{"a": "z"}, &map[string]interface{}{"a": "z"}, "", false}, // nil headers
|
{"header", "a", nil, &map[string]interface{}{"a": "z"}, &map[string]interface{}{"a": "z"}, "", false}, // nil headers
|
||||||
{"url", "a", &map[string]interface{}{"a": "z"}, nil, &map[string]interface{}{"a": "z"}, "", false}, // nil query
|
{"url", "a", &map[string]interface{}{"A": "z"}, nil, &map[string]interface{}{"a": "z"}, "", false}, // nil query
|
||||||
{"payload", "a", &map[string]interface{}{"a": "z"}, &map[string]interface{}{"a": "z"}, nil, "", false}, // nil payload
|
{"payload", "a", &map[string]interface{}{"A": "z"}, &map[string]interface{}{"a": "z"}, nil, "", false}, // nil payload
|
||||||
{"foo", "a", &map[string]interface{}{"a": "z"}, nil, nil, "", false}, // invalid source
|
{"foo", "a", &map[string]interface{}{"A": "z"}, nil, nil, "", false}, // invalid source
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArgumentGet(t *testing.T) {
|
func TestArgumentGet(t *testing.T) {
|
||||||
@@ -99,14 +99,14 @@ var hookParseJSONParametersTests = []struct {
|
|||||||
rheaders, rquery, rpayload *map[string]interface{}
|
rheaders, rquery, rpayload *map[string]interface{}
|
||||||
ok bool
|
ok bool
|
||||||
}{
|
}{
|
||||||
{[]Argument{Argument{"header", "a", ""}}, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, nil, true},
|
{[]Argument{Argument{"header", "a", ""}}, &map[string]interface{}{"A": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"A": map[string]interface{}{"b": "y"}}, nil, nil, true},
|
||||||
{[]Argument{Argument{"url", "a", ""}}, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true},
|
{[]Argument{Argument{"url", "a", ""}}, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true},
|
||||||
{[]Argument{Argument{"payload", "a", ""}}, nil, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true},
|
{[]Argument{Argument{"payload", "a", ""}}, nil, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true},
|
||||||
{[]Argument{Argument{"header", "z", ""}}, &map[string]interface{}{"z": `{}`}, nil, nil, &map[string]interface{}{"z": map[string]interface{}{}}, nil, nil, true},
|
{[]Argument{Argument{"header", "z", ""}}, &map[string]interface{}{"Z": `{}`}, nil, nil, &map[string]interface{}{"Z": map[string]interface{}{}}, nil, nil, true},
|
||||||
// failures
|
// failures
|
||||||
{[]Argument{Argument{"header", "z", ""}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil, false}, // empty string
|
{[]Argument{Argument{"header", "z", ""}}, &map[string]interface{}{"Z": ``}, nil, nil, &map[string]interface{}{"Z": ``}, nil, nil, false}, // empty string
|
||||||
{[]Argument{Argument{"header", "y", ""}}, &map[string]interface{}{"X": `{}`}, nil, nil, &map[string]interface{}{"X": `{}`}, nil, nil, false}, // missing parameter
|
{[]Argument{Argument{"header", "y", ""}}, &map[string]interface{}{"X": `{}`}, nil, nil, &map[string]interface{}{"X": `{}`}, nil, nil, false}, // missing parameter
|
||||||
{[]Argument{Argument{"string", "z", ""}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil, false}, // invalid argument source
|
{[]Argument{Argument{"string", "z", ""}}, &map[string]interface{}{"Z": ``}, nil, nil, &map[string]interface{}{"Z": ``}, nil, nil, false}, // invalid argument source
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHookParseJSONParameters(t *testing.T) {
|
func TestHookParseJSONParameters(t *testing.T) {
|
||||||
@@ -126,9 +126,9 @@ var hookExtractCommandArgumentsTests = []struct {
|
|||||||
value []string
|
value []string
|
||||||
ok bool
|
ok bool
|
||||||
}{
|
}{
|
||||||
{"test", []Argument{Argument{"header", "a", ""}}, &map[string]interface{}{"a": "z"}, nil, nil, []string{"test", "z"}, true},
|
{"test", []Argument{Argument{"header", "a", ""}}, &map[string]interface{}{"A": "z"}, nil, nil, []string{"test", "z"}, true},
|
||||||
// failures
|
// failures
|
||||||
{"fail", []Argument{Argument{"payload", "a", ""}}, &map[string]interface{}{"a": "z"}, nil, nil, []string{"fail", ""}, false},
|
{"fail", []Argument{Argument{"payload", "a", ""}}, &map[string]interface{}{"A": "z"}, nil, nil, []string{"fail", ""}, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHookExtractCommandArguments(t *testing.T) {
|
func TestHookExtractCommandArguments(t *testing.T) {
|
||||||
@@ -171,22 +171,22 @@ var hookExtractCommandArgumentsForEnvTests = []struct {
|
|||||||
{
|
{
|
||||||
"test",
|
"test",
|
||||||
[]Argument{Argument{"header", "a", ""}},
|
[]Argument{Argument{"header", "a", ""}},
|
||||||
&map[string]interface{}{"a": "z"}, nil, nil,
|
&map[string]interface{}{"A": "z"}, nil, nil,
|
||||||
[]string{"HOOK_a=z"},
|
[]string{"HOOK_a=z"},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"test",
|
"test",
|
||||||
[]Argument{Argument{"header", "a", "MYKEY"}},
|
[]Argument{Argument{"header", "a", "MYKEY"}},
|
||||||
&map[string]interface{}{"a": "z"}, nil, nil,
|
&map[string]interface{}{"A": "z"}, nil, nil,
|
||||||
[]string{"HOOK_MYKEY=z"},
|
[]string{"MYKEY=z"},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
// failures
|
// failures
|
||||||
{
|
{
|
||||||
"fail",
|
"fail",
|
||||||
[]Argument{Argument{"payload", "a", ""}},
|
[]Argument{Argument{"payload", "a", ""}},
|
||||||
&map[string]interface{}{"a": "z"}, nil, nil,
|
&map[string]interface{}{"A": "z"}, nil, nil,
|
||||||
[]string{},
|
[]string{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
@@ -248,16 +248,16 @@ var matchRuleTests = []struct {
|
|||||||
ok bool
|
ok bool
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{"value", "", "", "z", Argument{"header", "a", ""}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
{"value", "", "", "z", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false},
|
||||||
{"regex", "^z", "", "z", Argument{"header", "a", ""}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
{"regex", "^z", "", "z", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false},
|
||||||
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a", ""}, &map[string]interface{}{"a": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), true, false},
|
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), true, false},
|
||||||
// failures
|
// failures
|
||||||
{"value", "", "", "X", Argument{"header", "a", ""}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
{"value", "", "", "X", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false},
|
||||||
{"regex", "^X", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
{"regex", "^X", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false},
|
||||||
{"value", "", "2", "X", Argument{"header", "a", ""}, &map[string]interface{}{"y": "z"}, nil, nil, []byte{}, false, false}, // reference invalid header
|
{"value", "", "2", "X", Argument{"header", "a", ""}, &map[string]interface{}{"Y": "z"}, nil, nil, []byte{}, false, false}, // reference invalid header
|
||||||
// errors
|
// errors
|
||||||
{"regex", "*", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, true}, // invalid regex
|
{"regex", "*", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, true}, // invalid regex
|
||||||
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a", ""}, &map[string]interface{}{"a": ""}, nil, nil, []byte{}, false, true}, // invalid hmac
|
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": ""}, nil, nil, []byte{}, false, true}, // invalid hmac
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMatchRule(t *testing.T) {
|
func TestMatchRule(t *testing.T) {
|
||||||
@@ -284,7 +284,7 @@ var andRuleTests = []struct {
|
|||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "z", "b": "y"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "z", "B": "y"}, nil, nil, []byte{},
|
||||||
true, false,
|
true, false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -293,7 +293,7 @@ var andRuleTests = []struct {
|
|||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "z", "b": "Y"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "z", "B": "Y"}, nil, nil, []byte{},
|
||||||
false, false,
|
false, false,
|
||||||
},
|
},
|
||||||
// Complex test to cover Rules.Evaluate
|
// Complex test to cover Rules.Evaluate
|
||||||
@@ -319,7 +319,7 @@ var andRuleTests = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "z", "b": "y", "c": "x", "d": "w", "e": "X", "f": "X"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "z", "B": "y", "C": "x", "D": "w", "E": "X", "F": "X"}, nil, nil, []byte{},
|
||||||
true, false,
|
true, false,
|
||||||
},
|
},
|
||||||
{"empty rule", AndRule{{}}, nil, nil, nil, nil, false, false},
|
{"empty rule", AndRule{{}}, nil, nil, nil, nil, false, false},
|
||||||
@@ -327,7 +327,7 @@ var andRuleTests = []struct {
|
|||||||
{
|
{
|
||||||
"invalid rule",
|
"invalid rule",
|
||||||
AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", ""}}}},
|
AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", ""}}}},
|
||||||
&map[string]interface{}{"y": "z"}, nil, nil, nil,
|
&map[string]interface{}{"Y": "z"}, nil, nil, nil,
|
||||||
false, false,
|
false, false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -355,7 +355,7 @@ var orRuleTests = []struct {
|
|||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "z", "b": "X"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "z", "B": "X"}, nil, nil, []byte{},
|
||||||
true, false,
|
true, false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -364,7 +364,7 @@ var orRuleTests = []struct {
|
|||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "X", "b": "y"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "X", "B": "y"}, nil, nil, []byte{},
|
||||||
true, false,
|
true, false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -373,7 +373,7 @@ var orRuleTests = []struct {
|
|||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "Z", "b": "Y"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "Z", "B": "Y"}, nil, nil, []byte{},
|
||||||
false, false,
|
false, false,
|
||||||
},
|
},
|
||||||
// failures
|
// failures
|
||||||
@@ -382,7 +382,7 @@ var orRuleTests = []struct {
|
|||||||
OrRule{
|
OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"y": "Z"}, nil, nil, []byte{},
|
&map[string]interface{}{"Y": "Z"}, nil, nil, []byte{},
|
||||||
false, false,
|
false, false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -404,8 +404,8 @@ var notRuleTests = []struct {
|
|||||||
ok bool
|
ok bool
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", ""}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", ""}}}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false},
|
||||||
{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNotRule(t *testing.T) {
|
func TestNotRule(t *testing.T) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"execute-command": "{{ .Hookecho }}",
|
"execute-command": "{{ .Hookecho }}",
|
||||||
"command-working-directory": "/",
|
"command-working-directory": "/",
|
||||||
"include-command-output-in-response": true,
|
"include-command-output-in-response": true,
|
||||||
|
"trigger-rule-mismatch-http-response-code": 400,
|
||||||
"pass-environment-to-command":
|
"pass-environment-to-command":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@@ -57,8 +58,9 @@
|
|||||||
"id": "bitbucket",
|
"id": "bitbucket",
|
||||||
"execute-command": "{{ .Hookecho }}",
|
"execute-command": "{{ .Hookecho }}",
|
||||||
"command-working-directory": "/",
|
"command-working-directory": "/",
|
||||||
"include-command-output-in-response": true,
|
"include-command-output-in-response": false,
|
||||||
"response-message": "success",
|
"response-message": "success",
|
||||||
|
"trigger-rule-mismatch-http-response-code": 999,
|
||||||
"parse-parameters-as-json": [
|
"parse-parameters-as-json": [
|
||||||
{
|
{
|
||||||
"source": "payload",
|
"source": "payload",
|
||||||
@@ -136,4 +138,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
+46
-29
@@ -21,7 +21,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
version = "2.4.0"
|
version = "2.6.1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -35,6 +35,7 @@ var (
|
|||||||
secure = flag.Bool("secure", false, "use HTTPS instead of HTTP")
|
secure = flag.Bool("secure", false, "use HTTPS instead of HTTP")
|
||||||
cert = flag.String("cert", "cert.pem", "path to the HTTPS certificate pem file")
|
cert = flag.String("cert", "cert.pem", "path to the HTTPS certificate pem file")
|
||||||
key = flag.String("key", "key.pem", "path to the HTTPS certificate private key pem file")
|
key = flag.String("key", "key.pem", "path to the HTTPS certificate private key pem file")
|
||||||
|
justDisplayVersion = flag.Bool("version", false, "display webhook version and quit")
|
||||||
|
|
||||||
responseHeaders hook.ResponseHeaders
|
responseHeaders hook.ResponseHeaders
|
||||||
|
|
||||||
@@ -51,6 +52,11 @@ func main() {
|
|||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if *justDisplayVersion {
|
||||||
|
fmt.Println("webhook version " + version)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
log.SetPrefix("[webhook] ")
|
log.SetPrefix("[webhook] ")
|
||||||
log.SetFlags(log.Ldate | log.Ltime)
|
log.SetFlags(log.Ldate | log.Ltime)
|
||||||
|
|
||||||
@@ -191,12 +197,10 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handle hook
|
// handle hook
|
||||||
if err = matchedHook.ParseJSONParameters(&headers, &query, &payload); err != nil {
|
if errors := matchedHook.ParseJSONParameters(&headers, &query, &payload); errors != nil {
|
||||||
msg := fmt.Sprintf("error parsing JSON parameters: %s", err)
|
for _, err := range errors {
|
||||||
log.Printf(msg)
|
log.Printf("error parsing JSON parameters: %s\n", err)
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
}
|
||||||
fmt.Fprintf(w, "Unable to parse JSON parameters.")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ok bool
|
var ok bool
|
||||||
@@ -222,8 +226,15 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if matchedHook.CaptureCommandOutput {
|
if matchedHook.CaptureCommandOutput {
|
||||||
response := handleHook(matchedHook, &headers, &query, &payload, &body)
|
response, err := handleHook(matchedHook, &headers, &query, &payload, &body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
fmt.Fprintf(w, "Error occurred while executing the hook's command. Please check your logs for more details.")
|
||||||
|
} else {
|
||||||
fmt.Fprintf(w, response)
|
fmt.Fprintf(w, response)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
go handleHook(matchedHook, &headers, &query, &payload, &body)
|
go handleHook(matchedHook, &headers, &query, &payload, &body)
|
||||||
fmt.Fprintf(w, matchedHook.ResponseMessage)
|
fmt.Fprintf(w, matchedHook.ResponseMessage)
|
||||||
@@ -231,6 +242,17 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if a return code is configured for the hook
|
||||||
|
if matchedHook.TriggerRuleMismatchHttpResponseCode != 0 {
|
||||||
|
// Check if the configured return code is supported by the http package
|
||||||
|
// by testing if there is a StatusText for this code.
|
||||||
|
if len(http.StatusText(matchedHook.TriggerRuleMismatchHttpResponseCode)) > 0 {
|
||||||
|
w.WriteHeader(matchedHook.TriggerRuleMismatchHttpResponseCode)
|
||||||
|
} else {
|
||||||
|
log.Printf("%s got matched, but the configured return code %d is unknown - defaulting to 200\n", matchedHook.ID, matchedHook.TriggerRuleMismatchHttpResponseCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if none of the hooks got triggered
|
// if none of the hooks got triggered
|
||||||
log.Printf("%s got matched, but didn't get triggered because the trigger rules were not satisfied\n", matchedHook.ID)
|
log.Printf("%s got matched, but didn't get triggered because the trigger rules were not satisfied\n", matchedHook.ID)
|
||||||
|
|
||||||
@@ -241,48 +263,43 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleHook(h *hook.Hook, headers, query, payload *map[string]interface{}, body *[]byte) string {
|
func handleHook(h *hook.Hook, headers, query, payload *map[string]interface{}, body *[]byte) (string, error) {
|
||||||
var err error
|
var errors []error
|
||||||
|
|
||||||
cmd := exec.Command(h.ExecuteCommand)
|
cmd := exec.Command(h.ExecuteCommand)
|
||||||
cmd.Dir = h.CommandWorkingDirectory
|
cmd.Dir = h.CommandWorkingDirectory
|
||||||
|
|
||||||
cmd.Args, err = h.ExtractCommandArguments(headers, query, payload)
|
cmd.Args, errors = h.ExtractCommandArguments(headers, query, payload)
|
||||||
if err != nil {
|
if errors != nil {
|
||||||
log.Printf("error extracting command arguments: %s", err)
|
for _, err := range errors {
|
||||||
|
log.Printf("error extracting command arguments: %s\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var envs []string
|
var envs []string
|
||||||
envs, err = h.ExtractCommandArgumentsForEnv(headers, query, payload)
|
envs, errors = h.ExtractCommandArgumentsForEnv(headers, query, payload)
|
||||||
if err != nil {
|
|
||||||
log.Printf("error extracting command arguments for environment: %s", err)
|
if errors != nil {
|
||||||
|
for _, err := range errors {
|
||||||
|
log.Printf("error extracting command arguments for environment: %s\n", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cmd.Env = append(os.Environ(), envs...)
|
cmd.Env = append(os.Environ(), envs...)
|
||||||
|
|
||||||
log.Printf("executing %s (%s) with arguments %q and environment %s using %s as cwd\n", h.ExecuteCommand, cmd.Path, cmd.Args, envs, cmd.Dir)
|
log.Printf("executing %s (%s) with arguments %q and environment %s using %s as cwd\n", h.ExecuteCommand, cmd.Path, cmd.Args, envs, cmd.Dir)
|
||||||
|
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.Output()
|
||||||
|
|
||||||
log.Printf("command output: %s\n", out)
|
log.Printf("command output: %s\n", out)
|
||||||
|
|
||||||
var errorResponse string
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error occurred: %+v\n", err)
|
log.Printf("error occurred: %+v\n", err)
|
||||||
errorResponse = fmt.Sprintf("%+v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("finished handling %s\n", h.ID)
|
log.Printf("finished handling %s\n", h.ID)
|
||||||
|
|
||||||
var response []byte
|
return string(out), err
|
||||||
response, err = json.Marshal(&hook.CommandStatusResponse{ResponseMessage: h.ResponseMessage, Output: string(out), Error: errorResponse})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("error marshalling response: %+v", err)
|
|
||||||
return h.ResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func reloadHooks() {
|
func reloadHooks() {
|
||||||
|
|||||||
+17
-6
@@ -372,7 +372,9 @@ var hookHandlerTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
false,
|
false,
|
||||||
http.StatusOK,
|
http.StatusOK,
|
||||||
`{"output":"arg: 1481a2de7b2a7d02428ad93446ab166be7793fbb lolwut@noway.biz\nenv: HOOK_head_commit.timestamp=2013-03-12T08:14:29-07:00\n"}`,
|
`arg: 1481a2de7b2a7d02428ad93446ab166be7793fbb lolwut@noway.biz
|
||||||
|
env: HOOK_head_commit.timestamp=2013-03-12T08:14:29-07:00
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bitbucket", // bitbucket sends their payload using uriencoded params.
|
"bitbucket", // bitbucket sends their payload using uriencoded params.
|
||||||
@@ -381,7 +383,7 @@ var hookHandlerTests = []struct {
|
|||||||
`payload={"canon_url": "https://bitbucket.org","commits": [{"author": "marcus","branch": "master","files": [{"file": "somefile.py","type": "modified"}],"message": "Added some more things to somefile.py\n","node": "620ade18607a","parents": ["702c70160afc"],"raw_author": "Marcus Bertrand <marcus@somedomain.com>","raw_node": "620ade18607ac42d872b568bb92acaa9a28620e9","revision": null,"size": -1,"timestamp": "2012-05-30 05:58:56","utctimestamp": "2014-11-07 15:19:02+00:00"}],"repository": {"absolute_url": "/webhook/testing/","fork": false,"is_private": true,"name": "Project X","owner": "marcus","scm": "git","slug": "project-x","website": "https://atlassian.com/"},"user": "marcus"}`,
|
`payload={"canon_url": "https://bitbucket.org","commits": [{"author": "marcus","branch": "master","files": [{"file": "somefile.py","type": "modified"}],"message": "Added some more things to somefile.py\n","node": "620ade18607a","parents": ["702c70160afc"],"raw_author": "Marcus Bertrand <marcus@somedomain.com>","raw_node": "620ade18607ac42d872b568bb92acaa9a28620e9","revision": null,"size": -1,"timestamp": "2012-05-30 05:58:56","utctimestamp": "2014-11-07 15:19:02+00:00"}],"repository": {"absolute_url": "/webhook/testing/","fork": false,"is_private": true,"name": "Project X","owner": "marcus","scm": "git","slug": "project-x","website": "https://atlassian.com/"},"user": "marcus"}`,
|
||||||
true,
|
true,
|
||||||
http.StatusOK,
|
http.StatusOK,
|
||||||
`{"message":"success"}`,
|
`success`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"gitlab",
|
"gitlab",
|
||||||
@@ -431,7 +433,8 @@ var hookHandlerTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
false,
|
false,
|
||||||
http.StatusOK,
|
http.StatusOK,
|
||||||
`{"message":"success","output":"arg: b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327 John Smith john@example.com\n"}`,
|
`arg: b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327 John Smith john@example.com
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -469,7 +472,9 @@ var hookHandlerTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
false,
|
false,
|
||||||
http.StatusOK,
|
http.StatusOK,
|
||||||
`{"output":"arg: 1481a2de7b2a7d02428ad93446ab166be7793fbb lolwut@noway.biz\nenv: HOOK_head_commit.timestamp=2013-03-12T08:14:29-07:00\n"}`,
|
`arg: 1481a2de7b2a7d02428ad93446ab166be7793fbb lolwut@noway.biz
|
||||||
|
env: HOOK_head_commit.timestamp=2013-03-12T08:14:29-07:00
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -506,8 +511,14 @@ var hookHandlerTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
false,
|
false,
|
||||||
http.StatusOK,
|
http.StatusOK,
|
||||||
`{"output":"arg: 1481a2de7b2a7d02428ad93446ab166be7793fbb lolwut@noway.biz\n"}`,
|
`arg: 1481a2de7b2a7d02428ad93446ab166be7793fbb lolwut@noway.biz
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
|
|
||||||
{"empty payload", "github", nil, `{}`, false, http.StatusOK, `Hook rules were not satisfied.`},
|
// test with custom return code
|
||||||
|
{"empty payload", "github", nil, `{}`, false, http.StatusBadRequest, `Hook rules were not satisfied.`},
|
||||||
|
// test with custom invalid http code, should default to 200 OK
|
||||||
|
{"empty payload", "bitbucket", nil, `{}`, false, http.StatusOK, `Hook rules were not satisfied.`},
|
||||||
|
// test with no configured http return code, should default to 200 OK
|
||||||
|
{"empty payload", "gitlab", nil, `{}`, false, http.StatusOK, `Hook rules were not satisfied.`},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user