diff --git a/hook/hook.go b/hook/hook.go index b336e54..6fcda3e 100644 --- a/hook/hook.go +++ b/hook/hook.go @@ -15,6 +15,8 @@ import ( "regexp" "strconv" "strings" + + "github.com/ghodss/yaml" ) // Constants used to specify the parameter source @@ -503,7 +505,7 @@ func (h *Hooks) LoadFromFile(path string) error { return e } - e = json.Unmarshal(file, h) + e = yaml.Unmarshal(file, h) return e } diff --git a/hook/hook_test.go b/hook/hook_test.go index 3a92703..dc83cf5 100644 --- a/hook/hook_test.go +++ b/hook/hook_test.go @@ -233,6 +233,7 @@ var hooksLoadFromFileTests = []struct { ok bool }{ {"../hooks.json.example", true}, + {"../hooks.yaml.example", true}, {"", true}, // failures {"missing.json", false}, diff --git a/hooks.yaml.example b/hooks.yaml.example new file mode 100644 index 0000000..74713e0 --- /dev/null +++ b/hooks.yaml.example @@ -0,0 +1,28 @@ +- id: webhook + execute-command: /home/adnan/redeploy-go-webhook.sh + command-working-directory: /home/adnan/go + response-message: I got the payload! + response-headers: + - name: Access-Control-Allow-Origin + value: '*' + pass-arguments-to-command: + - source: payload + name: head_commit.id + - source: payload + name: pusher.name + - source: payload + name: pusher.email + trigger-rule: + and: + - match: + type: payload-hash-sha1 + secret: mysecret + parameter: + source: header + name: X-Hub-Signature + - match: + type: value + value: refs/heads/master + parameter: + source: payload + name: ref diff --git a/test/hooks.yaml.tmpl b/test/hooks.yaml.tmpl new file mode 100644 index 0000000..176a76d --- /dev/null +++ b/test/hooks.yaml.tmpl @@ -0,0 +1,75 @@ +- trigger-rule: + and: + - match: + parameter: + source: header + name: X-Hub-Signature + secret: mysecret + type: payload-hash-sha1 + - match: + parameter: + source: payload + name: ref + type: value + value: refs/heads/master + include-command-output-in-response: true + trigger-rule-mismatch-http-response-code: 400 + execute-command: '{{ .Hookecho }}' + pass-arguments-to-command: + - source: payload + name: head_commit.id + - source: payload + name: head_commit.author.email + pass-environment-to-command: + - source: payload + name: head_commit.timestamp + id: github + command-working-directory: / +- trigger-rule: + and: + - match: + parameter: + source: payload + name: payload.canon_url + type: value + value: https://bitbucket.org + - match: + parameter: + source: payload + name: payload.repository.absolute_url + type: value + value: /webhook/testing/ + - match: + parameter: + source: payload + name: payload.commits.0.branch + type: value + value: master + parse-parameters-as-json: + - source: payload + name: payload + trigger-rule-mismatch-http-response-code: 999 + execute-command: '{{ .Hookecho }}' + response-message: success + include-command-output-in-response: false + id: bitbucket + command-working-directory: / +- trigger-rule: + match: + parameter: + source: payload + name: ref + type: value + value: refs/heads/master + pass-arguments-to-command: + - source: payload + name: commits.0.id + - source: payload + name: user_name + - source: payload + name: user_email + execute-command: '{{ .Hookecho }}' + response-message: success + include-command-output-in-response: true + id: gitlab + command-working-directory: / diff --git a/webhook_test.go b/webhook_test.go index eef1e0a..5b3c760 100644 --- a/webhook_test.go +++ b/webhook_test.go @@ -21,60 +21,62 @@ func TestWebhook(t *testing.T) { hookecho, cleanupHookecho := buildHookecho(t) defer cleanupHookecho() - config, cleanupConfig := genConfig(t, hookecho) - defer cleanupConfig() + for _, hookTmpl := range []string{"test/hooks.json.tmpl", "test/hooks.yaml.tmpl"} { + config, cleanupConfig := genConfig(t, hookecho, hookTmpl) + defer cleanupConfig() - webhook, cleanupWebhook := buildWebhook(t) - defer cleanupWebhook() + webhook, cleanupWebhook := buildWebhook(t) + defer cleanupWebhook() - ip, port := serverAddress(t) - args := []string{fmt.Sprintf("-hooks=%s", config), fmt.Sprintf("-ip=%s", ip), fmt.Sprintf("-port=%s", port), "-verbose"} + ip, port := serverAddress(t) + args := []string{fmt.Sprintf("-hooks=%s", config), fmt.Sprintf("-ip=%s", ip), fmt.Sprintf("-port=%s", port), "-verbose"} - cmd := exec.Command(webhook, args...) - //cmd.Stderr = os.Stderr // uncomment to see verbose output - cmd.Env = webhookEnv() - cmd.Args[0] = "webhook" - if err := cmd.Start(); err != nil { - t.Fatalf("failed to start webhook: %s", err) - } - defer killAndWait(cmd) - - waitForServerReady(t, ip, port) - - for _, tt := range hookHandlerTests { - url := fmt.Sprintf("http://%s:%s/hooks/%s", ip, port, tt.id) - - req, err := http.NewRequest("POST", url, ioutil.NopCloser(strings.NewReader(tt.body))) - if err != nil { - t.Errorf("New request failed: %s", err) + cmd := exec.Command(webhook, args...) + //cmd.Stderr = os.Stderr // uncomment to see verbose output + cmd.Env = webhookEnv() + cmd.Args[0] = "webhook" + if err := cmd.Start(); err != nil { + t.Fatalf("failed to start webhook: %s", err) } + defer killAndWait(cmd) - for k, v := range tt.headers { - req.Header.Add(k, v) - } + waitForServerReady(t, ip, port) - var res *http.Response + for _, tt := range hookHandlerTests { + url := fmt.Sprintf("http://%s:%s/hooks/%s", ip, port, tt.id) - if tt.urlencoded == true { - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - } else { - req.Header.Add("Content-Type", "application/json") - } + req, err := http.NewRequest("POST", url, ioutil.NopCloser(strings.NewReader(tt.body))) + if err != nil { + t.Errorf("New request failed: %s", err) + } - client := &http.Client{} - res, err = client.Do(req) - if err != nil { - t.Errorf("client.Do failed: %s", err) - } + for k, v := range tt.headers { + req.Header.Add(k, v) + } - body, err := ioutil.ReadAll(res.Body) - res.Body.Close() - if err != nil { - t.Errorf("POST %q: failed to ready body: %s", tt.desc, err) - } + var res *http.Response - if res.StatusCode != tt.respStatus || string(body) != tt.respBody { - t.Errorf("failed %q (id: %s):\nexpected status: %#v, response: %s\ngot status: %#v, response: %s", tt.desc, tt.id, tt.respStatus, tt.respBody, res.StatusCode, body) + if tt.urlencoded == true { + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + } else { + req.Header.Add("Content-Type", "application/json") + } + + client := &http.Client{} + res, err = client.Do(req) + if err != nil { + t.Errorf("client.Do failed: %s", err) + } + + body, err := ioutil.ReadAll(res.Body) + res.Body.Close() + if err != nil { + t.Errorf("POST %q: failed to ready body: %s", tt.desc, err) + } + + if res.StatusCode != tt.respStatus || string(body) != tt.respBody { + t.Errorf("failed %q (id: %s):\nexpected status: %#v, response: %s\ngot status: %#v, response: %s", tt.desc, tt.id, tt.respStatus, tt.respBody, res.StatusCode, body) + } } } } @@ -103,8 +105,8 @@ func buildHookecho(t *testing.T) (bin string, cleanup func()) { return bin, func() { os.RemoveAll(tmp) } } -func genConfig(t *testing.T, bin string) (config string, cleanup func()) { - tmpl := template.Must(template.ParseFiles("test/hooks.json.tmpl")) +func genConfig(t *testing.T, bin string, hookTemplate string) (config string, cleanup func()) { + tmpl := template.Must(template.ParseFiles(hookTemplate)) tmp, err := ioutil.TempDir("", "webhook-config-") if err != nil { @@ -116,7 +118,9 @@ func genConfig(t *testing.T, bin string) (config string, cleanup func()) { } }() - path := filepath.Join(tmp, "hooks.json") + outputBaseName := filepath.Ext(filepath.Ext(hookTemplate)) + + path := filepath.Join(tmp, outputBaseName) file, err := os.Create(path) if err != nil { t.Fatalf("Creating config template: %v", err)