mirror of https://github.com/maxcnunes/gaper.git
Improve watching search and configuration by improving default ignore
Now it will ignore by default hidden files and folders, test files and vendor
This commit is contained in:
parent
586c834cb9
commit
a75eac8490
18
README.md
18
README.md
|
@ -49,9 +49,9 @@ GLOBAL OPTIONS:
|
||||||
--build-args value arguments used on building the program
|
--build-args value arguments used on building the program
|
||||||
--program-args value arguments used on executing the program
|
--program-args value arguments used on executing the program
|
||||||
--verbose turns on the verbose messages from gaper
|
--verbose turns on the verbose messages from gaper
|
||||||
|
--disable-default-ignore turns off default ignore for hidden files and folders, "*_test.go" files, and vendor folder
|
||||||
--watch value, -w value list of folders or files to watch for changes
|
--watch value, -w value list of folders or files to watch for changes
|
||||||
--ignore value, -i value list of folders or files to ignore for changes
|
--ignore value, -i value list of folders or files to ignore for changes
|
||||||
(always ignores all hidden files and directories)
|
|
||||||
--poll-interval value, -p value how often in milliseconds to poll watched files for changes (default: 500)
|
--poll-interval value, -p value how often in milliseconds to poll watched files for changes (default: 500)
|
||||||
--extensions value, -e value a comma-delimited list of file extensions to watch for changes (default: "go")
|
--extensions value, -e value a comma-delimited list of file extensions to watch for changes (default: "go")
|
||||||
--no-restart-on value, -n value don't automatically restart the supervised program if it ends:
|
--no-restart-on value, -n value don't automatically restart the supervised program if it ends:
|
||||||
|
@ -62,10 +62,26 @@ GLOBAL OPTIONS:
|
||||||
--version, -v print the version
|
--version, -v print the version
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Since in most projects there is no need to watch changes of:
|
||||||
|
|
||||||
|
* hidden files and folders
|
||||||
|
* test files (`*_test.go`)
|
||||||
|
* vendor folder
|
||||||
|
|
||||||
|
Gaper by default ignores those cases already. Although, if you need Gaper to watch those files anyway it is possible to disable this setting with `--disable-default-ignore` argument.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
Using all defaults provided by Gaper:
|
||||||
|
|
||||||
|
```
|
||||||
|
gaper
|
||||||
|
```
|
||||||
|
|
||||||
Ignore watch over all test files:
|
Ignore watch over all test files:
|
||||||
|
|
||||||
|
> no need for this in case you are not using `--disable-default-ignore`
|
||||||
|
|
||||||
```
|
```
|
||||||
--ignore './**/*_test.go'
|
--ignore './**/*_test.go'
|
||||||
```
|
```
|
||||||
|
|
|
@ -17,16 +17,17 @@ var logger = gaper.NewLogger("gaper")
|
||||||
func main() {
|
func main() {
|
||||||
parseArgs := func(c *cli.Context) *gaper.Config {
|
parseArgs := func(c *cli.Context) *gaper.Config {
|
||||||
return &gaper.Config{
|
return &gaper.Config{
|
||||||
BinName: c.String("bin-name"),
|
BinName: c.String("bin-name"),
|
||||||
BuildPath: c.String("build-path"),
|
BuildPath: c.String("build-path"),
|
||||||
BuildArgsMerged: c.String("build-args"),
|
BuildArgsMerged: c.String("build-args"),
|
||||||
ProgramArgsMerged: c.String("program-args"),
|
ProgramArgsMerged: c.String("program-args"),
|
||||||
Verbose: c.Bool("verbose"),
|
Verbose: c.Bool("verbose"),
|
||||||
WatchItems: c.StringSlice("watch"),
|
DisableDefaultIgnore: c.Bool("disable-default-ignore"),
|
||||||
IgnoreItems: c.StringSlice("ignore"),
|
WatchItems: c.StringSlice("watch"),
|
||||||
PollInterval: c.Int("poll-interval"),
|
IgnoreItems: c.StringSlice("ignore"),
|
||||||
Extensions: c.StringSlice("extensions"),
|
PollInterval: c.Int("poll-interval"),
|
||||||
NoRestartOn: c.String("no-restart-on"),
|
Extensions: c.StringSlice("extensions"),
|
||||||
|
NoRestartOn: c.String("no-restart-on"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +75,10 @@ func main() {
|
||||||
Name: "verbose",
|
Name: "verbose",
|
||||||
Usage: "turns on the verbose messages from gaper",
|
Usage: "turns on the verbose messages from gaper",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "disable-default-ignore",
|
||||||
|
Usage: "turns off default ignore for hidden files and folders, \"*_test.go\" files, and vendor folder",
|
||||||
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "watch, w",
|
Name: "watch, w",
|
||||||
Usage: "list of folders or files to watch for changes",
|
Usage: "list of folders or files to watch for changes",
|
||||||
|
|
37
gaper.go
37
gaper.go
|
@ -37,19 +37,20 @@ var exitStatusError = 1
|
||||||
|
|
||||||
// Config contains all settings supported by gaper
|
// Config contains all settings supported by gaper
|
||||||
type Config struct {
|
type Config struct {
|
||||||
BinName string
|
BinName string
|
||||||
BuildPath string
|
BuildPath string
|
||||||
BuildArgs []string
|
BuildArgs []string
|
||||||
BuildArgsMerged string
|
BuildArgsMerged string
|
||||||
ProgramArgs []string
|
ProgramArgs []string
|
||||||
ProgramArgsMerged string
|
ProgramArgsMerged string
|
||||||
WatchItems []string
|
WatchItems []string
|
||||||
IgnoreItems []string
|
IgnoreItems []string
|
||||||
PollInterval int
|
PollInterval int
|
||||||
Extensions []string
|
Extensions []string
|
||||||
NoRestartOn string
|
NoRestartOn string
|
||||||
Verbose bool
|
Verbose bool
|
||||||
WorkingDirectory string
|
DisableDefaultIgnore bool
|
||||||
|
WorkingDirectory string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts the whole gaper process watching for file changes or exit codes
|
// Run starts the whole gaper process watching for file changes or exit codes
|
||||||
|
@ -61,9 +62,17 @@ func Run(cfg *Config, chOSSiginal chan os.Signal) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wCfg := WatcherConfig{
|
||||||
|
DefaultIgnore: !cfg.DisableDefaultIgnore,
|
||||||
|
PollInterval: cfg.PollInterval,
|
||||||
|
WatchItems: cfg.WatchItems,
|
||||||
|
IgnoreItems: cfg.IgnoreItems,
|
||||||
|
Extensions: cfg.Extensions,
|
||||||
|
}
|
||||||
|
|
||||||
builder := NewBuilder(cfg.BuildPath, cfg.BinName, cfg.WorkingDirectory, cfg.BuildArgs)
|
builder := NewBuilder(cfg.BuildPath, cfg.BinName, cfg.WorkingDirectory, cfg.BuildArgs)
|
||||||
runner := NewRunner(os.Stdout, os.Stderr, filepath.Join(cfg.WorkingDirectory, builder.Binary()), cfg.ProgramArgs)
|
runner := NewRunner(os.Stdout, os.Stderr, filepath.Join(cfg.WorkingDirectory, builder.Binary()), cfg.ProgramArgs)
|
||||||
watcher, err := NewWatcher(cfg.PollInterval, cfg.WatchItems, cfg.IgnoreItems, cfg.Extensions)
|
watcher, err := NewWatcher(wCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("watcher error: %v", err)
|
return fmt.Errorf("watcher error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
61
watcher.go
61
watcher.go
|
@ -20,6 +20,7 @@ type Watcher interface {
|
||||||
|
|
||||||
// watcher is a interface for the watch process
|
// watcher is a interface for the watch process
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
|
defaultIgnore bool
|
||||||
pollInterval int
|
pollInterval int
|
||||||
watchItems map[string]bool
|
watchItems map[string]bool
|
||||||
ignoreItems map[string]bool
|
ignoreItems map[string]bool
|
||||||
|
@ -28,27 +29,36 @@ type watcher struct {
|
||||||
errors chan error
|
errors chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WatcherConfig defines the settings available for the watcher
|
||||||
|
type WatcherConfig struct {
|
||||||
|
DefaultIgnore bool
|
||||||
|
PollInterval int
|
||||||
|
WatchItems []string
|
||||||
|
IgnoreItems []string
|
||||||
|
Extensions []string
|
||||||
|
}
|
||||||
|
|
||||||
// NewWatcher creates a new watcher
|
// NewWatcher creates a new watcher
|
||||||
func NewWatcher(pollInterval int, watchItems []string, ignoreItems []string, extensions []string) (Watcher, error) {
|
func NewWatcher(cfg WatcherConfig) (Watcher, error) {
|
||||||
if pollInterval == 0 {
|
if cfg.PollInterval == 0 {
|
||||||
pollInterval = DefaultPoolInterval
|
cfg.PollInterval = DefaultPoolInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(extensions) == 0 {
|
if len(cfg.Extensions) == 0 {
|
||||||
extensions = DefaultExtensions
|
cfg.Extensions = DefaultExtensions
|
||||||
}
|
}
|
||||||
|
|
||||||
allowedExts := make(map[string]bool)
|
allowedExts := make(map[string]bool)
|
||||||
for _, ext := range extensions {
|
for _, ext := range cfg.Extensions {
|
||||||
allowedExts["."+ext] = true
|
allowedExts["."+ext] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
watchPaths, err := resolvePaths(watchItems, allowedExts)
|
watchPaths, err := resolvePaths(cfg.WatchItems, allowedExts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ignorePaths, err := resolvePaths(ignoreItems, allowedExts)
|
ignorePaths, err := resolvePaths(cfg.IgnoreItems, allowedExts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -58,7 +68,8 @@ func NewWatcher(pollInterval int, watchItems []string, ignoreItems []string, ext
|
||||||
return &watcher{
|
return &watcher{
|
||||||
events: make(chan string),
|
events: make(chan string),
|
||||||
errors: make(chan error),
|
errors: make(chan error),
|
||||||
pollInterval: pollInterval,
|
defaultIgnore: cfg.DefaultIgnore,
|
||||||
|
pollInterval: cfg.PollInterval,
|
||||||
watchItems: watchPaths,
|
watchItems: watchPaths,
|
||||||
ignoreItems: ignorePaths,
|
ignoreItems: ignorePaths,
|
||||||
allowedExtensions: allowedExts,
|
allowedExtensions: allowedExts,
|
||||||
|
@ -105,12 +116,7 @@ func (w *watcher) scanChange(watchPath string) (string, error) {
|
||||||
var fileChanged string
|
var fileChanged string
|
||||||
|
|
||||||
err := filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
|
||||||
// always ignore hidden files and directories
|
if w.ignoreFile(path, info) {
|
||||||
if dir := filepath.Base(path); dir[0] == '.' && dir != "." {
|
|
||||||
return skipFile(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ignored := w.ignoreItems[path]; ignored {
|
|
||||||
return skipFile(info)
|
return skipFile(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +136,31 @@ func (w *watcher) scanChange(watchPath string) (string, error) {
|
||||||
return fileChanged, nil
|
return fileChanged, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *watcher) ignoreFile(path string, info os.FileInfo) bool {
|
||||||
|
// check if preset ignore is enabled
|
||||||
|
if w.defaultIgnore {
|
||||||
|
// check for hidden files and directories
|
||||||
|
if name := info.Name(); name[0] == '.' && name != "." {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it is a Go testing file
|
||||||
|
if strings.HasSuffix(path, "_test.go") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it is the vendor folder
|
||||||
|
if info.IsDir() && info.Name() == "vendor" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ignored := w.ignoreItems[path]; ignored {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func resolvePaths(paths []string, extensions map[string]bool) (map[string]bool, error) {
|
func resolvePaths(paths []string, extensions map[string]bool) (map[string]bool, error) {
|
||||||
result := map[string]bool{}
|
result := map[string]bool{}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,14 @@ func TestWatcherDefaultValues(t *testing.T) {
|
||||||
var ignoreItems []string
|
var ignoreItems []string
|
||||||
var extensions []string
|
var extensions []string
|
||||||
|
|
||||||
wt, err := NewWatcher(pollInterval, watchItems, ignoreItems, extensions)
|
wCfg := WatcherConfig{
|
||||||
|
DefaultIgnore: true,
|
||||||
|
PollInterval: pollInterval,
|
||||||
|
WatchItems: watchItems,
|
||||||
|
IgnoreItems: ignoreItems,
|
||||||
|
Extensions: extensions,
|
||||||
|
}
|
||||||
|
wt, err := NewWatcher(wCfg)
|
||||||
|
|
||||||
expectedPath := "testdata/server"
|
expectedPath := "testdata/server"
|
||||||
if runtime.GOOS == OSWindows {
|
if runtime.GOOS == OSWindows {
|
||||||
|
@ -37,7 +44,14 @@ func TestWatcherGlobPath(t *testing.T) {
|
||||||
ignoreItems := []string{"./testdata/**/*_test.go"}
|
ignoreItems := []string{"./testdata/**/*_test.go"}
|
||||||
var extensions []string
|
var extensions []string
|
||||||
|
|
||||||
wt, err := NewWatcher(pollInterval, watchItems, ignoreItems, extensions)
|
wCfg := WatcherConfig{
|
||||||
|
DefaultIgnore: true,
|
||||||
|
PollInterval: pollInterval,
|
||||||
|
WatchItems: watchItems,
|
||||||
|
IgnoreItems: ignoreItems,
|
||||||
|
Extensions: extensions,
|
||||||
|
}
|
||||||
|
wt, err := NewWatcher(wCfg)
|
||||||
assert.Nil(t, err, "wacher error")
|
assert.Nil(t, err, "wacher error")
|
||||||
w := wt.(*watcher)
|
w := wt.(*watcher)
|
||||||
assert.Equal(t, map[string]bool{"testdata/server/main_test.go": true}, w.ignoreItems)
|
assert.Equal(t, map[string]bool{"testdata/server/main_test.go": true}, w.ignoreItems)
|
||||||
|
@ -49,7 +63,14 @@ func TestWatcherRemoveOverlapdPaths(t *testing.T) {
|
||||||
ignoreItems := []string{"./testdata/**/*", "./testdata/server"}
|
ignoreItems := []string{"./testdata/**/*", "./testdata/server"}
|
||||||
var extensions []string
|
var extensions []string
|
||||||
|
|
||||||
wt, err := NewWatcher(pollInterval, watchItems, ignoreItems, extensions)
|
wCfg := WatcherConfig{
|
||||||
|
DefaultIgnore: true,
|
||||||
|
PollInterval: pollInterval,
|
||||||
|
WatchItems: watchItems,
|
||||||
|
IgnoreItems: ignoreItems,
|
||||||
|
Extensions: extensions,
|
||||||
|
}
|
||||||
|
wt, err := NewWatcher(wCfg)
|
||||||
assert.Nil(t, err, "wacher error")
|
assert.Nil(t, err, "wacher error")
|
||||||
w := wt.(*watcher)
|
w := wt.(*watcher)
|
||||||
assert.Equal(t, map[string]bool{"./testdata/server": true}, w.ignoreItems)
|
assert.Equal(t, map[string]bool{"./testdata/server": true}, w.ignoreItems)
|
||||||
|
@ -69,7 +90,14 @@ func TestWatcherWatchChange(t *testing.T) {
|
||||||
ignoreItems := []string{testfile}
|
ignoreItems := []string{testfile}
|
||||||
extensions := []string{"go"}
|
extensions := []string{"go"}
|
||||||
|
|
||||||
w, err := NewWatcher(pollInterval, watchItems, ignoreItems, extensions)
|
wCfg := WatcherConfig{
|
||||||
|
DefaultIgnore: true,
|
||||||
|
PollInterval: pollInterval,
|
||||||
|
WatchItems: watchItems,
|
||||||
|
IgnoreItems: ignoreItems,
|
||||||
|
Extensions: extensions,
|
||||||
|
}
|
||||||
|
w, err := NewWatcher(wCfg)
|
||||||
assert.Nil(t, err, "wacher error")
|
assert.Nil(t, err, "wacher error")
|
||||||
|
|
||||||
go w.Watch()
|
go w.Watch()
|
||||||
|
|
Loading…
Reference in New Issue