diff --git a/README.md b/README.md index 33b358e..db26e91 100644 --- a/README.md +++ b/README.md @@ -14,27 +14,15 @@ A Go build system with file watchers, output streams and live reload. Run, build ![Preview](http://i.imgur.com/dJbNZjt.gif) -#### What's new - -##### v1.2 -- [x] Windows support -- [x] Go generate support -- [x] Bugs fix -- [x] Web panel errors log improved -- [x] Refactoring -- [x] Web panel edit settings, partial - #### Features -- Build, Install, Test, Fmt and Run at the same time -- Live reload on file changes (re-build, re-install and re-run) -- Watch custom paths -- Watch specific file extensions -- Multiple projects support -- Output streams -- Execution times - Highly customizable -- Fast run +- Build, Install, Test, Fmt, Generate and Run at the same time +- Live reload on file changes (re-build, re-install...) +- Watch custom paths and specific file extensions +- Support for multiple projects +- Output streams and error logs (Watch them in console or save them on a file) +- Web Panel (Watch all projects, edit the config settings, download each type of log) #### Installation and usage @@ -186,12 +174,16 @@ A Go build system with file watchers, output streams and live reload. Run, build errors: false // saves the errors of the project in a file ``` -#### Next release +#### Next features, in progress... -##### v1.3 -- [ ] Web panel edit settings, full support +- [ ] Web panel - edit settings (full support) +- [ ] Web panel - logs download +- [ ] Schedule - reload a project after a specific time +- [ ] Easy dependencies - automatically resolve the project dependencies +- [ ] Import license - retrieve the license for each imported library - [ ] Tests + #### Contacts - Chat with us [Gitter](https://gitter.im/tockins/realize) diff --git a/realize.go b/realize.go index 221a91b..364f86e 100644 --- a/realize.go +++ b/realize.go @@ -12,12 +12,11 @@ import ( const ( name = "Realize" - version = "1.2" + version = "1.2.1" description = "A Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths" config = "realize.yaml" output = "outputs.log" log = "logs.log" - err = "errors.log" host = "localhost" port = 5000 server = true diff --git a/settings/flimit.go b/settings/flimit.go index 4aa15d5..f1ccfbf 100644 --- a/settings/flimit.go +++ b/settings/flimit.go @@ -1,4 +1,3 @@ - // +build !windows package settings @@ -12,6 +11,6 @@ func (s *Settings) Flimit() { rLimit.Cur = s.Config.Flimit err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) if err != nil { - s.Fatal("Error Setting Rlimit", err) + s.Fatal(err, "Error setting rlimit") } } diff --git a/settings/io.go b/settings/io.go index 02b9e6d..9619aea 100644 --- a/settings/io.go +++ b/settings/io.go @@ -3,6 +3,7 @@ package settings import ( "io/ioutil" "os" + "path/filepath" ) // Scan return a byte stream of a given file @@ -23,7 +24,13 @@ func (s Settings) Write(name string, data []byte) error { } // Create a new file and return its pointer -func (s Settings) Create(file string) *os.File { +func (s Settings) Create(path string, name string) *os.File { + var file string + if _, err := os.Stat(".realize/"); err == nil { + file = filepath.Join(path, ".realize/", name) + } else { + file = filepath.Join(path, name) + } out, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0655) s.Validate(err) return out diff --git a/settings/settings.go b/settings/settings.go index d956e21..57f671e 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -2,6 +2,7 @@ package settings import ( "gopkg.in/yaml.v2" + "os" ) type Settings struct { @@ -28,9 +29,13 @@ type Resources struct { Log string `yaml:"log" json:"log"` } -// Read from the configuration file +// Read from config file func (s *Settings) Read(out interface{}) error { - content, err := s.Stream(s.Resources.Config) + localConfigPath := s.Resources.Config + if _, err := os.Stat(".realize/" + s.Resources.Config); err == nil { + localConfigPath = ".realize/" + s.Resources.Config + } + content, err := s.Stream(localConfigPath) if err == nil { err = yaml.Unmarshal(content, out) return err @@ -39,10 +44,15 @@ func (s *Settings) Read(out interface{}) error { } // Record create and unmarshal the yaml config file -func (h *Settings) Record(out interface{}) error { +func (s *Settings) Record(out interface{}) error { y, err := yaml.Marshal(out) if err != nil { return err } - return h.Write(h.Resources.Config, y) + if _, err := os.Stat(".realize/"); os.IsNotExist(err) { + if err = os.Mkdir(".realize/", 0770); err != nil { + return s.Write(s.Resources.Config, y) + } + } + return s.Write(".realize/"+s.Resources.Config, y) } diff --git a/settings/utils.go b/settings/utils.go index a030559..e67c502 100644 --- a/settings/utils.go +++ b/settings/utils.go @@ -14,14 +14,14 @@ func (s Settings) Wdir() string { func (s Settings) Validate(err error) error { if err != nil { - s.Fatal("", err) + s.Fatal(err, "") } return nil } -func (s Settings) Fatal(msg string, err error) { - if msg != "" { - log.Fatal(s.Red.Regular(msg), err.Error()) +func (s Settings) Fatal(err error, msg ...interface{}) { + if len(msg) > 0 { + log.Fatalln(s.Red.Regular(msg...), err.Error()) } - log.Fatal(err.Error()) + log.Fatalln(err.Error()) } diff --git a/watcher/exec.go b/watcher/exec.go index bf489fa..4523819 100644 --- a/watcher/exec.go +++ b/watcher/exec.go @@ -16,21 +16,34 @@ import ( func (p *Project) goRun(channel chan bool, runner chan bool, wr *sync.WaitGroup) error { var build *exec.Cmd - if len(p.Params) != 0 { - var params []string - for _, param := range p.Params { - arr := strings.Fields(param) - params = append(params, arr...) - } - build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path)), params...) - } else { - build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path))) + var params []string + var path = "" + + for _, param := range p.Params { + arr := strings.Fields(param) + params = append(params, arr...) + } + if _, err := os.Stat(filepath.Join(p.base, p.path)); err == nil { + path = filepath.Join(p.base, p.path) + } + if _, err := os.Stat(filepath.Join(p.base, p.path+".exe")); err == nil { + path = filepath.Join(p.base, p.path+".exe") + } + + if path != "" { + build = exec.Command(path, params...) + } else { + if _, err := os.Stat(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path))); err == nil { + build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path)), params...) + } else { + p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Can't run a not compiled project"}) + p.Fatal(err, "Can't run a not compiled project", ":") + } } - build.Dir = p.base defer func() { if err := build.Process.Kill(); err != nil { p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Failed to stop: " + err.Error()}) - p.Fatal("Failed to stop:", err) + p.Fatal(err, "Failed to stop", ":") } p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Ended"}) log.Println(p.pname(p.Name, 2), ":", p.Red.Regular("Ended")) @@ -68,11 +81,10 @@ func (p *Project) goRun(channel chan bool, runner chan bool, wr *sync.WaitGroup) log.Println(p.pname(p.Name, 3), ":", p.Blue.Regular(output.Text())) } if p.File.Streams { - path := filepath.Join(p.base, p.Resources.Output) - f := p.Create(path) + f := p.Create(p.base, p.parent.Resources.Output) t := time.Now() if _, err := f.WriteString(t.Format("2006-01-02 15:04:05") + " : " + output.Text() + "\r\n"); err != nil { - p.Fatal("", err) + p.Fatal(err, "") } } } diff --git a/watcher/watcher.go b/watcher/watcher.go index a8d82a4..4ba78ff 100644 --- a/watcher/watcher.go +++ b/watcher/watcher.go @@ -26,7 +26,6 @@ func (p *Project) watching() { defer func() { wg.Done() }() - if err != nil { log.Fatalln(p.pname(p.Name, 2), ":", p.Red.Bold(err.Error())) return @@ -85,51 +84,47 @@ func (p *Project) watching() { } // Install calls an implementation of the "go install" -func (p *Project) install(channel chan bool, wr *sync.WaitGroup) { - defer func() { - p.sync() - }() +func (p *Project) install() { if p.Bin { - log.Println(p.pname(p.Name, 1), ":", "Installing..") start := time.Now() + log.Println(p.pname(p.Name, 1), ":", "Installing..") if stream, err := p.goInstall(); err != nil { msg := fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Install"), p.Red.Regular(err.Error())) out := BufferOut{Time: time.Now(), Text: err.Error(), Type: "Go Install", Stream: stream} p.print("error", out, msg, stream) - wr.Done() } else { msg := fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Regular("Installed")+" after", p.Magenta.Regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s")) out := BufferOut{Time: time.Now(), Text: "Installed"} p.print("log", out, msg, stream) - - if p.Run { - runner := make(chan bool, 1) - log.Println(p.pname(p.Name, 1), ":", "Running..") - start = time.Now() - go p.goRun(channel, runner, wr) - for { - select { - case <-runner: - msg := fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Regular("Has been run")+" after", p.Magenta.Regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s")) - out := BufferOut{Time: time.Now(), Text: "Has been run"} - p.print("log", out, msg, stream) - return - } - } - } } + p.sync() } return } +func (p *Project) run(channel chan bool, wr *sync.WaitGroup) { + if p.Run { + start := time.Now() + runner := make(chan bool, 1) + log.Println(p.pname(p.Name, 1), ":", "Running..") + go p.goRun(channel, runner, wr) + for { + select { + case <-runner: + msg := fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Regular("Has been run")+" after", p.Magenta.Regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s")) + out := BufferOut{Time: time.Now(), Text: "Has been run"} + p.print("log", out, msg, "") + return + } + } + } +} + // Build calls an implementation of the "go build" func (p *Project) build() { - defer func() { - p.sync() - }() if p.Build { - log.Println(p.pname(p.Name, 1), ":", "Building..") start := time.Now() + log.Println(p.pname(p.Name, 1), ":", "Building..") if stream, err := p.goBuild(); err != nil { msg := fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Build"), p.Red.Regular(err.Error())) out := BufferOut{Time: time.Now(), Text: err.Error(), Type: "Go Build", Stream: stream} @@ -139,6 +134,7 @@ func (p *Project) build() { out := BufferOut{Time: time.Now(), Text: "Builded"} p.print("log", out, msg, stream) } + p.sync() } return } @@ -282,9 +278,10 @@ func (p *Project) ignore(str string) bool { // Routines launches the following methods: run, build, install func (p *Project) routines(channel chan bool, wr *sync.WaitGroup) { + p.install() + p.build() wr.Add(1) - go p.build() - go p.install(channel, wr) + go p.run(channel, wr) wr.Wait() } @@ -315,31 +312,28 @@ func (p *Project) print(t string, o BufferOut, msg string, stream string) { case "out": p.Buffer.StdOut = append(p.Buffer.StdOut, o) if p.File.Streams { - path := filepath.Join(p.base, p.Resources.Output) - f := p.Create(path) + f := p.Create(p.base, p.parent.Resources.Output) t := time.Now() if _, err := f.WriteString(t.Format("2006-01-02 15:04:05") + " : " + o.Text + "\r\n"); err != nil { - p.Fatal("", err) + p.Fatal(err, "") } } case "log": p.Buffer.StdLog = append(p.Buffer.StdLog, o) if p.File.Logs { - path := filepath.Join(p.base, p.Resources.Log) - f := p.Create(path) + f := p.Create(p.base, p.parent.Resources.Log) t := time.Now() if _, err := f.WriteString(t.Format("2006-01-02 15:04:05") + " : " + o.Text + "\r\n"); err != nil { - p.Fatal("", err) + p.Fatal(err, "") } } case "error": p.Buffer.StdErr = append(p.Buffer.StdErr, o) if p.File.Errors { - path := filepath.Join(p.base, p.Resources.Log) - f := p.Create(path) + f := p.Create(p.base, p.parent.Resources.Log) t := time.Now() if _, err := f.WriteString(t.Format("2006-01-02 15:04:05") + " : " + o.Text + "\r\n"); err != nil { - p.Fatal("", err) + p.Fatal(err, "") } }