Skip to content

Commit 49ee12a

Browse files
committed
handle the error cases, add some command lines, remove unnecessary
fields
1 parent 1b2bf01 commit 49ee12a

File tree

7 files changed

+164
-99
lines changed

7 files changed

+164
-99
lines changed

build.go

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import (
1010
"github.com/fatih/color"
1111
)
1212

13+
// Builder composes of both runner and watcher. Whenever watcher gets notified, builder starts a build process, and forces the runner to restart
1314
type Builder struct {
1415
runner *Runner
1516
watcher *Watcher
1617
}
1718

19+
// NewBuilder constructs the Builder instance
1820
func NewBuilder(w *Watcher, r *Runner) *Builder {
1921
return &Builder{watcher: w, runner: r}
2022
}
@@ -24,14 +26,16 @@ func NewBuilder(w *Watcher, r *Runner) *Builder {
2426
func (b *Builder) Build(p *Params) {
2527
go b.registerSignalHandler()
2628
go func() {
27-
b.watcher.update <- true
29+
// used for triggering the first build
30+
b.watcher.update <- struct{}{}
2831
}()
2932

30-
for <-b.watcher.Wait() {
31-
fileName := p.createBinaryName()
33+
for range b.watcher.Wait() {
34+
fileName := p.generateBinaryName()
3235

33-
pkg := p.GetPackage()
36+
pkg := p.packagePath()
3437

38+
log.Println("build started")
3539
color.Cyan("Building %s...\n", pkg)
3640

3741
// build package
@@ -50,25 +54,19 @@ func (b *Builder) Build(p *Params) {
5054

5155
continue
5256
}
57+
log.Println("build completed")
5358

5459
// and start the new process
5560
b.runner.restart(fileName)
5661
}
5762
}
5863

5964
func (b *Builder) registerSignalHandler() {
60-
go func() {
61-
signals := make(chan os.Signal, 1)
62-
signal.Notify(signals)
63-
for {
64-
signal := <-signals
65-
switch signal {
66-
case syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGSTOP:
67-
b.watcher.Close()
68-
b.runner.Close()
69-
}
70-
}
71-
}()
65+
signals := make(chan os.Signal)
66+
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
67+
<-signals
68+
b.watcher.Close()
69+
b.runner.Close()
7270
}
7371

7472
// interpretError checks the error, and returns nil if it is

cmd/watcher/main.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ package main
33
import (
44
"os"
55

6-
watcher "github.com/canthefason/go-watcher"
6+
"github.com/canthefason/go-watcher"
77
)
88

99
func main() {
10-
params := watcher.PrepareArgs(os.Args)
10+
params := watcher.ParseArgs(os.Args)
1111

1212
w := watcher.MustRegisterWatcher(params)
13-
defer w.Close()
1413

1514
r := watcher.NewRunner()
1615

common.go

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,36 @@ import (
1414
// Binary name used for built package
1515
const binaryName = "watcher"
1616

17+
var watcherFlags = []string{"run", "watch", "watch-vendor"}
18+
19+
// Params is used for keeping go-watcher and application flag parameters
1720
type Params struct {
1821
// Package parameters
1922
Package []string
2023
// Go-Watcher parameters
2124
Watcher map[string]string
2225
}
2326

24-
// NewParams creates a new instance of Params and returns the pointer
27+
// NewParams creates a new Params instance
2528
func NewParams() *Params {
2629
return &Params{
2730
Package: make([]string, 0),
2831
Watcher: make(map[string]string),
2932
}
3033
}
3134

32-
// Get returns the watcher parameter with given name
35+
// Get returns the watcher parameter with the given name
3336
func (p *Params) Get(name string) string {
3437
return p.Watcher[name]
3538
}
3639

37-
// CloneRun copies run parameter value to watch parameter in-case watch
38-
// parameter does not exist
39-
func (p *Params) CloneRun() {
40+
func (p *Params) cloneRunFlag() {
4041
if p.Watcher["watch"] == "" && p.Watcher["run"] != "" {
4142
p.Watcher["watch"] = p.Watcher["run"]
4243
}
4344
}
4445

45-
func (p *Params) GetPackage() string {
46+
func (p *Params) packagePath() string {
4647
run := p.Get("run")
4748
if run != "" {
4849
return run
@@ -51,16 +52,16 @@ func (p *Params) GetPackage() string {
5152
return "."
5253
}
5354

54-
// GetBinaryName prepares binary name with GOPATH if it is set
55-
func (p *Params) createBinaryName() string {
55+
// generateBinaryName generates a new binary name for each rebuild, for preventing any sorts of conflicts
56+
func (p *Params) generateBinaryName() string {
5657
rand.Seed(time.Now().UnixNano())
5758
randName := rand.Int31n(999999)
58-
packageName := strings.Replace(p.GetPackage(), "/", "-", -1)
59+
packageName := strings.Replace(p.packagePath(), "/", "-", -1)
5960

60-
return fmt.Sprintf("%s-%s-%d", getBinaryNameRoot(), packageName, randName)
61+
return fmt.Sprintf("%s-%s-%d", generateBinaryPrefix(), packageName, randName)
6162
}
6263

63-
func getBinaryNameRoot() string {
64+
func generateBinaryPrefix() string {
6465
path := os.Getenv("GOPATH")
6566
if path != "" {
6667
return fmt.Sprintf("%s/bin/%s", path, binaryName)
@@ -93,20 +94,21 @@ func runCommand(name string, args ...string) (*exec.Cmd, error) {
9394
return cmd, nil
9495
}
9596

96-
// PrepareArgs filters the system parameters from package parameters
97-
// and returns Params instance
98-
func PrepareArgs(args []string) *Params {
97+
// ParseArgs extracts the application parameters from args and returns
98+
// Params instance with separated watcher and application parameters
99+
func ParseArgs(args []string) *Params {
99100

100101
params := NewParams()
101102

102-
// remove command
103+
// remove the command argument
103104
args = args[1:len(args)]
104105

105106
for i := 0; i < len(args); i++ {
106107
arg := args[i]
107108
arg = stripDash(arg)
108109

109-
if arg == "run" || arg == "watch" || arg == "watch-vendor" {
110+
if existIn(arg, watcherFlags) {
111+
// used for fetching the value of the given parameter
110112
if len(args) <= i+1 {
111113
log.Fatalf("missing parameter value: %s", arg)
112114
}
@@ -123,12 +125,13 @@ func PrepareArgs(args []string) *Params {
123125
params.Package = append(params.Package, args[i])
124126
}
125127

126-
params.CloneRun()
128+
params.cloneRunFlag()
127129

128130
return params
129131
}
130132

131-
// stripDash removes the dash chars and returns parameter name
133+
// stripDash removes the both single and double dash chars and returns
134+
// the actual parameter name
132135
func stripDash(arg string) string {
133136
if len(arg) > 1 {
134137
if arg[1] == '-' {
@@ -140,3 +143,21 @@ func stripDash(arg string) string {
140143

141144
return arg
142145
}
146+
147+
func existIn(search string, in []string) bool {
148+
for i := range in {
149+
if search == in[i] {
150+
return true
151+
}
152+
}
153+
154+
return false
155+
}
156+
157+
func removeFile(fileName string) {
158+
if fileName != "" {
159+
cmd := exec.Command("rm", fileName)
160+
cmd.Run()
161+
cmd.Wait()
162+
}
163+
}

common_test.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ func TestParamsClone(t *testing.T) {
1717
params := NewParams()
1818
params.Watcher["run"] = "statler"
1919

20-
params.CloneRun()
20+
params.cloneRunFlag()
2121
watch := params.Get("watch")
2222
if watch != "statler" {
2323
t.Error("Expected statler but got %s when watch param is not set", watch)
2424
}
2525

2626
params.Watcher["watch"] = "waldorf"
2727

28-
params.CloneRun()
28+
params.cloneRunFlag()
2929

3030
watch = params.Get("watch")
3131
if watch != "waldorf" {
@@ -37,7 +37,7 @@ func TestParamsClone(t *testing.T) {
3737
func TestPrepareArgs(t *testing.T) {
3838
args := []string{"watcher", "-run", "balcony", "-p", "11880", "--watch", "show", "--host", "localhost"}
3939

40-
params := PrepareArgs(args)
40+
params := ParseArgs(args)
4141
if len(params.Package) != 4 {
4242
t.Fatalf("Expected 2 parameters with their values in Package parameters but got %d", len(params.Package))
4343
}
@@ -86,3 +86,15 @@ func TestStripDash(t *testing.T) {
8686
}
8787

8888
}
89+
90+
func TestExistIn(t *testing.T) {
91+
input := []string{"a", "b", "c"}
92+
93+
if !existIn("c", input) {
94+
t.Errorf("expected true, got false")
95+
}
96+
97+
if existIn("d", input) {
98+
t.Errorf("expected false, got true")
99+
}
100+
}

run.go

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Watcher is a command line tool inspired by fresh (https://github.com/pilu/fresh) and used
1+
// Package watcher is a command line tool inspired by fresh (https://github.com/pilu/fresh) and used
22
// for watching .go file changes, and restarting the app in case of an update/delete/add operation.
33
// After you installed it, you can run your apps with their default parameters as:
44
// watcher -c config -p 7000 -h localhost
@@ -12,80 +12,75 @@ import (
1212
"github.com/fatih/color"
1313
)
1414

15-
// Runner listens change events and depending on that kills
16-
// the obsolete process, and runs the new one
15+
// Runner listens for the change events and depending on that kills
16+
// the obsolete process, and runs a new one
1717
type Runner struct {
18-
running bool
19-
start chan string
20-
done chan struct{}
21-
fileName string
22-
cmd *exec.Cmd
18+
start chan string
19+
done chan struct{}
20+
cmd *exec.Cmd
2321

2422
mu *sync.Mutex
2523
}
2624

2725
// NewRunner creates a new Runner instance and returns its pointer
2826
func NewRunner() *Runner {
2927
return &Runner{
30-
running: false,
31-
start: make(chan string),
32-
done: make(chan struct{}),
33-
mu: &sync.Mutex{},
28+
start: make(chan string),
29+
done: make(chan struct{}),
30+
mu: &sync.Mutex{},
3431
}
3532
}
3633

37-
// Init initializes runner with given parameters.
34+
// Run initializes runner with given parameters.
3835
func (r *Runner) Run(p *Params) {
3936
for fileName := range r.start {
4037

4138
color.Green("Running %s...\n", p.Get("run"))
4239

4340
cmd, err := runCommand(fileName, p.Package...)
4441
if err != nil {
45-
log.Printf("Could not run the go binary: %s", err)
42+
log.Printf("Could not run the go binary: %s \n", err)
43+
r.kill()
44+
4645
continue
4746
}
47+
48+
r.mu.Lock()
4849
r.cmd = cmd
50+
removeFile(fileName)
51+
r.mu.Unlock()
4952

50-
go func(name string) {
51-
r.mu.Lock()
52-
r.running = true
53-
r.fileName = name
54-
r.mu.Unlock()
55-
r.cmd.Wait()
56-
}(fileName)
53+
go func(cmd *exec.Cmd) {
54+
if err := cmd.Wait(); err != nil {
55+
log.Printf("process interrupted: %s \n", err)
56+
r.kill()
57+
}
58+
}(r.cmd)
5759
}
5860
}
5961

6062
// Restart kills the process, removes the old binary and
6163
// restarts the new process
6264
func (r *Runner) restart(fileName string) {
63-
if r.running {
64-
r.kill()
65-
r.removeFile()
66-
}
65+
r.kill()
6766

6867
r.start <- fileName
6968
}
7069

7170
func (r *Runner) kill() {
72-
pid := r.cmd.Process.Pid
73-
log.Printf("Killing PID %d \n", pid)
74-
r.cmd.Process.Kill()
75-
}
76-
77-
func (r *Runner) removeFile() {
78-
if r.fileName != "" {
79-
cmd := exec.Command("rm", r.fileName)
80-
cmd.Run()
81-
cmd.Wait()
71+
r.mu.Lock()
72+
defer r.mu.Unlock()
73+
if r.cmd != nil {
74+
pid := r.cmd.Process.Pid
75+
log.Printf("Killing PID %d \n", pid)
76+
r.cmd.Process.Kill()
77+
r.cmd = nil
8278
}
8379
}
8480

8581
func (r *Runner) Close() {
86-
r.kill()
87-
r.removeFile()
8882
close(r.start)
83+
r.kill()
8984
close(r.done)
9085
}
9186

0 commit comments

Comments
 (0)