Skip to content

Commit

Permalink
add watch command output (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
饺子w authored Apr 23, 2019
1 parent 80aad21 commit 170fbab
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 5 deletions.
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Gotify-CLI is a command line client for pushing messages to [gotify/server][goti
* initialization wizard
* piping support (`echo message | gotify push`)
* simple to use
* watch and push script result changes (`gotify watch "curl http://example.com/api | jq '.data'"`)

## Alternatives

Expand Down Expand Up @@ -61,35 +62,55 @@ $ gotify push "my message"
$ echo my message | gotify push
$ gotify push < somefile
$ gotify push -t "my title" -p 10 "my message"
$ gotify watch "curl http://example.com/api | jq '.data'"
```

## Help

**Uses version `v1.2.0`**
**Uses version `v2.1.0`**

```bash
$ gotify help
NAME:
Gotify - The official Gotify-CLI

USAGE:
gotify [global options] command [command options] [arguments...]
cli [global options] command [command options] [arguments...]

VERSION:
1.2.0
2.1.0

COMMANDS:
init Initializes the Gotify-CLI
version, v Shows the version
config Shows the config
push, p Pushes a message
watch watch the result of a command and pushes output difference
help, h Shows a list of commands or help for one command

GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
```

### Watch help

```
NAME:
cli watch - watch the result of a command and pushes output difference
USAGE:
cli watch [command options] <cmd>
OPTIONS:
--interval value, -n value watch interval (sec) (default: 2)
--priority value, -p value Set the priority (default: 0)
--exec value, -x value Pass command to exec (default to "sh -c")
--title value, -t value Set the title (empty for command)
--token value Override the app token
--url value Override the Gotify URL
--output value, -o value Output verbosity (short|default|long) (default: "default")
```

### Push help

```bash
Expand Down
1 change: 1 addition & 0 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func main() {
command.Version(),
command.Config(),
command.Push(),
command.Watch(),
}
err := app.Run(os.Args)
if err != nil {
Expand Down
1 change: 0 additions & 1 deletion command/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"gopkg.in/urfave/cli.v1"
)


func Push() cli.Command {
return cli.Command{
Name: "push",
Expand Down
148 changes: 148 additions & 0 deletions command/watch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package command

import (
"bytes"
"errors"
"fmt"
"net/url"
"os/exec"
"strings"
"time"

"github.com/gotify/cli/v2/config"
"github.com/gotify/cli/v2/utils"
"github.com/gotify/go-api-client/v2/models"
"gopkg.in/urfave/cli.v1"
)

func Watch() cli.Command {
return cli.Command{
Name: "watch",
Usage: "watch the result of a command and pushes output difference",
ArgsUsage: "<cmd>",
Flags: []cli.Flag{
cli.Float64Flag{Name: "interval,n", Usage: "watch interval (sec)", Value: 2},
cli.IntFlag{Name: "priority,p", Usage: "Set the priority"},
cli.StringFlag{Name: "exec,x", Usage: "Pass command to exec", Value: "sh -c"},
cli.StringFlag{Name: "title,t", Usage: "Set the title (empty for command)"},
cli.StringFlag{Name: "token", Usage: "Override the app token"},
cli.StringFlag{Name: "url", Usage: "Override the Gotify URL"},
cli.StringFlag{Name: "output,o", Usage: "Output verbosity (short|default|long)", Value: "default"},
},
Action: doWatch,
}
}

func doWatch(ctx *cli.Context) {
conf, confErr := config.ReadConfig(config.GetLocations())

cmdArgs := ctx.Args()
cmdStringNotation := strings.Join(cmdArgs, " ")
execArgs := strings.Split(ctx.String("exec"), " ")
cmdArgs = append(execArgs[1:], cmdStringNotation)
execCmd := execArgs[0]

outputMode := ctx.String("output")
if !(outputMode == "default" || outputMode == "long" || outputMode == "short") {
utils.Exit1With("output mode should be short|default|long")
return
}
interval := ctx.Float64("interval")
priority := ctx.Int("priority")
title := ctx.String("title")
if title == "" {
title = cmdStringNotation
}
token := ctx.String("token")
if token == "" {
if confErr != nil {
utils.Exit1With("token is not configured, run 'gotify init'")
return
}
token = conf.Token
}
stringURL := ctx.String("url")
if stringURL == "" {
if confErr != nil {
utils.Exit1With("url is not configured, run 'gotify init'")
return
}
stringURL = conf.URL
}
parsedURL, err := url.Parse(stringURL)
if err != nil {
utils.Exit1With("invalid url", stringURL)
return
}

watchInterval := time.Duration(interval*1000) * time.Millisecond

evalCmdOutput := func() (string, error) {
cmd := exec.Command(execCmd, cmdArgs...)
timeOut := time.After(watchInterval)
outputBuf := bytes.NewBuffer([]byte{})
cmd.Stdout = outputBuf
cmd.Stderr = outputBuf
err := cmd.Start()
if err != nil {
return "", fmt.Errorf("command failed to invoke: %v", err)
}
done := make(chan error)
go func() {
err := cmd.Wait()
if err != nil {
done <- fmt.Errorf("command failed to invoke: %v", err)
}
done <- nil
}()
select {
case err := <-done:
return outputBuf.String(), err
case <-timeOut:
cmd.Process.Kill()
return outputBuf.String(), errors.New("command timed out")
}
}

lastOutput, err := evalCmdOutput()
if err != nil {
utils.Exit1With("first run failed", err)
}
for range time.NewTicker(watchInterval).C {
output, err := evalCmdOutput()
if err != nil {
output += fmt.Sprintf("\n!== <%v> ==!", err)
}
if output != lastOutput {
msgData := bytes.NewBuffer([]byte{})

switch outputMode {
case "long":
fmt.Fprintf(msgData, "command output for \"%s\" changed:\n\n", cmdStringNotation)
fmt.Fprintln(msgData, "== BEGIN OLD OUTPUT ==")
fmt.Fprint(msgData, lastOutput)
fmt.Fprintln(msgData, "== END OLD OUTPUT ==")
fmt.Fprintln(msgData, "== BEGIN NEW OUTPUT ==")
fmt.Fprint(msgData, output)
fmt.Fprintln(msgData, "== END NEW OUTPUT ==")
case "default":
fmt.Fprintf(msgData, "command output for \"%s\" changed:\n\n", cmdStringNotation)
fmt.Fprintln(msgData, "== BEGIN NEW OUTPUT ==")
fmt.Fprint(msgData, output)
fmt.Fprintln(msgData, "== END NEW OUTPUT ==")
case "short":
fmt.Fprintf(msgData, output)
}

msgString := msgData.String()
fmt.Println(msgString)
pushMessage(parsedURL, token, models.MessageExternal{
Title: title,
Message: msgString,
Priority: priority,
}, true)
lastOutput = output
}
}

}

0 comments on commit 170fbab

Please sign in to comment.