commit
7f02f5256c
28
cmd.go
28
cmd.go
@ -9,12 +9,9 @@ import (
|
|||||||
|
|
||||||
// Tool options customizable, should be moved in Cmd
|
// Tool options customizable, should be moved in Cmd
|
||||||
type tool struct {
|
type tool struct {
|
||||||
dir bool
|
name, err, out string
|
||||||
status bool
|
cmd, options []string
|
||||||
name string
|
dir, status bool
|
||||||
err string
|
|
||||||
cmd []string
|
|
||||||
options []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cmds list of go commands
|
// Cmds list of go commands
|
||||||
@ -32,11 +29,11 @@ type Cmds struct {
|
|||||||
|
|
||||||
// Cmd single command fields and options
|
// Cmd single command fields and options
|
||||||
type Cmd struct {
|
type Cmd struct {
|
||||||
Status bool `yaml:"status,omitempty" json:"status,omitempty"`
|
|
||||||
Method string `yaml:"method,omitempty" json:"method,omitempty"`
|
Method string `yaml:"method,omitempty" json:"method,omitempty"`
|
||||||
Args []string `yaml:"args,omitempty" json:"args,omitempty"`
|
Args []string `yaml:"args,omitempty" json:"args,omitempty"`
|
||||||
method []string
|
Status bool `yaml:"status,omitempty" json:"status,omitempty"`
|
||||||
tool bool
|
tool bool
|
||||||
|
method []string
|
||||||
name, startTxt, endTxt string
|
name, startTxt, endTxt string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +43,8 @@ func (r *realize) clean() error {
|
|||||||
arr := r.Schema
|
arr := r.Schema
|
||||||
for key, val := range arr {
|
for key, val := range arr {
|
||||||
if _, err := duplicates(val, arr[key+1:]); err != nil {
|
if _, err := duplicates(val, arr[key+1:]); err != nil {
|
||||||
|
// path validation
|
||||||
|
|
||||||
r.Schema = append(arr[:key], arr[key+1:]...)
|
r.Schema = append(arr[:key], arr[key+1:]...)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -56,14 +55,15 @@ func (r *realize) clean() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a new project
|
// Add a new project
|
||||||
func (r *realize) add(p *cli.Context) error {
|
func (r *realize) add(p *cli.Context) (err error) {
|
||||||
path, err := filepath.Abs(p.String("path"))
|
// project init
|
||||||
if err != nil {
|
name := filepath.Base(p.String("path"))
|
||||||
return err
|
if name == "." {
|
||||||
|
name = filepath.Base(wdir())
|
||||||
}
|
}
|
||||||
project := Project{
|
project := Project{
|
||||||
Name: filepath.Base(filepath.Clean(p.String("path"))),
|
Name: name,
|
||||||
Path: path,
|
Path: p.String("path"),
|
||||||
Cmds: Cmds{
|
Cmds: Cmds{
|
||||||
Vet: Cmd{
|
Vet: Cmd{
|
||||||
Status: p.Bool("vet"),
|
Status: p.Bool("vet"),
|
||||||
|
33
exec.go
33
exec.go
@ -39,6 +39,7 @@ func (p *Project) goCompile(stop <-chan bool, method []string, args []string) (s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return stderr.String(), err
|
return stderr.String(), err
|
||||||
}
|
}
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
@ -47,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) {
|
func (p *Project) goRun(stop <-chan bool, runner chan bool) {
|
||||||
var build *exec.Cmd
|
var build *exec.Cmd
|
||||||
var args []string
|
var args []string
|
||||||
|
|
||||||
// custom error pattern
|
// custom error pattern
|
||||||
isErrorText := func(string) bool {
|
isErrorText := func(string) bool {
|
||||||
return false
|
return false
|
||||||
@ -72,13 +72,16 @@ func (p *Project) goRun(stop <-chan bool, runner chan bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gobin := os.Getenv("GOBIN")
|
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 {
|
if _, err := os.Stat(path); err == nil {
|
||||||
build = exec.Command(path, args...)
|
build = exec.Command(path, args...)
|
||||||
} else if _, err := os.Stat(path + extWindows); err == nil {
|
} else if _, err := os.Stat(path + extWindows); err == nil {
|
||||||
build = exec.Command(path+extWindows, args...)
|
build = exec.Command(path+extWindows, args...)
|
||||||
} else {
|
} else {
|
||||||
path := filepath.Join(p.Path, p.name)
|
|
||||||
if _, err = os.Stat(path); err == nil {
|
if _, err = os.Stat(path); err == nil {
|
||||||
build = exec.Command(path, args...)
|
build = exec.Command(path, args...)
|
||||||
} else if _, err = os.Stat(path + extWindows); err == nil {
|
} else if _, err = os.Stat(path + extWindows); err == nil {
|
||||||
@ -148,26 +151,26 @@ func (p *Project) command(stop <-chan bool, cmd Command) (string, string) {
|
|||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
done := make(chan error)
|
done := make(chan error)
|
||||||
args := strings.Split(strings.Replace(strings.Replace(cmd.Command, "'", "", -1), "\"", "", -1), " ")
|
args := strings.Split(strings.Replace(strings.Replace(cmd.Command, "'", "", -1), "\"", "", -1), " ")
|
||||||
exec := exec.Command(args[0], args[1:]...)
|
ex := exec.Command(args[0], args[1:]...)
|
||||||
exec.Dir = p.Path
|
ex.Dir = p.Path
|
||||||
// make cmd path
|
// make cmd path
|
||||||
if cmd.Path != "" {
|
if cmd.Path != "" {
|
||||||
if strings.Contains(cmd.Path, p.Path) {
|
if strings.Contains(cmd.Path, p.Path) {
|
||||||
exec.Dir = cmd.Path
|
ex.Dir = cmd.Path
|
||||||
} else {
|
} else {
|
||||||
exec.Dir = filepath.Join(p.Path, cmd.Path)
|
ex.Dir = filepath.Join(p.Path, cmd.Path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exec.Stdout = &stdout
|
ex.Stdout = &stdout
|
||||||
exec.Stderr = &stderr
|
ex.Stderr = &stderr
|
||||||
// Start command
|
// Start command
|
||||||
exec.Start()
|
ex.Start()
|
||||||
go func() { done <- exec.Wait() }()
|
go func() { done <- ex.Wait() }()
|
||||||
// Wait a result
|
// Wait a result
|
||||||
select {
|
select {
|
||||||
case <-stop:
|
case <-stop:
|
||||||
// Stop running command
|
// Stop running command
|
||||||
exec.Process.Kill()
|
ex.Process.Kill()
|
||||||
return "", ""
|
return "", ""
|
||||||
case err := <-done:
|
case err := <-done:
|
||||||
// Command completed
|
// Command completed
|
||||||
@ -206,15 +209,17 @@ func (p *Project) goTool(wg *sync.WaitGroup, stop <-chan bool, result chan<- too
|
|||||||
case <-stop:
|
case <-stop:
|
||||||
// Stop running command
|
// Stop running command
|
||||||
cmd.Process.Kill()
|
cmd.Process.Kill()
|
||||||
break
|
return
|
||||||
case err := <-done:
|
case err := <-done:
|
||||||
// Command completed
|
// Command completed
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tool.err = stderr.String() + out.String()
|
tool.err = stderr.String() + out.String()
|
||||||
// send command result
|
// send command result
|
||||||
result <- tool
|
result <- tool
|
||||||
|
} else {
|
||||||
|
tool.out = out.String()
|
||||||
}
|
}
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
37
notify.go
37
notify.go
@ -95,6 +95,7 @@ func (w *fsNotifyWatcher) Events() <-chan fsnotify.Event {
|
|||||||
return w.Watcher.Events
|
return w.Watcher.Events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Walk fsnotify
|
||||||
func (w *fsNotifyWatcher) Walk(path string, init bool) string {
|
func (w *fsNotifyWatcher) Walk(path string, init bool) string {
|
||||||
if err := w.Add(path); err != nil {
|
if err := w.Add(path); err != nil {
|
||||||
return ""
|
return ""
|
||||||
@ -106,9 +107,8 @@ func (w *fsNotifyWatcher) Walk(path string, init bool) string {
|
|||||||
// All watches are stopped, removed, and the poller cannot be added to
|
// All watches are stopped, removed, and the poller cannot be added to
|
||||||
func (w *filePoller) Close() error {
|
func (w *filePoller) Close() error {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
if w.closed {
|
if w.closed {
|
||||||
|
w.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +117,7 @@ func (w *filePoller) Close() error {
|
|||||||
w.remove(name)
|
w.remove(name)
|
||||||
delete(w.watches, name)
|
delete(w.watches, name)
|
||||||
}
|
}
|
||||||
|
w.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +158,7 @@ func (w *filePoller) Add(name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove poller
|
||||||
func (w *filePoller) remove(name string) error {
|
func (w *filePoller) remove(name string) error {
|
||||||
if w.closed {
|
if w.closed {
|
||||||
return errPollerClosed
|
return errPollerClosed
|
||||||
@ -184,6 +186,7 @@ func (w *filePoller) Events() <-chan fsnotify.Event {
|
|||||||
return w.events
|
return w.events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Walk poller
|
||||||
func (w *filePoller) Walk(path string, init bool) string {
|
func (w *filePoller) Walk(path string, init bool) string {
|
||||||
check := w.watches[path]
|
check := w.watches[path]
|
||||||
if err := w.Add(path); err != nil {
|
if err := w.Add(path); err != nil {
|
||||||
@ -232,12 +235,8 @@ func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{}
|
|||||||
}
|
}
|
||||||
|
|
||||||
fi, err := os.Stat(f.Name())
|
fi, err := os.Stat(f.Name())
|
||||||
if err != nil {
|
switch {
|
||||||
// if we got an error here and lastFi is not set, we can presume that nothing has changed
|
case err != nil && lastFi != nil:
|
||||||
// This should be safe since before `watch()` is called, a stat is performed, there is any error `watch` is not called
|
|
||||||
if lastFi == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// If it doesn't exist at this point, it must have been removed
|
// 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
|
// no need to send the error here since this is a valid operation
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@ -245,37 +244,25 @@ func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{}
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
lastFi = nil
|
lastFi = nil
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
// at this point, send the error
|
// at this point, send the error
|
||||||
if err := w.sendErr(err, chClose); err != nil {
|
w.sendErr(err, chClose)
|
||||||
return
|
return
|
||||||
}
|
case lastFi == nil:
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if lastFi == nil {
|
|
||||||
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Create, Name: f.Name()}, chClose); err != nil {
|
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Create, Name: f.Name()}, chClose); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lastFi = fi
|
lastFi = fi
|
||||||
continue
|
case fi.Mode() != lastFi.Mode():
|
||||||
}
|
|
||||||
|
|
||||||
if fi.Mode() != lastFi.Mode() {
|
|
||||||
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Chmod, Name: f.Name()}, chClose); err != nil {
|
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Chmod, Name: f.Name()}, chClose); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lastFi = fi
|
lastFi = fi
|
||||||
continue
|
case fi.ModTime() != lastFi.ModTime() || fi.Size() != lastFi.Size():
|
||||||
}
|
|
||||||
|
|
||||||
if fi.ModTime() != lastFi.ModTime() || fi.Size() != lastFi.Size() {
|
|
||||||
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Write, Name: f.Name()}, chClose); err != nil {
|
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Write, Name: f.Name()}, chClose); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lastFi = fi
|
lastFi = fi
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
realize.go
32
realize.go
@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
version = "1.5.1r2"
|
version = "1.5.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// New realize instance
|
// New realize instance
|
||||||
@ -35,26 +35,16 @@ type realize struct {
|
|||||||
// Cli commands
|
// Cli commands
|
||||||
func main() {
|
func main() {
|
||||||
app := &cli.App{
|
app := &cli.App{
|
||||||
Name: "Realize",
|
Name: "Realize",
|
||||||
Version: version,
|
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",
|
Description: "Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "start",
|
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",
|
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{
|
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.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: "fmt", Aliases: []string{"f"}, Value: false, Usage: "Enable go fmt"},
|
||||||
&cli.BoolFlag{Name: "vet", Aliases: []string{"v"}, Value: false, Usage: "Enable go vet"},
|
&cli.BoolFlag{Name: "vet", Aliases: []string{"v"}, Value: false, Usage: "Enable go vet"},
|
||||||
@ -172,7 +162,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Err()
|
return d.Err()
|
||||||
}
|
}
|
||||||
r.Settings.FileLimit = val
|
r.Settings.FileLimit = int32(val)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1217,11 +1207,11 @@ func prefix(s string) string {
|
|||||||
if s != "" {
|
if s != "" {
|
||||||
return fmt.Sprint(yellow.bold("["), "REALIZE", yellow.bold("]"), " : ", s)
|
return fmt.Sprint(yellow.bold("["), "REALIZE", yellow.bold("]"), " : ", s)
|
||||||
}
|
}
|
||||||
return ""
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before is launched before each command
|
// Before is launched before each command
|
||||||
func before(*cli.Context) error {
|
func before(*cli.Context) (err error) {
|
||||||
// custom log
|
// custom log
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
log.SetOutput(logWriter{})
|
log.SetOutput(logWriter{})
|
||||||
@ -1230,7 +1220,7 @@ func before(*cli.Context) error {
|
|||||||
if gopath == "" {
|
if gopath == "" {
|
||||||
return errors.New("$GOPATH isn't set properly")
|
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
|
return err
|
||||||
}
|
}
|
||||||
// new realize instance
|
// new realize instance
|
||||||
@ -1239,11 +1229,11 @@ func before(*cli.Context) error {
|
|||||||
r.Settings.read(&r)
|
r.Settings.read(&r)
|
||||||
// increase the file limit
|
// increase the file limit
|
||||||
if r.Settings.FileLimit != 0 {
|
if r.Settings.FileLimit != 0 {
|
||||||
if err := r.Settings.flimit(); err != nil {
|
if err = r.Settings.flimit(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite the layout of the log timestamp
|
// Rewrite the layout of the log timestamp
|
||||||
|
14
server.go
14
server.go
@ -26,16 +26,15 @@ type Server struct {
|
|||||||
parent *realize
|
parent *realize
|
||||||
Status bool `yaml:"status" json:"status"`
|
Status bool `yaml:"status" json:"status"`
|
||||||
Open bool `yaml:"open" json:"open"`
|
Open bool `yaml:"open" json:"open"`
|
||||||
Host string `yaml:"host" json:"host"`
|
|
||||||
Port int `yaml:"port" json:"port"`
|
Port int `yaml:"port" json:"port"`
|
||||||
|
Host string `yaml:"host" json:"host"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Websocket projects
|
// 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) {
|
websocket.Handler(func(ws *websocket.Conn) {
|
||||||
defer ws.Close()
|
|
||||||
msg, _ := json.Marshal(s.parent)
|
msg, _ := json.Marshal(s.parent)
|
||||||
err := websocket.Message.Send(ws, string(msg))
|
err = websocket.Message.Send(ws, string(msg))
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -51,7 +50,7 @@ func (s *Server) projects(c echo.Context) error {
|
|||||||
for {
|
for {
|
||||||
// Read
|
// Read
|
||||||
text := ""
|
text := ""
|
||||||
err := websocket.Message.Receive(ws, &text)
|
err = websocket.Message.Receive(ws, &text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
@ -62,6 +61,7 @@ func (s *Server) projects(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ws.Close()
|
||||||
}).ServeHTTP(c.Response(), c.Request())
|
}).ServeHTTP(c.Response(), c.Request())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -69,7 +69,9 @@ func (s *Server) projects(c echo.Context) error {
|
|||||||
// Start the web server
|
// Start the web server
|
||||||
func (s *Server) start(p *cli.Context) (err error) {
|
func (s *Server) start(p *cli.Context) (err error) {
|
||||||
if p.Bool("server") {
|
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
|
s.parent.Server.Open = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ const (
|
|||||||
type Settings struct {
|
type Settings struct {
|
||||||
file string
|
file string
|
||||||
Files `yaml:"files,omitempty" json:"files,omitempty"`
|
Files `yaml:"files,omitempty" json:"files,omitempty"`
|
||||||
FileLimit int64 `yaml:"flimit,omitempty" json:"flimit,omitempty"`
|
|
||||||
Legacy Legacy `yaml:"legacy" json:"legacy"`
|
Legacy Legacy `yaml:"legacy" json:"legacy"`
|
||||||
|
FileLimit int32 `yaml:"flimit,omitempty" json:"flimit,omitempty"`
|
||||||
Recovery bool `yaml:"recovery,omitempty" json:"recovery,omitempty"`
|
Recovery bool `yaml:"recovery,omitempty" json:"recovery,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
177
watcher.go
177
watcher.go
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
@ -12,8 +13,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -54,12 +53,12 @@ type Project struct {
|
|||||||
parent *realize
|
parent *realize
|
||||||
watcher FileWatcher
|
watcher FileWatcher
|
||||||
init bool
|
init bool
|
||||||
|
Settings `yaml:"-" json:"-"`
|
||||||
files, folders int64
|
files, folders int64
|
||||||
name, lastFile string
|
name, lastFile string
|
||||||
tools []tool
|
tools []tool
|
||||||
paths []string
|
paths []string
|
||||||
lastTime time.Time
|
lastTime time.Time
|
||||||
Settings `yaml:"-" json:"-"`
|
|
||||||
Name string `yaml:"name" json:"name"`
|
Name string `yaml:"name" json:"name"`
|
||||||
Path string `yaml:"path" json:"path"`
|
Path string `yaml:"path" json:"path"`
|
||||||
Environment map[string]string `yaml:"environment,omitempty" json:"environment,omitempty"`
|
Environment map[string]string `yaml:"environment,omitempty" json:"environment,omitempty"`
|
||||||
@ -119,16 +118,27 @@ L:
|
|||||||
select {
|
select {
|
||||||
case event := <-p.watcher.Events():
|
case event := <-p.watcher.Events():
|
||||||
if time.Now().Truncate(time.Second).After(p.lastTime) || event.Name != p.lastFile {
|
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 {
|
switch event.Op {
|
||||||
case fsnotify.Chmod:
|
case fsnotify.Chmod:
|
||||||
case fsnotify.Remove:
|
case fsnotify.Remove:
|
||||||
ext := ext(event.Name)
|
p.watcher.Remove(event.Name)
|
||||||
if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) {
|
if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) {
|
||||||
close(stop)
|
close(stop)
|
||||||
stop = make(chan bool)
|
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:
|
default:
|
||||||
file, err := os.Stat(event.Name)
|
file, err := os.Stat(event.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -137,15 +147,20 @@ L:
|
|||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
filepath.Walk(event.Name, p.walk)
|
filepath.Walk(event.Name, p.walk)
|
||||||
} else if file.Size() > 0 {
|
} else if file.Size() > 0 {
|
||||||
|
// used only for test and debug
|
||||||
if p.parent.Settings.Recovery {
|
if p.parent.Settings.Recovery {
|
||||||
log.Println(event)
|
log.Println(event)
|
||||||
}
|
}
|
||||||
ext := ext(event.Name)
|
|
||||||
if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) {
|
if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) {
|
||||||
// change watched
|
// change watched
|
||||||
close(stop)
|
// check if a file is still writing #119
|
||||||
stop = make(chan bool)
|
if event.Op != fsnotify.Write || eventTime.Truncate(time.Millisecond).After(file.ModTime().Truncate(time.Millisecond)) {
|
||||||
p.changed(event, stop)
|
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.lastTime = time.Now().Truncate(time.Second)
|
||||||
p.lastFile = event.Name
|
p.lastFile = event.Name
|
||||||
@ -172,12 +187,6 @@ func (p *Project) err(err error) {
|
|||||||
|
|
||||||
// Config project init
|
// Config project init
|
||||||
func (p *Project) config(r *realize) {
|
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
|
// get basepath name
|
||||||
p.name = filepath.Base(p.Path)
|
p.name = filepath.Base(p.Path)
|
||||||
// env variables
|
// env variables
|
||||||
@ -190,24 +199,12 @@ func (p *Project) config(r *realize) {
|
|||||||
if len(p.Cmds.Fmt.Args) == 0 {
|
if len(p.Cmds.Fmt.Args) == 0 {
|
||||||
p.Cmds.Fmt.Args = []string{"-s", "-w", "-e", "./"}
|
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{
|
p.tools = append(p.tools, tool{
|
||||||
status: p.Cmds.Clean.Status,
|
status: p.Cmds.Clean.Status,
|
||||||
cmd: replace([]string{"go clean"}, p.Cmds.Clean.Method),
|
cmd: replace([]string{"go clean"}, p.Cmds.Clean.Method),
|
||||||
options: split([]string{}, p.Cmds.Clean.Args),
|
options: split([]string{}, p.Cmds.Clean.Args),
|
||||||
name: "Clean",
|
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{
|
p.tools = append(p.tools, tool{
|
||||||
status: p.Cmds.Generate.Status,
|
status: p.Cmds.Generate.Status,
|
||||||
cmd: replace([]string{"go", "generate"}, p.Cmds.Generate.Method),
|
cmd: replace([]string{"go", "generate"}, p.Cmds.Generate.Method),
|
||||||
@ -216,11 +213,16 @@ func (p *Project) config(r *realize) {
|
|||||||
dir: true,
|
dir: true,
|
||||||
})
|
})
|
||||||
p.tools = append(p.tools, tool{
|
p.tools = append(p.tools, tool{
|
||||||
status: p.Cmds.Test.Status,
|
status: p.Cmds.Fix.Status,
|
||||||
cmd: replace([]string{"go", "test"}, p.Cmds.Test.Method),
|
cmd: replace([]string{"go fix"}, p.Cmds.Fix.Method),
|
||||||
options: split([]string{}, p.Cmds.Test.Args),
|
options: split([]string{}, p.Cmds.Fix.Args),
|
||||||
name: "Test",
|
name: "Fix",
|
||||||
dir: true,
|
})
|
||||||
|
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{
|
p.tools = append(p.tools, tool{
|
||||||
status: p.Cmds.Vet.Status,
|
status: p.Cmds.Vet.Status,
|
||||||
@ -229,6 +231,13 @@ func (p *Project) config(r *realize) {
|
|||||||
name: "Vet",
|
name: "Vet",
|
||||||
dir: true,
|
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{
|
p.Cmds.Install = Cmd{
|
||||||
Status: p.Cmds.Install.Status,
|
Status: p.Cmds.Install.Status,
|
||||||
Args: append([]string{}, p.Cmds.Install.Args...),
|
Args: append([]string{}, p.Cmds.Install.Args...),
|
||||||
@ -292,10 +301,11 @@ func (p *Project) cmd(stop <-chan bool, flag string, global bool) {
|
|||||||
// Compile is used for run and display the result of a compiling
|
// Compile is used for run and display the result of a compiling
|
||||||
func (p *Project) compile(stop <-chan bool, cmd Cmd) error {
|
func (p *Project) compile(stop <-chan bool, cmd Cmd) error {
|
||||||
if cmd.Status {
|
if cmd.Status {
|
||||||
start := time.Now()
|
var start time.Time
|
||||||
channel := make(chan Result)
|
channel := make(chan Result)
|
||||||
go func() {
|
go func() {
|
||||||
log.Println(p.pname(p.Name, 1), ":", cmd.startTxt)
|
log.Println(p.pname(p.Name, 1), ":", cmd.startTxt)
|
||||||
|
start = time.Now()
|
||||||
stream, err := p.goCompile(stop, cmd.method, cmd.Args)
|
stream, err := p.goCompile(stop, cmd.method, cmd.Args)
|
||||||
if stream != msgStop {
|
if stream != msgStop {
|
||||||
channel <- Result{stream, err}
|
channel <- Result{stream, err}
|
||||||
@ -349,10 +359,12 @@ func (p *Project) tool(stop <-chan bool, path string) error {
|
|||||||
result := make(chan tool)
|
result := make(chan tool)
|
||||||
go func() {
|
go func() {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(len(p.tools))
|
|
||||||
for _, element := range p.tools {
|
for _, element := range p.tools {
|
||||||
// no need a sequence, these commands can be asynchronous
|
// no need a sequence, these commands can be asynchronous
|
||||||
go p.goTool(&wg, stop, result, path, element)
|
if element.status {
|
||||||
|
wg.Add(1)
|
||||||
|
p.goTool(&wg, stop, result, path, element)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(done)
|
close(done)
|
||||||
@ -361,9 +373,15 @@ func (p *Project) tool(stop <-chan bool, path string) error {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case tool := <-result:
|
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))
|
if tool.err != "" {
|
||||||
buff := BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: tool.name, Stream: tool.err}
|
msg = fmt.Sprintln(p.pname(p.Name, 2), ":", red.bold(tool.name), red.regular("there are some errors in"), ":", magenta.bold(path))
|
||||||
p.stamp("error", buff, msg, tool.err)
|
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:
|
case <-done:
|
||||||
break loop
|
break loop
|
||||||
case <-stop:
|
case <-stop:
|
||||||
@ -374,19 +392,6 @@ func (p *Project) tool(stop <-chan bool, path string) error {
|
|||||||
return nil
|
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
|
// Watch the files tree of a project
|
||||||
func (p *Project) walk(path string, info os.FileInfo, err error) error {
|
func (p *Project) walk(path string, info os.FileInfo, err error) error {
|
||||||
for _, v := range p.Watcher.Ignore {
|
for _, v := range p.Watcher.Ignore {
|
||||||
@ -475,45 +480,53 @@ func (p *Project) routines(stop <-chan bool, watcher FileWatcher, path string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if !done {
|
if done {
|
||||||
// before command
|
return
|
||||||
p.cmd(stop, "before", false)
|
|
||||||
}
|
}
|
||||||
if !done {
|
// before command
|
||||||
// Go supported tools
|
p.cmd(stop, "before", false)
|
||||||
p.tool(stop, path)
|
if done {
|
||||||
// Prevent fake events on polling startup
|
return
|
||||||
p.init = true
|
|
||||||
}
|
}
|
||||||
|
// 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
|
// prevent errors using realize without config with only run flag
|
||||||
if p.Cmds.Run && !p.Cmds.Install.Status && !p.Cmds.Build.Status {
|
if p.Cmds.Run && !p.Cmds.Install.Status && !p.Cmds.Build.Status {
|
||||||
p.Cmds.Install.Status = true
|
p.Cmds.Install.Status = true
|
||||||
}
|
}
|
||||||
if !done {
|
if done {
|
||||||
install = p.compile(stop, p.Cmds.Install)
|
return
|
||||||
}
|
}
|
||||||
if !done {
|
install = p.compile(stop, p.Cmds.Install)
|
||||||
build = p.compile(stop, p.Cmds.Build)
|
if done {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if !done && (install == nil && build == nil) {
|
build = p.compile(stop, p.Cmds.Build)
|
||||||
if p.Cmds.Run {
|
if done {
|
||||||
start := time.Now()
|
return
|
||||||
runner := make(chan bool, 1)
|
}
|
||||||
go func() {
|
if install == nil && build == nil && p.Cmds.Run {
|
||||||
log.Println(p.pname(p.Name, 1), ":", "Running..")
|
var start time.Time
|
||||||
p.goRun(stop, runner)
|
runner := make(chan bool, 1)
|
||||||
}()
|
go func() {
|
||||||
select {
|
log.Println(p.pname(p.Name, 1), ":", "Running..")
|
||||||
case <-runner:
|
start = time.Now()
|
||||||
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"))
|
p.goRun(stop, runner)
|
||||||
out = BufferOut{Time: time.Now(), Text: "Started in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"}
|
}()
|
||||||
p.stamp("log", out, msg, "")
|
select {
|
||||||
case <-stop:
|
case <-runner:
|
||||||
return
|
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 {
|
if done {
|
||||||
p.cmd(stop, "after", false)
|
return
|
||||||
}
|
}
|
||||||
|
p.cmd(stop, "after", false)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user