diff --git a/.travis.yml b/.travis.yml index fb887a5..f126179 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: go -go: ['1.16.x', '1.17.x', '1.18.x', '1.19.x'] +go: ['1.18.x', '1.19.x'] go_import_path: github.com/teamwork/reload jobs: include: diff --git a/README.md b/README.md index cad2c8a..e813de4 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ Lightweight automatic reloading of Go processes. -After initialisation with `reload.Do()` any changes to the binary (and *only* -the binary) will restart the process. For example: +After initialisation with `reload.Do()` any changes to the binary will restart +the process. For example: ```go func main() { @@ -39,6 +39,9 @@ func main() { } ``` +This will run reloadTpl if any file in the "tpl" directory changes. The process +won't be restarted. + You can also use `reload.Exec()` to manually restart your process without calling `reload.Do()`. diff --git a/go.mod b/go.mod index af17381..cde20c9 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,7 @@ module github.com/teamwork/reload -go 1.16 +go 1.18 require github.com/fsnotify/fsnotify v1.6.0 + +require golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect diff --git a/reload.go b/reload.go index 80c3a6d..b1d0c7a 100644 --- a/reload.go +++ b/reload.go @@ -1,29 +1,35 @@ // Package reload offers lightweight automatic reloading of running processes. // -// After initialisation with reload.Do() any changes to the binary will restart +// After initialisation with [reload.Do] any changes to the binary will restart // the process. // // Example: // -// go func() { -// err := reload.Do(log.Printf) -// if err != nil { -// panic(err) -// } -// }() +// go func() { +// err := reload.Do(log.Printf) +// if err != nil { +// panic(err) +// } +// }() +// +// The current process is replaced with [syscall.Exec]; this means that defered +// functions and signal handlers will not be run. You can use [OnExec] to run +// some code before the process is restarted. +// +// # Additional directories // // A list of additional directories to watch can be added: // -// go func() { -// err := reload.Do(log.Printf, reload.Dir("tpl", reloadTpl) -// if err != nil { -// panic(err) -// } -// }() +// go func() { +// err := reload.Do(log.Printf, reload.Dir("tpl", reloadTpl) +// if err != nil { +// panic(err) +// } +// }() // -// Note that this package won't prevent race conditions (e.g. when assigning to -// a global templates variable). You'll need to use sync.RWMutex yourself. -package reload // import "github.com/teamwork/reload" +// This will run reloadTpl if any file in the "tpl" directory changes. The +// process won't be restarted. +package reload import ( "fmt" @@ -64,7 +70,7 @@ func Dir(path string, cb func()) dir { return dir{path, cb} } // // The error return will only return initialisation errors. Once initialized it // will use the log function to print errors, rather than return. -func Do(log func(string, ...interface{}), additional ...dir) error { +func Do(log func(string, ...any), additional ...dir) error { watcher, err := fsnotify.NewWatcher() if err != nil { return fmt.Errorf("reload.Do: cannot setup watcher: %w", err) @@ -109,12 +115,17 @@ func Do(log func(string, ...interface{}), additional ...dir) error { go func() { for { select { - case err := <-watcher.Errors: - if err != nil { - log("reload error: %v", err) + case err, ok := <-watcher.Errors: + if !ok { + return } - case event := <-watcher.Events: - trigger := (event.Op&fsnotify.Write == fsnotify.Write) || (event.Op&fsnotify.Create == fsnotify.Create) + log("reload error: %v", err) + case event, ok := <-watcher.Events: + if !ok { + return + } + + trigger := event.Has(fsnotify.Write) || event.Has(fsnotify.Create) if !trigger { continue } @@ -151,6 +162,9 @@ func Do(log func(string, ...interface{}), additional ...dir) error { return nil } +// OnExec is called before the current process is replaced. +var OnExec func() + // Exec replaces the current process with a new copy of itself. func Exec() { execName := binSelf @@ -166,6 +180,10 @@ func Exec() { closeWatcher() } + if OnExec != nil { + OnExec() + } + err := syscall.Exec(execName, append([]string{execName}, os.Args[1:]...), os.Environ()) if err != nil { panic(fmt.Sprintf("cannot restart: %v", err))