-
Notifications
You must be signed in to change notification settings - Fork 1
/
command.go
155 lines (127 loc) · 2.83 KB
/
command.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package egg
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/gen2brain/beeep"
"github.com/hajimehoshi/go-mp3"
"github.com/hajimehoshi/oto"
)
const successSound = "sounds/success.mp3"
const errorSound = "sounds/error.mp3"
// Command contains the current command run by egg
type Command struct {
Arguments []string
ran bool
cmd *exec.Cmd
duration time.Duration
err error
}
// NewCommand returns a Command if enough args are given
func NewCommand(args []string) (*Command, error) {
if len(args) < 1 {
return nil, errors.New("No command was given")
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return &Command{
Arguments: args,
cmd: cmd,
}, nil
}
// Run will run the command keepting track of its duration
func (c *Command) Run() error {
if c.ran {
return errors.New("Attempt to run command twice")
}
c.ran = true
started := time.Now()
if c.err = c.cmd.Start(); c.err != nil {
return c.err
}
c.err = c.cmd.Wait()
completed := time.Now()
c.duration = completed.Sub(started)
return c.err
}
// AnnounceIntent prints out what egg is about to run + workingDir
func (c *Command) AnnounceIntent() {
fmt.Printf("📣 [%s] %s\n", c.workingDir(), strings.Join(c.Arguments, " "))
}
// NotifyStatus will send an os notification about the status of the command
func (c *Command) NotifyStatus() {
if c.ran {
fullCommand := strings.Join(c.Arguments, " ")
resultIcon := "✅"
resultText := "completed"
if c.err != nil {
resultIcon = "❌"
resultText = fmt.Sprintf("exitied with code %d", c.ExitCode())
}
beeep.Notify(
fmt.Sprintf("%s %s", resultIcon, fullCommand),
fmt.Sprintf("[%s] %s (%s)", c.workingDir(), resultText, c.duration),
"na",
)
}
}
// PlaySound will play either the error or success sound
func (c *Command) PlaySound() error {
if c.ran {
sound := successSound
if c.err != nil {
sound = errorSound
}
a, err := Asset(sound)
if err != nil {
return err
}
r := bytes.NewReader(a)
d, err := mp3.NewDecoder(r)
if err != nil {
return err
}
ctx, err := oto.NewContext(d.SampleRate(), 2, 2, 8192)
if err != nil {
return err
}
defer ctx.Close()
p := ctx.NewPlayer()
defer p.Close()
if _, err := io.Copy(p, d); err != nil {
return err
}
}
return nil
}
// ExitCode will return the code the command returned
func (c *Command) ExitCode() int {
if c.err != nil {
if exiterr, ok := c.err.(*exec.ExitError); ok {
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
return status.ExitStatus()
}
return 1
}
return 1
}
return 0
}
func (c Command) workingDir() string {
dir, err := os.Getwd()
if err != nil {
log.Println(err)
return "Unknown"
}
return filepath.Base(dir)
}