Skip to content

Commit

Permalink
make it simple to use
Browse files Browse the repository at this point in the history
  • Loading branch information
codeskyblue committed Nov 15, 2019
1 parent 776b613 commit fe2525c
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 77 deletions.
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,38 @@ Works well on Mac, Linux, and should also works on Windows(not well tested).
go get -u -v github.com/codeskyblue/fswatch
```

## Quick start
### Step 1
Create a config file `.fsw.yml`, quickly generated by the following command.
## Simple way to use fswatch

Usage example

```
$ fswatch -v
3.0
$ fswtach --help
show help message ...
```

fswatch init
Watch file change event and trigger command

TODO: now only watch `*.go, *.c, *.py` files, watch depth = 0 (current directory)

```bash
$ fswatch sh -c "ls -l | wc -l"
fswatch >>> [] exec start: sh -c 'ls -l | wc -l'
8
fswatch >>> [] finish in 11.822873ms
fswatch >>> changed: fswatch.go
fswatch >>> delay: 100ms
fswatch >>> [] exec start: [sh -c ls -l | wc -l]
8
fswatch >>> [] finish in 13.606428ms
^Cfswatch >>> Catch signal interrupt!
fswatch >>> Kill all running ... Done
```

## Hard way to use fswatch (not recommend)
### Step 1
Create a config file `.fsw.yml`

config file example

Expand Down Expand Up @@ -53,8 +80,6 @@ fswatch >>> program exited: exit status 2
fswatch >>> finish in 145.499911ms
```



## You should know
### How fswatch kill process
fswatch send signal to all process when restart. (mac and linux killed by pgid, windows by taskkill)
Expand Down
162 changes: 91 additions & 71 deletions fswatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"bytes"
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"os"
Expand All @@ -21,6 +20,7 @@ import (

ignore "github.com/codeskyblue/dockerignore"
"github.com/codeskyblue/kexec"
"github.com/frioux/shellquote"
"github.com/fsnotify/fsnotify"
"github.com/gobuild/log"
"github.com/google/shlex"
Expand All @@ -33,7 +33,7 @@ const (
)

var (
VERSION = "2.3"
VERSION = "3.0"
)

var signalMaps = map[string]os.Signal{
Expand Down Expand Up @@ -106,7 +106,8 @@ type TriggerEvent struct {
}

func (this *TriggerEvent) Start() (waitC chan error) {
CPrintf(CGREEN, fmt.Sprintf("[%s] exec start: %v", this.Name, this.cmdArgs))
cmdString, _ := shellquote.Quote(this.cmdArgs)
CPrintf(CGREEN, fmt.Sprintf("[%s] exec start: %v", this.Name, cmdString))
startTime := time.Now()
cmd := kexec.Command(this.cmdArgs[0], this.cmdArgs[1:]...)
cmd.Stdin = os.Stdin
Expand Down Expand Up @@ -303,29 +304,6 @@ func genFWConfig() FWConfig {
return out
}

func ListAllDir(path string, depth int) (dirs []string, err error) {
baseNumSeps := strings.Count(path, string(os.PathSeparator))
err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
base := info.Name()
if base != "." && strings.HasPrefix(base, ".") { // ignore hidden dir
return filepath.SkipDir
}
if base == "node_modules" {
return filepath.SkipDir
}

pathDepth := strings.Count(path, string(os.PathSeparator)) - baseNumSeps
if pathDepth > depth {
return filepath.SkipDir
}
dirs = append(dirs, path)
}
return nil
})
return
}

func UniqStrings(ss []string) []string {
out := make([]string, 0, len(ss))
m := make(map[string]bool, len(ss))
Expand Down Expand Up @@ -376,7 +354,6 @@ func WatchPathAndChildren(w *fsnotify.Watcher, paths []string, depth int, visits
return err
}
log.Debug("Watch directory:", dir)
//log.Info("Watch directory:", dir)
visits[dir] = true
return nil
}
Expand All @@ -387,7 +364,7 @@ func WatchPathAndChildren(w *fsnotify.Watcher, paths []string, depth int, visits
}

watchDir(path)
dirs, er := ListAllDir(path, depth)
dirs, er := listAllDir(path, depth)
if er != nil {
err = er
log.Warnf("ERR list dir: %s, depth: %d, %v", path, depth, err)
Expand All @@ -401,6 +378,30 @@ func WatchPathAndChildren(w *fsnotify.Watcher, paths []string, depth int, visits
return err
}

func listAllDir(path string, depth int) (dirs []string, err error) {
baseNumSeps := strings.Count(path, string(os.PathSeparator))
err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
base := info.Name()
if base != "." && strings.HasPrefix(base, ".") { // ignore hidden dir
return filepath.SkipDir
}
if base == "node_modules" {
return filepath.SkipDir
}

pathDepth := strings.Count(path, string(os.PathSeparator)) - baseNumSeps
if pathDepth > depth {
return filepath.SkipDir
}

dirs = append(dirs, path)
}
return nil
})
return
}

func drainEvent(fwc FWConfig) (globalEventC chan FSEvent, wg *sync.WaitGroup, err error) {
globalEventC = make(chan FSEvent, 1)
wg = &sync.WaitGroup{}
Expand Down Expand Up @@ -504,59 +505,78 @@ func initFWConfig() {
fmt.Printf("Saved to %s\n", strconv.Quote(cfg))
}

func main() {
version := flag.Bool("version", false, "Show version")
configfile := flag.String("config", FWCONFIG_YAML, "Specify config file")
flag.Parse()
const helpMessage = `fswatch usage:
fswatch [OPTIONS]
fswatch [Sub commands]
if *version {
fmt.Println(VERSION)
return
}
OPTIONS:
-h, --help show this help message
-v, --version show version
subCmd := flag.Arg(0)
var fwc FWConfig
var err error
if subCmd == "" {
fwc, err = readFWConfig(*configfile, FWCONFIG_JSON)
if err == nil {
subCmd = "start"
} else {
subCmd = "init"
}
}
Examples:
fswatch ls -l # show files when current directory file changes
`

switch subCmd {
case "init":
initFWConfig()
case "start":
visits := make(map[string]bool)
fsw, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
func main() {
args := os.Args[1:]
if len(args) == 1 {
switch args[0] {
case "-h", "--help":
fmt.Print(helpMessage)
fallthrough
case "-v", "--version":
fmt.Println(VERSION)
return
}
}

err = WatchPathAndChildren(fsw, fwc.WatchPaths, fwc.WatchDepth, visits)
if err != nil {
log.Println(err)
}
fwc, err := readFWConfig(FWCONFIG_YAML, FWCONFIG_JSON)
if err != nil {
log.Fatal(err)
}

evtC, wg, err := drainEvent(fwc)
if len(args) > 0 {
command, _ := shellquote.Quote(args)
fwc.Triggers = []TriggerEvent{{
Pattens: []string{"**/*.go", "**/*.c", "**/*.py"},
Environ: map[string]string{
"DEBUG": "1",
},
Shell: false,
Command: command,
}}
fwc, err = fixFWConfig(fwc)
if err != nil {
log.Fatal(err)
}
}

sigOS := make(chan os.Signal, 1)
signal.Notify(sigOS, syscall.SIGINT)
signal.Notify(sigOS, syscall.SIGTERM)
visits := make(map[string]bool)
fswatcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}

go func() {
sig := <-sigOS
CPrintf(CPURPLE, "Catch signal %v!", sig)
close(evtC)
}()
go transformEvent(fsw, evtC)
wg.Wait()
CPrintf(CPURPLE, "Kill all running ... Done")
err = WatchPathAndChildren(fswatcher, fwc.WatchPaths, fwc.WatchDepth, visits)
if err != nil {
log.Println(err)
}

evtC, wg, err := drainEvent(fwc)
if err != nil {
log.Fatal(err)
}

sigOS := make(chan os.Signal, 1)
signal.Notify(sigOS, syscall.SIGINT)
signal.Notify(sigOS, syscall.SIGTERM)

go func() {
sig := <-sigOS
CPrintf(CPURPLE, "Catch signal %v!", sig)
close(evtC)
}()
go transformEvent(fswatcher, evtC)
wg.Wait()
CPrintf(CPURPLE, "Kill all running ... Done")
}

0 comments on commit fe2525c

Please sign in to comment.