@@ -5,7 +5,6 @@ package gaper
55import (
66 "fmt"
77 "os"
8- "os/exec"
98 "os/signal"
109 "path/filepath"
1110 "syscall"
@@ -23,6 +22,13 @@ var DefaultExtensions = []string{"go"}
2322// DefaultPoolInterval is the time in ms used by the watcher to wait between scans
2423var DefaultPoolInterval = 500
2524
25+ // No restart types
26+ var (
27+ NoRestartOnError = "error"
28+ NoRestartOnSuccess = "success"
29+ NoRestartOnExit = "exit"
30+ )
31+
2632var logger = NewLogger ("gaper" )
2733
2834// exit statuses
@@ -43,71 +49,53 @@ type Config struct {
4349 Extensions []string
4450 NoRestartOn string
4551 Verbose bool
46- ExitOnSIGINT bool
52+ WorkingDirectory string
4753}
4854
49- // Run in the gaper high level API
50- // It starts the whole gaper process watching for file changes or exit codes
55+ // Run starts the whole gaper process watching for file changes or exit codes
5156// and restarting the program
52- func Run (cfg * Config ) error { // nolint: gocyclo
53- var err error
54- logger .Verbose (cfg .Verbose )
57+ func Run (cfg * Config , chOSSiginal chan os.Signal ) error {
5558 logger .Debug ("Starting gaper" )
5659
57- if len (cfg .BuildPath ) == 0 {
58- cfg .BuildPath = DefaultBuildPath
59- }
60-
61- cfg .BuildArgs , err = parseInnerArgs (cfg .BuildArgs , cfg .BuildArgsMerged )
62- if err != nil {
63- return err
64- }
65-
66- cfg .ProgramArgs , err = parseInnerArgs (cfg .ProgramArgs , cfg .ProgramArgsMerged )
67- if err != nil {
60+ if err := setupConfig (cfg ); err != nil {
6861 return err
6962 }
7063
71- wd , err := os .Getwd ()
64+ builder := NewBuilder (cfg .BuildPath , cfg .BinName , cfg .WorkingDirectory , cfg .BuildArgs )
65+ runner := NewRunner (os .Stdout , os .Stderr , filepath .Join (cfg .WorkingDirectory , builder .Binary ()), cfg .ProgramArgs )
66+ watcher , err := NewWatcher (cfg .PollInterval , cfg .WatchItems , cfg .IgnoreItems , cfg .Extensions )
7267 if err != nil {
73- return err
74- }
75-
76- if len (cfg .WatchItems ) == 0 {
77- cfg .WatchItems = append (cfg .WatchItems , cfg .BuildPath )
68+ return fmt .Errorf ("watcher error: %v" , err )
7869 }
7970
80- builder := NewBuilder (cfg . BuildPath , cfg . BinName , wd , cfg . BuildArgs )
81- runner := NewRunner ( os . Stdout , os . Stderr , filepath . Join ( wd , builder . Binary ()), cfg . ProgramArgs )
71+ return run (cfg , chOSSiginal , builder , runner , watcher )
72+ }
8273
83- if err = builder .Build (); err != nil {
74+ func run (cfg * Config , chOSSiginal chan os.Signal , builder Builder , runner Runner , watcher Watcher ) error { // nolint: gocyclo
75+ if err := builder .Build (); err != nil {
8476 return fmt .Errorf ("build error: %v" , err )
8577 }
8678
87- shutdown (runner , cfg .ExitOnSIGINT )
79+ // listen for OS signals
80+ signal .Notify (chOSSiginal , os .Interrupt , syscall .SIGTERM )
8881
89- if _ , err = runner .Run (); err != nil {
82+ if _ , err : = runner .Run (); err != nil {
9083 return fmt .Errorf ("run error: %v" , err )
9184 }
9285
93- watcher , err := NewWatcher (cfg .PollInterval , cfg .WatchItems , cfg .IgnoreItems , cfg .Extensions )
94- if err != nil {
95- return fmt .Errorf ("watcher error: %v" , err )
96- }
97-
9886 // flag to know if an exit was caused by a restart from a file changing
9987 changeRestart := false
10088
10189 go watcher .Watch ()
10290 for {
10391 select {
104- case event := <- watcher .Events :
92+ case event := <- watcher .Events () :
10593 logger .Debug ("Detected new changed file: " , event )
10694 changeRestart = true
10795 if err := restart (builder , runner ); err != nil {
10896 return err
10997 }
110- case err := <- watcher .Errors :
98+ case err := <- watcher .Errors () :
11199 return fmt .Errorf ("error on watching files: %v" , err )
112100 case err := <- runner .Errors ():
113101 logger .Debug ("Detected program exit: " , err )
@@ -121,6 +109,14 @@ func Run(cfg *Config) error { // nolint: gocyclo
121109 if err = handleProgramExit (builder , runner , err , cfg .NoRestartOn ); err != nil {
122110 return err
123111 }
112+ case signal := <- chOSSiginal :
113+ logger .Debug ("Got signal: " , signal )
114+
115+ if err := runner .Kill (); err != nil {
116+ logger .Error ("Error killing: " , err )
117+ }
118+
119+ return fmt .Errorf ("OS signal: %v" , signal )
124120 default :
125121 time .Sleep (time .Duration (cfg .PollInterval ) * time .Millisecond )
126122 }
@@ -149,49 +145,53 @@ func restart(builder Builder, runner Runner) error {
149145}
150146
151147func handleProgramExit (builder Builder , runner Runner , err error , noRestartOn string ) error {
152- var exitStatus int
153- if exiterr , ok := err .(* exec.ExitError ); ok {
154- status , oks := exiterr .Sys ().(syscall.WaitStatus )
155- if ! oks {
156- return fmt .Errorf ("couldn't resolve exit status: %v" , err )
157- }
158-
159- exitStatus = status .ExitStatus ()
160- }
148+ exitStatus := runner .ExitStatus (err )
161149
162150 // if "error", an exit code of 0 will still restart.
163- if noRestartOn == "error" && exitStatus == exitStatusError {
151+ if noRestartOn == NoRestartOnError && exitStatus == exitStatusError {
164152 return nil
165153 }
166154
167155 // if "success", no restart only if exit code is 0.
168- if noRestartOn == "success" && exitStatus == exitStatusSuccess {
156+ if noRestartOn == NoRestartOnSuccess && exitStatus == exitStatusSuccess {
169157 return nil
170158 }
171159
172160 // if "exit", no restart regardless of exit code.
173- if noRestartOn == "exit" {
161+ if noRestartOn == NoRestartOnExit {
174162 return nil
175163 }
176164
177165 return restart (builder , runner )
178166}
179167
180- func shutdown (runner Runner , exitOnSIGINT bool ) {
181- c := make (chan os.Signal , 2 )
182- signal .Notify (c , os .Interrupt , syscall .SIGTERM )
183- go func () {
184- s := <- c
185- logger .Debug ("Got signal: " , s )
168+ func setupConfig (cfg * Config ) error {
169+ var err error
186170
187- if err := runner . Kill (); err != nil {
188- logger . Error ( "Error killing: " , err )
189- }
171+ if len ( cfg . BuildPath ) == 0 {
172+ cfg . BuildPath = DefaultBuildPath
173+ }
190174
191- if exitOnSIGINT {
192- os .Exit (0 )
193- }
194- }()
175+ cfg .BuildArgs , err = parseInnerArgs (cfg .BuildArgs , cfg .BuildArgsMerged )
176+ if err != nil {
177+ return err
178+ }
179+
180+ cfg .ProgramArgs , err = parseInnerArgs (cfg .ProgramArgs , cfg .ProgramArgsMerged )
181+ if err != nil {
182+ return err
183+ }
184+
185+ cfg .WorkingDirectory , err = os .Getwd ()
186+ if err != nil {
187+ return err
188+ }
189+
190+ if len (cfg .WatchItems ) == 0 {
191+ cfg .WatchItems = append (cfg .WatchItems , cfg .BuildPath )
192+ }
193+
194+ return nil
195195}
196196
197197func parseInnerArgs (args []string , argsm string ) ([]string , error ) {
0 commit comments