Skip to content

Commit

Permalink
cmd/webhook: map script arguments to .Args field
Browse files Browse the repository at this point in the history
Closes #3
  • Loading branch information
rjeczalik committed Mar 23, 2015
1 parent cff9569 commit a8e20f8
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 86 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Notify Slack's channel about recent push:
> {{with $e := .}}
> {{if eq $e.Name "push"}}
> {{with $text := (urlquery (printf "%s pushed to %s" $e.Payload.Pusher.Email $e.Payload.Repository.Name))}}
> {{with $url := (printf "https://slack.com/api/chat.postMessage?token=%s&channel=%s&text=%s" (env "SLACK_TOKEN") (env "SLACK_CHANNEL") $text)}}
> {{with $url := (printf "https://slack.com/api/chat.postMessage?token=%s&channel=%s&text=%s" $e.Args.Token $e.Args.Channel $text)}}
> {{exec "curl" "-X" "GET" $url}}
> {{end}}
> {{end}}
Expand All @@ -118,16 +118,16 @@ Notify Slack's channel about recent push:
> EOF
```
```
~ $ SLACK_TOKEN=token SLACK_CHANNEL=channel123 webhook -secret secret123 slack.tsc
~ $ webhook -secret secret123 slack.tsc -- -token token123 -channel CH123
```
Notify HipChat's room about recent push:
```bash
~ $ cat >hipchat.tsc <<EOF
> {{with $e := .}}
> {{if eq $e.Name "push"}}
> {{with $auth := (printf "authorization: bearer %s" (env "HIPCHAT_TOKEN"))}}
> {{with $auth := (printf "authorization: bearer %s" $e.Args.Token)}}
> {{with $msg := (printf "{\"message_format\": \"text\", \"message\": \"%s pushed to %s\"}" $e.Payload.Pusher.Email $e.Payload.Repository.Name)}}
> {{with $url := (printf "https://api.hipchat.com/v2/room/%s/notification" (env "HIPCHAT_ROOM"))}}
> {{with $url := (printf "https://api.hipchat.com/v2/room/%s/notification" $e.Args.Room)}}
> {{exec "curl" "-h" "content-type: application/json" "-h" $auth "-x" "post" "-d" $msg $url | log}}
> {{end}}
> {{end}}
Expand All @@ -137,7 +137,7 @@ Notify HipChat's room about recent push:
> EOF
```
```
~ $ HIPCHAT_TOKEN=token HIPCHAT_ROOM=123 webhook -secret secret123 hipchat.tsc
~ $ webhook -secret secret123 hipchat.tsc -- -token token123 -room 123
```
### Contributing to the `webhook` package
Expand Down
2 changes: 2 additions & 0 deletions cmd/internal/tsc/testdata/script.tsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{with $e := .}}name={{$e.Name}}
payload={{$e.Payload}}{{range $k, $v := $e.Args}}{{printf "\n%s=%s" $k $v}}{{end}}{{end}}
131 changes: 131 additions & 0 deletions cmd/internal/tsc/tsc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package tsc

import (
"bytes"
"errors"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"text/template"
"unicode"
"unicode/utf8"
)

func nonil(err ...error) error {
for _, err := range err {
if err != nil {
return err
}
}
return nil
}

type Event struct {
Name string // https://developer.github.com/webhooks/#events
Payload interface{} // https://developer.github.com/v3/activity/events/types/
Args map[string]string
}

type Script struct {
// ErrorLog specifies an optional logger for errors serving requests.
// If nil, logging goes to os.Stderr via the log package's standard logger.
ErrorLog *log.Logger

OutputFunc func() io.Writer

tmpl *template.Template
args map[string]string
}

func New(file string, args []string) (*Script, error) {
if len(args)&1 == 1 {
return nil, errors.New("number of arguments for template script must be even")
}
sc := &Script{}
if len(args) != 0 {
sc.args = make(map[string]string)
for i := 0; i < len(args); i += 2 {
if len(args[i]) < 2 || args[i][0] != '-' {
return nil, errors.New("invalid flag name: " + args[i])
}
r, n := utf8.DecodeRuneInString(args[i][1:])
if r == utf8.RuneError {
return nil, errors.New("invalid flag name: " + args[i])
}
sc.args[string(unicode.ToUpper(r))+args[i][1+n:]] = args[i+1]
}
}
tmpl, err := template.New(filepath.Base(file)).Funcs(sc.funcs()).ParseFiles(file)
if err != nil {
return nil, err
}
sc.tmpl = tmpl
return sc, nil
}

func (s *Script) Webhook(event string, payload interface{}) {
w := s.output()
err := s.tmpl.Execute(w, Event{Name: event, Payload: payload, Args: s.args})
if c, ok := w.(io.Closer); ok {
err = nonil(err, c.Close())
}
if err != nil {
s.logf("ERROR template script error:", err)
}
}

func (s *Script) funcs() template.FuncMap {
return template.FuncMap{
"env": func(s string) string {
return os.Getenv(s)
},
"exec": func(cmd string, args ...string) (string, error) {
out, err := exec.Command(cmd, args...).Output()
return string(bytes.TrimSpace(out)), err
},
"log": func(v ...interface{}) string {
if len(v) != 0 {
s.log(v...)
}
return ""
},
"logf": func(format string, v ...interface{}) string {
if format == "" {
return ""
}
if len(v) == 0 {
s.logf("%s", format)
} else {
s.logf(format, v...)
}
return ""
},
}
}

func (s *Script) output() io.Writer {
if s.OutputFunc != nil {
return s.OutputFunc()
} else {
return ioutil.Discard
}
}

func (s *Script) logf(format string, v ...interface{}) {
if s.ErrorLog != nil {
s.ErrorLog.Printf(format, v...)
} else {
log.Printf(format, v...)
}
}

func (s *Script) log(v ...interface{}) {
if s.ErrorLog != nil {
s.ErrorLog.Println(v...)
} else {
log.Println(v...)
}
}
54 changes: 54 additions & 0 deletions cmd/internal/tsc/tsc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package tsc

import (
"bytes"
"io"
"path/filepath"
"testing"
)

var script = filepath.Join("testdata", "script.tsc")

func pipe() (*bytes.Buffer, func() io.Writer) {
buf := &bytes.Buffer{}
fn := func() io.Writer { return buf }
return buf, fn
}

func TestScript(t *testing.T) {
cases := [...]struct {
args []string
name string
payload interface{}
output []byte
}{{
nil,
"name",
"payload",
[]byte("name=name\npayload=payload\n"),
}, {
[]string{"-flag", "value"},
"name",
"payload",
[]byte("name=name\npayload=payload\nFlag=value\n"),
}, {
[]string{"-a", "b", "-c", "d", "-e", "f"},
"name",
"payload",
[]byte("name=name\npayload=payload\nA=b\nC=d\nE=f\n"),
}}
for i, cas := range cases {
sc, err := New(script, cas.args)
if err != nil {
t.Errorf("New()=%v (i=%d)", err, i)
continue
}
buf, out := pipe()
sc.OutputFunc = out
sc.Webhook(cas.name, cas.payload)
if !bytes.Equal(buf.Bytes(), cas.output) {
t.Errorf("want output=%q; got %q (i=%d)", cas.output, buf.Bytes(), i)
continue
}
}
}
Loading

0 comments on commit a8e20f8

Please sign in to comment.