From bcd5b6191802b3189ba536c5b7ce84d64e9e1553 Mon Sep 17 00:00:00 2001 From: asoseil Date: Sat, 11 Nov 2017 22:53:29 +0100 Subject: [PATCH 01/15] exec name collision --- exec.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/exec.go b/exec.go index 82984ce..bfdff8d 100644 --- a/exec.go +++ b/exec.go @@ -148,26 +148,26 @@ func (p *Project) command(stop <-chan bool, cmd Command) (string, string) { var stderr bytes.Buffer done := make(chan error) args := strings.Split(strings.Replace(strings.Replace(cmd.Command, "'", "", -1), "\"", "", -1), " ") - exec := exec.Command(args[0], args[1:]...) - exec.Dir = p.Path + ex := exec.Command(args[0], args[1:]...) + ex.Dir = p.Path // make cmd path if cmd.Path != "" { if strings.Contains(cmd.Path, p.Path) { - exec.Dir = cmd.Path + ex.Dir = cmd.Path } else { - exec.Dir = filepath.Join(p.Path, cmd.Path) + ex.Dir = filepath.Join(p.Path, cmd.Path) } } - exec.Stdout = &stdout - exec.Stderr = &stderr + ex.Stdout = &stdout + ex.Stderr = &stderr // Start command - exec.Start() - go func() { done <- exec.Wait() }() + ex.Start() + go func() { done <- ex.Wait() }() // Wait a result select { case <-stop: // Stop running command - exec.Process.Kill() + ex.Process.Kill() return "", "" case err := <-done: // Command completed From 7bedd4c32ada4f461ddb486f979702a5d67731de Mon Sep 17 00:00:00 2001 From: asoseil Date: Sat, 11 Nov 2017 22:54:25 +0100 Subject: [PATCH 02/15] method comments --- notify.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/notify.go b/notify.go index 19eac5a..eda781a 100644 --- a/notify.go +++ b/notify.go @@ -95,6 +95,7 @@ func (w *fsNotifyWatcher) Events() <-chan fsnotify.Event { return w.Watcher.Events } +// Walk fsnotify func (w *fsNotifyWatcher) Walk(path string, init bool) string { if err := w.Add(path); err != nil { return "" @@ -157,6 +158,7 @@ func (w *filePoller) Add(name string) error { return nil } +// Remove poller func (w *filePoller) remove(name string) error { if w.closed { return errPollerClosed @@ -184,6 +186,7 @@ func (w *filePoller) Events() <-chan fsnotify.Event { return w.events } +// Walk poller func (w *filePoller) Walk(path string, init bool) string { check := w.watches[path] if err := w.Add(path); err != nil { From 5002abf4796509b46a77529c0a82cd2101ee0409 Mon Sep 17 00:00:00 2001 From: asoseil Date: Sat, 11 Nov 2017 23:11:53 +0100 Subject: [PATCH 03/15] decreased complexity watch --- notify.go | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/notify.go b/notify.go index eda781a..2e203a1 100644 --- a/notify.go +++ b/notify.go @@ -227,17 +227,12 @@ func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{} defer f.Close() for { time.Sleep(w.interval) + fi, err := os.Stat(f.Name()) select { case <-chClose: logrus.Debugf("watch for %s closed", f.Name()) return - default: - } - - fi, err := os.Stat(f.Name()) - if err != nil { - // if we got an error here and lastFi is not set, we can presume that nothing has changed - // This should be safe since before `watch()` is called, a stat is performed, there is any error `watch` is not called + case err != nil: if lastFi == nil { continue } @@ -254,26 +249,17 @@ func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{} if err := w.sendErr(err, chClose); err != nil { return } - continue - } - - if lastFi == nil { + case lastFi == nil: if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Create, Name: f.Name()}, chClose); err != nil { return } lastFi = fi - continue - } - - if fi.Mode() != lastFi.Mode() { + case fi.Mode() != lastFi.Mode(): if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Chmod, Name: f.Name()}, chClose); err != nil { return } lastFi = fi - continue - } - - if fi.ModTime() != lastFi.ModTime() || fi.Size() != lastFi.Size() { + case fi.ModTime() != lastFi.ModTime() || fi.Size() != lastFi.Size(): if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Write, Name: f.Name()}, chClose); err != nil { return } From b98f93706e06084061c5505bf2bafbc3acde99a5 Mon Sep 17 00:00:00 2001 From: asoseil Date: Sat, 11 Nov 2017 23:25:27 +0100 Subject: [PATCH 04/15] decreased complexity watch --- notify.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/notify.go b/notify.go index 2e203a1..cd30b16 100644 --- a/notify.go +++ b/notify.go @@ -227,15 +227,16 @@ func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{} defer f.Close() for { time.Sleep(w.interval) - fi, err := os.Stat(f.Name()) select { case <-chClose: logrus.Debugf("watch for %s closed", f.Name()) return - case err != nil: - if lastFi == nil { - continue - } + default: + } + + fi, err := os.Stat(f.Name()) + switch { + case err != nil && lastFi != nil: // If it doesn't exist at this point, it must have been removed // no need to send the error here since this is a valid operation if os.IsNotExist(err) { @@ -243,12 +244,10 @@ func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{} return } lastFi = nil - continue } // at this point, send the error - if err := w.sendErr(err, chClose); err != nil { - return - } + w.sendErr(err, chClose) + return case lastFi == nil: if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Create, Name: f.Name()}, chClose); err != nil { return @@ -264,7 +263,6 @@ func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{} return } lastFi = fi - continue } } } From 369215a8f142a5f020409912141b431f3dce48c0 Mon Sep 17 00:00:00 2001 From: asoseil Date: Sun, 12 Nov 2017 11:15:57 +0100 Subject: [PATCH 05/15] #118 fixed --- cmd.go | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/cmd.go b/cmd.go index 69e7933..3fd1f4b 100644 --- a/cmd.go +++ b/cmd.go @@ -9,12 +9,9 @@ import ( // Tool options customizable, should be moved in Cmd type tool struct { - dir bool - status bool - name string - err string - cmd []string - options []string + dir, status bool + name, err string + cmd, options []string } // Cmds list of go commands @@ -56,13 +53,21 @@ func (r *realize) clean() error { } // Add a new project -func (r *realize) add(p *cli.Context) error { - path, err := filepath.Abs(p.String("path")) - if err != nil { - return err +func (r *realize) add(p *cli.Context) (err error) { + var path string + // #118 get relative and if not exist try to get abs + if _, err = os.Stat(p.String("path")); os.IsNotExist(err) { + // path doesn't exist + path, err = filepath.Abs(p.String("path")) + if err != nil { + return err + } + }else{ + path = filepath.Clean(p.String("path")) } + project := Project{ - Name: filepath.Base(filepath.Clean(p.String("path"))), + Name: filepath.Base(wdir()), Path: path, Cmds: Cmds{ Vet: Cmd{ From edb66c814f5184ee9b22a2a80f98838e6bbd0ddc Mon Sep 17 00:00:00 2001 From: asoseil Date: Sun, 12 Nov 2017 11:16:26 +0100 Subject: [PATCH 06/15] filelimit to int8 --- settings.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.go b/settings.go index a712153..7dd928e 100644 --- a/settings.go +++ b/settings.go @@ -32,7 +32,7 @@ const ( type Settings struct { file string Files `yaml:"files,omitempty" json:"files,omitempty"` - FileLimit int64 `yaml:"flimit,omitempty" json:"flimit,omitempty"` + FileLimit int8 `yaml:"flimit,omitempty" json:"flimit,omitempty"` Legacy Legacy `yaml:"legacy" json:"legacy"` Recovery bool `yaml:"recovery,omitempty" json:"recovery,omitempty"` } From 4664efe7351b33ec5916f3131be6836f4ec03074 Mon Sep 17 00:00:00 2001 From: asoseil Date: Sun, 12 Nov 2017 11:16:45 +0100 Subject: [PATCH 07/15] version updated --- realize.go | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/realize.go b/realize.go index 27fff9b..604062e 100644 --- a/realize.go +++ b/realize.go @@ -15,7 +15,7 @@ import ( ) const ( - version = "1.5.1r2" + version = "1.5.2" ) // New realize instance @@ -37,16 +37,6 @@ func main() { app := &cli.App{ Name: "Realize", Version: version, - Authors: []*cli.Author{ - { - Name: "Alessio Pracchia", - Email: "pracchia@hastega.it", - }, - { - Name: "Daniele Conventi", - Email: "conventi@hastega.it", - }, - }, Description: "Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths", Commands: []*cli.Command{ { @@ -54,7 +44,7 @@ func main() { Aliases: []string{"r"}, Description: "Start a toolchain on a project or a list of projects. If not exist a config file it creates a new one", Flags: []cli.Flag{ - &cli.StringFlag{Name: "path", Aliases: []string{"p"}, Value: wdir(), Usage: "Project base path"}, + &cli.StringFlag{Name: "path", Aliases: []string{"p"}, Value: ".", Usage: "Project base path"}, &cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: "", Usage: "Run a project by its name"}, &cli.BoolFlag{Name: "fmt", Aliases: []string{"f"}, Value: false, Usage: "Enable go fmt"}, &cli.BoolFlag{Name: "vet", Aliases: []string{"v"}, Value: false, Usage: "Enable go vet"}, @@ -172,7 +162,7 @@ func main() { if err != nil { return d.Err() } - r.Settings.FileLimit = val + r.Settings.FileLimit = int8(val) return nil }, }, @@ -1217,11 +1207,11 @@ func prefix(s string) string { if s != "" { return fmt.Sprint(yellow.bold("["), "REALIZE", yellow.bold("]"), " : ", s) } - return "" + return s } // Before is launched before each command -func before(*cli.Context) error { +func before(*cli.Context) (err error) { // custom log log.SetFlags(0) log.SetOutput(logWriter{}) @@ -1230,7 +1220,7 @@ func before(*cli.Context) error { if gopath == "" { return errors.New("$GOPATH isn't set properly") } - if err := os.Setenv("GOPATH", gopath); err != nil { + if err = os.Setenv("GOPATH", gopath); err != nil { return err } // new realize instance @@ -1239,11 +1229,11 @@ func before(*cli.Context) error { r.Settings.read(&r) // increase the file limit if r.Settings.FileLimit != 0 { - if err := r.Settings.flimit(); err != nil { + if err = r.Settings.flimit(); err != nil { return err } } - return nil + return } // Rewrite the layout of the log timestamp From 989c898dbf0e1964005b5eb5b43cf3875dc77d6c Mon Sep 17 00:00:00 2001 From: asoseil Date: Sun, 12 Nov 2017 15:25:57 +0100 Subject: [PATCH 08/15] decreased routines complexity --- watcher.go | 70 ++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/watcher.go b/watcher.go index 8c5cfe7..cdd3897 100644 --- a/watcher.go +++ b/watcher.go @@ -14,6 +14,7 @@ import ( "time" "github.com/fsnotify/fsnotify" + "reflect" ) var ( @@ -465,7 +466,6 @@ func (p *Project) stamp(t string, o BufferOut, msg string, stream string) { // Routines launches the toolchain run, build, install func (p *Project) routines(stop <-chan bool, watcher FileWatcher, path string) { var done bool - var install, build error go func() { for { select { @@ -475,45 +475,43 @@ func (p *Project) routines(stop <-chan bool, watcher FileWatcher, path string) { } } }() - if !done { - // before command - p.cmd(stop, "before", false) - } - if !done { - // Go supported tools - p.tool(stop, path) - // Prevent fake events on polling startup - p.init = true - } + invoke(done,p.cmd,stop,"before",false) + invoke(done,p.tool,stop,path) + // prevent init error on walk + p.init = true // prevent errors using realize without config with only run flag if p.Cmds.Run && !p.Cmds.Install.Status && !p.Cmds.Build.Status { p.Cmds.Install.Status = true } - if !done { - install = p.compile(stop, p.Cmds.Install) - } - if !done { - build = p.compile(stop, p.Cmds.Build) - } - if !done && (install == nil && build == nil) { - if p.Cmds.Run { - start := time.Now() - runner := make(chan bool, 1) - go func() { - log.Println(p.pname(p.Name, 1), ":", "Running..") - p.goRun(stop, runner) - }() - select { - case <-runner: - msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.regular("Started"), "in", magenta.regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s")) - out = BufferOut{Time: time.Now(), Text: "Started in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"} - p.stamp("log", out, msg, "") - case <-stop: - return - } + invoke(done,p.compile,stop,p.Cmds.Install) + invoke(done,p.compile,stop,p.Cmds.Build) + if !done && p.Cmds.Run { + start := time.Now() + runner := make(chan bool, 1) + go func() { + log.Println(p.pname(p.Name, 1), ":", "Running..") + p.goRun(stop, runner) + }() + select { + case <-runner: + msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.regular("Started"), "in", magenta.regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s")) + out = BufferOut{Time: time.Now(), Text: "Started in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"} + p.stamp("log", out, msg, "") + case <-stop: + return } } - if !done { - p.cmd(stop, "after", false) - } + invoke(done,p.cmd,stop,"after",false) } + +// Invoke is used to exec func from routines and check done +func invoke(done bool, fn interface{}, args ...interface{}) { + if !done { + v := reflect.ValueOf(fn) + rargs := make([]reflect.Value, len(args)) + for i, a := range args { + rargs[i] = reflect.ValueOf(a) + } + v.Call(rargs) + } +} \ No newline at end of file From a18fb33cd57755fc50eb3d0153ebf4be52432981 Mon Sep 17 00:00:00 2001 From: asoseil Date: Sun, 12 Nov 2017 22:42:21 +0100 Subject: [PATCH 09/15] bug fix --- cmd.go | 14 ++++----- exec.go | 4 +-- notify.go | 4 +-- realize.go | 6 ++-- server.go | 10 +++---- settings.go | 2 +- watcher.go | 83 +++++++++++++++++++++++++++-------------------------- 7 files changed, 63 insertions(+), 60 deletions(-) diff --git a/cmd.go b/cmd.go index 3fd1f4b..6dfa95c 100644 --- a/cmd.go +++ b/cmd.go @@ -9,9 +9,9 @@ import ( // Tool options customizable, should be moved in Cmd type tool struct { - dir, status bool - name, err string - cmd, options []string + name, err string + cmd, options []string + dir, status bool } // Cmds list of go commands @@ -29,11 +29,11 @@ type Cmds struct { // Cmd single command fields and options type Cmd struct { - Status bool `yaml:"status,omitempty" json:"status,omitempty"` Method string `yaml:"method,omitempty" json:"method,omitempty"` Args []string `yaml:"args,omitempty" json:"args,omitempty"` - method []string + Status bool `yaml:"status,omitempty" json:"status,omitempty"` tool bool + method []string name, startTxt, endTxt string } @@ -53,7 +53,7 @@ func (r *realize) clean() error { } // Add a new project -func (r *realize) add(p *cli.Context) (err error) { +func (r *realize) add(p *cli.Context) (err error) { var path string // #118 get relative and if not exist try to get abs if _, err = os.Stat(p.String("path")); os.IsNotExist(err) { @@ -62,7 +62,7 @@ func (r *realize) add(p *cli.Context) (err error) { if err != nil { return err } - }else{ + } else { path = filepath.Clean(p.String("path")) } diff --git a/exec.go b/exec.go index bfdff8d..3b5e6e5 100644 --- a/exec.go +++ b/exec.go @@ -206,7 +206,7 @@ func (p *Project) goTool(wg *sync.WaitGroup, stop <-chan bool, result chan<- too case <-stop: // Stop running command cmd.Process.Kill() - break + return case err := <-done: // Command completed if err != nil { @@ -214,7 +214,7 @@ func (p *Project) goTool(wg *sync.WaitGroup, stop <-chan bool, result chan<- too // send command result result <- tool } - break + return } } diff --git a/notify.go b/notify.go index cd30b16..47cb785 100644 --- a/notify.go +++ b/notify.go @@ -107,9 +107,8 @@ func (w *fsNotifyWatcher) Walk(path string, init bool) string { // All watches are stopped, removed, and the poller cannot be added to func (w *filePoller) Close() error { w.mu.Lock() - defer w.mu.Unlock() - if w.closed { + w.mu.Unlock() return nil } @@ -118,6 +117,7 @@ func (w *filePoller) Close() error { w.remove(name) delete(w.watches, name) } + w.mu.Unlock() return nil } diff --git a/realize.go b/realize.go index 604062e..7473e65 100644 --- a/realize.go +++ b/realize.go @@ -35,8 +35,8 @@ type realize struct { // Cli commands func main() { app := &cli.App{ - Name: "Realize", - Version: version, + Name: "Realize", + Version: version, Description: "Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths", Commands: []*cli.Command{ { @@ -162,7 +162,7 @@ func main() { if err != nil { return d.Err() } - r.Settings.FileLimit = int8(val) + r.Settings.FileLimit = int32(val) return nil }, }, diff --git a/server.go b/server.go index f5c6d29..4f6d535 100644 --- a/server.go +++ b/server.go @@ -26,16 +26,15 @@ type Server struct { parent *realize Status bool `yaml:"status" json:"status"` Open bool `yaml:"open" json:"open"` - Host string `yaml:"host" json:"host"` Port int `yaml:"port" json:"port"` + Host string `yaml:"host" json:"host"` } // Websocket projects -func (s *Server) projects(c echo.Context) error { +func (s *Server) projects(c echo.Context) (err error) { websocket.Handler(func(ws *websocket.Conn) { - defer ws.Close() msg, _ := json.Marshal(s.parent) - err := websocket.Message.Send(ws, string(msg)) + err = websocket.Message.Send(ws, string(msg)) go func() { for { select { @@ -51,7 +50,7 @@ func (s *Server) projects(c echo.Context) error { for { // Read text := "" - err := websocket.Message.Receive(ws, &text) + err = websocket.Message.Receive(ws, &text) if err != nil { break } else { @@ -62,6 +61,7 @@ func (s *Server) projects(c echo.Context) error { } } } + ws.Close() }).ServeHTTP(c.Response(), c.Request()) return nil } diff --git a/settings.go b/settings.go index 7dd928e..e941874 100644 --- a/settings.go +++ b/settings.go @@ -32,8 +32,8 @@ const ( type Settings struct { file string Files `yaml:"files,omitempty" json:"files,omitempty"` - FileLimit int8 `yaml:"flimit,omitempty" json:"flimit,omitempty"` Legacy Legacy `yaml:"legacy" json:"legacy"` + FileLimit int32 `yaml:"flimit,omitempty" json:"flimit,omitempty"` Recovery bool `yaml:"recovery,omitempty" json:"recovery,omitempty"` } diff --git a/watcher.go b/watcher.go index cdd3897..c26ead1 100644 --- a/watcher.go +++ b/watcher.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/fsnotify/fsnotify" "log" "math/big" "os" @@ -12,9 +13,6 @@ import ( "sync" "syscall" "time" - - "github.com/fsnotify/fsnotify" - "reflect" ) var ( @@ -55,12 +53,12 @@ type Project struct { parent *realize watcher FileWatcher init bool + Settings `yaml:"-" json:"-"` files, folders int64 name, lastFile string tools []tool paths []string lastTime time.Time - Settings `yaml:"-" json:"-"` Name string `yaml:"name" json:"name"` Path string `yaml:"path" json:"path"` Environment map[string]string `yaml:"environment,omitempty" json:"environment,omitempty"` @@ -350,10 +348,12 @@ func (p *Project) tool(stop <-chan bool, path string) error { result := make(chan tool) go func() { var wg sync.WaitGroup - wg.Add(len(p.tools)) for _, element := range p.tools { // no need a sequence, these commands can be asynchronous - go p.goTool(&wg, stop, result, path, element) + if element.status { + wg.Add(1) + go p.goTool(&wg, stop, result, path, element) + } } wg.Wait() close(done) @@ -466,6 +466,7 @@ func (p *Project) stamp(t string, o BufferOut, msg string, stream string) { // Routines launches the toolchain run, build, install func (p *Project) routines(stop <-chan bool, watcher FileWatcher, path string) { var done bool + var install, build error go func() { for { select { @@ -475,43 +476,45 @@ func (p *Project) routines(stop <-chan bool, watcher FileWatcher, path string) { } } }() - invoke(done,p.cmd,stop,"before",false) - invoke(done,p.tool,stop,path) - // prevent init error on walk - p.init = true + if !done { + // before command + p.cmd(stop, "before", false) + } + if !done { + // Go supported tools + p.tool(stop, path) + // Prevent fake events on polling startup + p.init = true + } // prevent errors using realize without config with only run flag if p.Cmds.Run && !p.Cmds.Install.Status && !p.Cmds.Build.Status { p.Cmds.Install.Status = true } - invoke(done,p.compile,stop,p.Cmds.Install) - invoke(done,p.compile,stop,p.Cmds.Build) - if !done && p.Cmds.Run { - start := time.Now() - runner := make(chan bool, 1) - go func() { - log.Println(p.pname(p.Name, 1), ":", "Running..") - p.goRun(stop, runner) - }() - select { - case <-runner: - msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.regular("Started"), "in", magenta.regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s")) - out = BufferOut{Time: time.Now(), Text: "Started in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"} - p.stamp("log", out, msg, "") - case <-stop: - return - } - } - invoke(done,p.cmd,stop,"after",false) -} - -// Invoke is used to exec func from routines and check done -func invoke(done bool, fn interface{}, args ...interface{}) { if !done { - v := reflect.ValueOf(fn) - rargs := make([]reflect.Value, len(args)) - for i, a := range args { - rargs[i] = reflect.ValueOf(a) - } - v.Call(rargs) + install = p.compile(stop, p.Cmds.Install) } -} \ No newline at end of file + if !done { + build = p.compile(stop, p.Cmds.Build) + } + if !done && (install == nil && build == nil) { + if p.Cmds.Run { + start := time.Now() + runner := make(chan bool, 1) + go func() { + log.Println(p.pname(p.Name, 1), ":", "Running..") + p.goRun(stop, runner) + }() + select { + case <-runner: + msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.regular("Started"), "in", magenta.regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s")) + out = BufferOut{Time: time.Now(), Text: "Started in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"} + p.stamp("log", out, msg, "") + case <-stop: + return + } + } + } + if !done { + p.cmd(stop, "after", false) + } +} From 47077a5d42c367e71c335075b2a43a28ac74c75e Mon Sep 17 00:00:00 2001 From: asoseil Date: Mon, 13 Nov 2017 22:51:05 +0100 Subject: [PATCH 10/15] #121 fixed --- server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server.go b/server.go index 4f6d535..1051964 100644 --- a/server.go +++ b/server.go @@ -70,6 +70,8 @@ func (s *Server) projects(c echo.Context) (err error) { func (s *Server) start(p *cli.Context) (err error) { if p.Bool("server") { s.parent.Server.Status = p.Bool("server") + } + if p.Bool("open"){ s.parent.Server.Open = true } From 527d454786d2ce6493e725725d03aabbbe522df2 Mon Sep 17 00:00:00 2001 From: asoseil Date: Mon, 13 Nov 2017 22:51:45 +0100 Subject: [PATCH 11/15] server flag fixed --- server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.go b/server.go index 1051964..5a3944b 100644 --- a/server.go +++ b/server.go @@ -69,7 +69,7 @@ func (s *Server) projects(c echo.Context) (err error) { // Start the web server func (s *Server) start(p *cli.Context) (err error) { if p.Bool("server") { - s.parent.Server.Status = p.Bool("server") + s.parent.Server.Status = true } if p.Bool("open"){ s.parent.Server.Open = true From f3fc26299420b1e07eaad06f1ecde181a7a119a4 Mon Sep 17 00:00:00 2001 From: asoseil Date: Mon, 13 Nov 2017 22:55:11 +0100 Subject: [PATCH 12/15] realize start alias fixed --- realize.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/realize.go b/realize.go index 7473e65..5e33324 100644 --- a/realize.go +++ b/realize.go @@ -41,7 +41,7 @@ func main() { Commands: []*cli.Command{ { Name: "start", - Aliases: []string{"r"}, + Aliases: []string{"s"}, Description: "Start a toolchain on a project or a list of projects. If not exist a config file it creates a new one", Flags: []cli.Flag{ &cli.StringFlag{Name: "path", Aliases: []string{"p"}, Value: ".", Usage: "Project base path"}, From 331b181b698210a34cb529f3b542455f8136d395 Mon Sep 17 00:00:00 2001 From: asoseil Date: Tue, 14 Nov 2017 16:12:37 +0100 Subject: [PATCH 13/15] #119 fixed workflow --- cmd.go | 6 +- exec.go | 3 + watcher.go | 162 +++++++++++++++++++++++++++++------------------------ 3 files changed, 96 insertions(+), 75 deletions(-) diff --git a/cmd.go b/cmd.go index 6dfa95c..6ae1fb8 100644 --- a/cmd.go +++ b/cmd.go @@ -9,9 +9,9 @@ import ( // Tool options customizable, should be moved in Cmd type tool struct { - name, err string - cmd, options []string - dir, status bool + name, err, out string + cmd, options []string + dir, status bool } // Cmds list of go commands diff --git a/exec.go b/exec.go index 3b5e6e5..de21a32 100644 --- a/exec.go +++ b/exec.go @@ -39,6 +39,7 @@ func (p *Project) goCompile(stop <-chan bool, method []string, args []string) (s if err != nil { return stderr.String(), err } + return "", nil } return "", nil } @@ -213,6 +214,8 @@ func (p *Project) goTool(wg *sync.WaitGroup, stop <-chan bool, result chan<- too tool.err = stderr.String() + out.String() // send command result result <- tool + } else { + tool.out = out.String() } return } diff --git a/watcher.go b/watcher.go index c26ead1..d62674c 100644 --- a/watcher.go +++ b/watcher.go @@ -118,16 +118,27 @@ L: select { case event := <-p.watcher.Events(): if time.Now().Truncate(time.Second).After(p.lastTime) || event.Name != p.lastFile { + // event time + eventTime := time.Now() + // file extension + ext := ext(event.Name) + if ext == "" { + ext = "DIR" + } + // change message + msg = fmt.Sprintln(p.pname(p.Name, 4), ":", magenta.bold(strings.ToUpper(ext)), "changed", magenta.bold(event.Name)) + out = BufferOut{Time: time.Now(), Text: ext + " changed " + event.Name} + // switch event type switch event.Op { case fsnotify.Chmod: case fsnotify.Remove: - ext := ext(event.Name) + p.watcher.Remove(event.Name) if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) { close(stop) stop = make(chan bool) - p.changed(event, stop) // stop + p.stamp("log", out, msg, "") + go p.routines(stop, p.watcher, "") } - p.watcher.Remove(event.Name) default: file, err := os.Stat(event.Name) if err != nil { @@ -136,15 +147,20 @@ L: if file.IsDir() { filepath.Walk(event.Name, p.walk) } else if file.Size() > 0 { + // used only for test and debug if p.parent.Settings.Recovery { log.Println(event) } - ext := ext(event.Name) if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) { // change watched - close(stop) - stop = make(chan bool) - p.changed(event, stop) + // check if a file is still writing #119 + if event.Op != fsnotify.Write || eventTime.Truncate(time.Millisecond).After(file.ModTime().Truncate(time.Millisecond)) { + close(stop) + stop = make(chan bool) + // stop and start again + p.stamp("log", out, msg, "") + go p.routines(stop, p.watcher, event.Name) + } } p.lastTime = time.Now().Truncate(time.Second) p.lastFile = event.Name @@ -189,24 +205,12 @@ func (p *Project) config(r *realize) { if len(p.Cmds.Fmt.Args) == 0 { p.Cmds.Fmt.Args = []string{"-s", "-w", "-e", "./"} } - p.tools = append(p.tools, tool{ - status: p.Cmds.Fix.Status, - cmd: replace([]string{"go fix"}, p.Cmds.Fix.Method), - options: split([]string{}, p.Cmds.Fix.Args), - name: "Fix", - }) p.tools = append(p.tools, tool{ status: p.Cmds.Clean.Status, cmd: replace([]string{"go clean"}, p.Cmds.Clean.Method), options: split([]string{}, p.Cmds.Clean.Args), name: "Clean", }) - p.tools = append(p.tools, tool{ - status: p.Cmds.Fmt.Status, - cmd: replace([]string{"gofmt"}, p.Cmds.Fmt.Method), - options: split([]string{}, p.Cmds.Fmt.Args), - name: "Fmt", - }) p.tools = append(p.tools, tool{ status: p.Cmds.Generate.Status, cmd: replace([]string{"go", "generate"}, p.Cmds.Generate.Method), @@ -215,11 +219,16 @@ func (p *Project) config(r *realize) { dir: true, }) p.tools = append(p.tools, tool{ - status: p.Cmds.Test.Status, - cmd: replace([]string{"go", "test"}, p.Cmds.Test.Method), - options: split([]string{}, p.Cmds.Test.Args), - name: "Test", - dir: true, + status: p.Cmds.Fix.Status, + cmd: replace([]string{"go fix"}, p.Cmds.Fix.Method), + options: split([]string{}, p.Cmds.Fix.Args), + name: "Fix", + }) + p.tools = append(p.tools, tool{ + status: p.Cmds.Fmt.Status, + cmd: replace([]string{"gofmt"}, p.Cmds.Fmt.Method), + options: split([]string{}, p.Cmds.Fmt.Args), + name: "Fmt", }) p.tools = append(p.tools, tool{ status: p.Cmds.Vet.Status, @@ -228,6 +237,13 @@ func (p *Project) config(r *realize) { name: "Vet", dir: true, }) + p.tools = append(p.tools, tool{ + status: p.Cmds.Test.Status, + cmd: replace([]string{"go", "test"}, p.Cmds.Test.Method), + options: split([]string{}, p.Cmds.Test.Args), + name: "Test", + dir: true, + }) p.Cmds.Install = Cmd{ Status: p.Cmds.Install.Status, Args: append([]string{}, p.Cmds.Install.Args...), @@ -291,10 +307,11 @@ func (p *Project) cmd(stop <-chan bool, flag string, global bool) { // Compile is used for run and display the result of a compiling func (p *Project) compile(stop <-chan bool, cmd Cmd) error { if cmd.Status { - start := time.Now() + var start time.Time channel := make(chan Result) go func() { log.Println(p.pname(p.Name, 1), ":", cmd.startTxt) + start = time.Now() stream, err := p.goCompile(stop, cmd.method, cmd.Args) if stream != msgStop { channel <- Result{stream, err} @@ -352,7 +369,7 @@ func (p *Project) tool(stop <-chan bool, path string) error { // no need a sequence, these commands can be asynchronous if element.status { wg.Add(1) - go p.goTool(&wg, stop, result, path, element) + p.goTool(&wg, stop, result, path, element) } } wg.Wait() @@ -362,9 +379,15 @@ func (p *Project) tool(stop <-chan bool, path string) error { for { select { case tool := <-result: - msg = fmt.Sprintln(p.pname(p.Name, 2), ":", red.bold(tool.name), red.regular("there are some errors in"), ":", magenta.bold(path)) - buff := BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: tool.name, Stream: tool.err} - p.stamp("error", buff, msg, tool.err) + if tool.err != "" { + msg = fmt.Sprintln(p.pname(p.Name, 2), ":", red.bold(tool.name), red.regular("there are some errors in"), ":", magenta.bold(path)) + buff := BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: tool.name, Stream: tool.err} + p.stamp("error", buff, msg, tool.err) + } else if tool.out != "" { + msg = fmt.Sprintln(p.pname(p.Name, 3), ":", red.bold(tool.name), red.regular("outputs"), ":", blue.bold(path)) + buff := BufferOut{Time: time.Now(), Text: "outputs", Path: path, Type: tool.name, Stream: tool.out} + p.stamp("out", buff, msg, tool.out) + } case <-done: break loop case <-stop: @@ -375,19 +398,6 @@ func (p *Project) tool(stop <-chan bool, path string) error { return nil } -// Changed detect a file/directory change -func (p *Project) changed(event fsnotify.Event, stop chan bool) { - e := ext(event.Name) - if e == "" { - e = "DIR" - } - msg = fmt.Sprintln(p.pname(p.Name, 4), ":", magenta.bold(strings.ToUpper(e)), "changed", magenta.bold(event.Name)) - out = BufferOut{Time: time.Now(), Text: ext(event.Name) + " changed " + event.Name} - p.stamp("log", out, msg, "") - //stop running process - go p.routines(stop, p.watcher, event.Name) -} - // Watch the files tree of a project func (p *Project) walk(path string, info os.FileInfo, err error) error { for _, v := range p.Watcher.Ignore { @@ -476,45 +486,53 @@ func (p *Project) routines(stop <-chan bool, watcher FileWatcher, path string) { } } }() - if !done { - // before command - p.cmd(stop, "before", false) + if done { + return } - if !done { - // Go supported tools - p.tool(stop, path) - // Prevent fake events on polling startup - p.init = true + // before command + p.cmd(stop, "before", false) + if done { + return } + // Go supported tools + p.tool(stop, path) + // Prevent fake events on polling startup + p.init = true // prevent errors using realize without config with only run flag if p.Cmds.Run && !p.Cmds.Install.Status && !p.Cmds.Build.Status { p.Cmds.Install.Status = true } - if !done { - install = p.compile(stop, p.Cmds.Install) + if done { + return } - if !done { - build = p.compile(stop, p.Cmds.Build) + install = p.compile(stop, p.Cmds.Install) + if done { + return } - if !done && (install == nil && build == nil) { - if p.Cmds.Run { - start := time.Now() - runner := make(chan bool, 1) - go func() { - log.Println(p.pname(p.Name, 1), ":", "Running..") - p.goRun(stop, runner) - }() - select { - case <-runner: - msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.regular("Started"), "in", magenta.regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s")) - out = BufferOut{Time: time.Now(), Text: "Started in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"} - p.stamp("log", out, msg, "") - case <-stop: - return - } + build = p.compile(stop, p.Cmds.Build) + if done { + return + } + if install == nil && build == nil && p.Cmds.Run { + var start time.Time + runner := make(chan bool, 1) + go func() { + log.Println(p.pname(p.Name, 1), ":", "Running..") + start = time.Now() + p.goRun(stop, runner) + }() + select { + case <-runner: + msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.regular("Started"), "in", magenta.regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s")) + out = BufferOut{Time: time.Now(), Text: "Started in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"} + p.stamp("log", out, msg, "") + case <-stop: + return } } - if !done { - p.cmd(stop, "after", false) + if done { + return } + p.cmd(stop, "after", false) + } From 49b574813c12be61e78033a9980e0b4cd02bd0e2 Mon Sep 17 00:00:00 2001 From: asoseil Date: Tue, 14 Nov 2017 16:12:56 +0100 Subject: [PATCH 14/15] fmt --- server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.go b/server.go index 5a3944b..98fd7ed 100644 --- a/server.go +++ b/server.go @@ -71,7 +71,7 @@ func (s *Server) start(p *cli.Context) (err error) { if p.Bool("server") { s.parent.Server.Status = true } - if p.Bool("open"){ + if p.Bool("open") { s.parent.Server.Open = true } From cf97995848e4e50cf94b3dfda6cf30e57c89ccc5 Mon Sep 17 00:00:00 2001 From: asoseil Date: Tue, 14 Nov 2017 19:17:34 +0100 Subject: [PATCH 15/15] #118 fixed --- cmd.go | 21 ++++++++------------- exec.go | 8 +++++--- watcher.go | 6 ------ 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/cmd.go b/cmd.go index 6ae1fb8..8a71296 100644 --- a/cmd.go +++ b/cmd.go @@ -43,6 +43,8 @@ func (r *realize) clean() error { arr := r.Schema for key, val := range arr { if _, err := duplicates(val, arr[key+1:]); err != nil { + // path validation + r.Schema = append(arr[:key], arr[key+1:]...) break } @@ -54,21 +56,14 @@ func (r *realize) clean() error { // Add a new project func (r *realize) add(p *cli.Context) (err error) { - var path string - // #118 get relative and if not exist try to get abs - if _, err = os.Stat(p.String("path")); os.IsNotExist(err) { - // path doesn't exist - path, err = filepath.Abs(p.String("path")) - if err != nil { - return err - } - } else { - path = filepath.Clean(p.String("path")) + // project init + name := filepath.Base(p.String("path")) + if name == "." { + name = filepath.Base(wdir()) } - project := Project{ - Name: filepath.Base(wdir()), - Path: path, + Name: name, + Path: p.String("path"), Cmds: Cmds{ Vet: Cmd{ Status: p.Bool("vet"), diff --git a/exec.go b/exec.go index de21a32..09c4615 100644 --- a/exec.go +++ b/exec.go @@ -48,7 +48,6 @@ func (p *Project) goCompile(stop <-chan bool, method []string, args []string) (s func (p *Project) goRun(stop <-chan bool, runner chan bool) { var build *exec.Cmd var args []string - // custom error pattern isErrorText := func(string) bool { return false @@ -73,13 +72,16 @@ func (p *Project) goRun(stop <-chan bool, runner chan bool) { } gobin := os.Getenv("GOBIN") - path := filepath.Join(gobin, p.name) + dirPath := filepath.Base(p.Path) + if p.Path == "." { + dirPath = filepath.Base(wdir()) + } + path := filepath.Join(gobin, dirPath) if _, err := os.Stat(path); err == nil { build = exec.Command(path, args...) } else if _, err := os.Stat(path + extWindows); err == nil { build = exec.Command(path+extWindows, args...) } else { - path := filepath.Join(p.Path, p.name) if _, err = os.Stat(path); err == nil { build = exec.Command(path, args...) } else if _, err = os.Stat(path + extWindows); err == nil { diff --git a/watcher.go b/watcher.go index d62674c..a53ba20 100644 --- a/watcher.go +++ b/watcher.go @@ -187,12 +187,6 @@ func (p *Project) err(err error) { // Config project init func (p *Project) config(r *realize) { - // validate project path, if invalid get wdir or clean current - if !filepath.IsAbs(p.Path) { - p.Path = wdir() - } else { - p.Path = filepath.Clean(p.Path) - } // get basepath name p.name = filepath.Base(p.Path) // env variables