diff --git a/assets/index.html b/assets/index.html
new file mode 100644
index 0000000..9ad0403
--- /dev/null
+++ b/assets/index.html
@@ -0,0 +1,9 @@
+
+
+
+
+Realize
+
+
+
+
\ No newline at end of file
diff --git a/bindata.go b/bindata.go
index 34917ed..d430eb0 100644
--- a/bindata.go
+++ b/bindata.go
@@ -398,7 +398,7 @@ func assetsIndexHtml() (*asset, error) {
return nil, err
}
- info := bindataFileInfo{name: "assets/index.html", size: 936, mode: os.FileMode(420), modTime: time.Unix(1505914049, 0)}
+ info := bindataFileInfo{name: "assets/index.html", size: 936, mode: os.FileMode(420), modTime: time.Unix(1510563869, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
diff --git a/cli.go b/cli.go
new file mode 100644
index 0000000..5b9795d
--- /dev/null
+++ b/cli.go
@@ -0,0 +1,1144 @@
+package main
+
+import (
+ "github.com/fatih/color"
+ "github.com/tockins/interact"
+ "gopkg.in/urfave/cli.v2"
+ "log"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Version print current version
+func version() {
+ r := Realize{}
+ log.Println(r.Prefix(green.bold(version)))
+}
+
+// Clean remove realize folder
+func clean() (err error) {
+ r := Realize{}
+ if err := r.Settings.Remove(RDir); err != nil {
+ return err
+ }
+ log.Println(r.Prefix(green.bold("folder successfully removed")))
+ return nil
+}
+
+// Add a project to an existing config or create a new one
+func add(c *cli.Context) (err error) {
+ r := Realize{}
+ // read a config if exist
+ err = r.Settings.Read(&r)
+ if err != nil {
+ return err
+ }
+ projects := len(r.Schema.Projects)
+ // create and add a new project
+ r.Schema.Add(r.Schema.New(c))
+ if len(r.Schema.Projects) > projects {
+ // update config
+ err = r.Settings.Write(r)
+ if err != nil {
+ return err
+ }
+ log.Println(r.Prefix(green.bold("project successfully added")))
+ } else {
+ log.Println(r.Prefix(green.bold("project can't be added")))
+ }
+ return nil
+}
+
+// Setup a new config step by step
+func setup(c *cli.Context) (err error) {
+ r := Realize{}
+ interact.Run(&interact.Interact{
+ Before: func(context interact.Context) error {
+ context.SetErr(red.bold("INVALID INPUT"))
+ context.SetPrfx(color.Output, yellow.regular("[")+time.Now().Format("15:04:05")+yellow.regular("]")+yellow.bold("[")+strings.ToUpper(RPrefix)+yellow.bold("]"))
+ return nil
+ },
+ Questions: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ if _, err := os.Stat(RDir + "/" + RFile); err != nil {
+ d.Skip()
+ }
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Would you want to overwrite existing " + magenta.bold(RPrefix) + " config?",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ } else if val {
+ r := Realize{}
+ r.Server = Server{&r, false, false, Port, Host}
+ }
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Would you want to customize settings?",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(0, green.regular("(os default)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[int]"),
+ Msg: "Set max number of open files (root required)",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Int()
+ if err != nil {
+ return d.Err()
+ }
+ r.Settings.FileLimit = int32(val)
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Force polling watcher?",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(100, green.regular("(100ms)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[int]"),
+ Msg: "Set polling interval",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Int()
+ if err != nil {
+ return d.Err()
+ }
+ r.Settings.Legacy.Interval = time.Duration(int(val)) * time.Millisecond
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Settings.Legacy.Force = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable logging files",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Settings.Files.Errors = Resource{Name: FileErr, Status: val}
+ r.Settings.Files.Outputs = Resource{Name: FileOut, Status: val}
+ r.Settings.Files.Logs = Resource{Name: FileLog, Status: val}
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable web server",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(Port, green.regular("("+strconv.Itoa(Port)+")"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[int]"),
+ Msg: "Server port",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Int()
+ if err != nil {
+ return d.Err()
+ }
+ r.Server.Port = int(val)
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(Host, green.regular("("+Host+")"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Server host",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Server.Host = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Open in current browser",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Server.Open = val
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Server.Status = val
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ _, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(true, green.regular("(y)"))
+ d.SetEnd("!")
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Would you want to " + magenta.regular("add a new project") + "? (insert '!' to stop)",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ if val {
+ r.Schema.Add(r.Schema.New(c))
+ }
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(wdir(), green.regular("("+wdir()+")"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Project name",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Name = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ dir := wdir()
+ d.SetDef(dir, green.regular("("+dir+")"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Project path",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Path = filepath.Clean(val)
+ return nil
+ },
+ },
+
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable go vet",
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(none)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Vet additional arguments",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ if val != "" {
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Vet.Args = append(r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Vet.Args, val)
+ }
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Vet.Status = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable go fmt",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(none)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Fmt additional arguments",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ if val != "" {
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Fmt.Args = append(r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Fmt.Args, val)
+ }
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Fmt.Status = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable go test",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(none)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Test additional arguments",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ if val != "" {
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Test.Args = append(r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Test.Args, val)
+ }
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Test.Status = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable go fix",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(none)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Fix additional arguments",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ if val != "" {
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Fix.Args = append(r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Fix.Args, val)
+ }
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Fix.Status = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable go clean",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(none)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Clean additional arguments",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ if val != "" {
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Clean.Args = append(r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Clean.Args, val)
+ }
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Clean.Status = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable go generate",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(none)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Generate additional arguments",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ if val != "" {
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Generate.Args = append(r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Generate.Args, val)
+ }
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Generate.Status = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(true, green.regular("(y)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable go install",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(none)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Install additional arguments",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ if val != "" {
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Install.Args = append(r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Install.Args, val)
+ }
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Install.Status = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable go build",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(none)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Build additional arguments",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ if val != "" {
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Build.Args = append(r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Build.Args, val)
+ }
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Build.Status = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(true, green.regular("(y)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Enable go run",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Tools.Run = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Customize watching paths",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ if val {
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Paths = r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Paths[:len(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Paths)-1]
+ }
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetEnd("!")
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Insert a path to watch (insert '!' to stop)",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Paths = append(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Paths, val)
+ d.Reload()
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ _, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Customize ignore paths",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ if val {
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Ignore = r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Ignore[:len(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Ignore)-1]
+ }
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetEnd("!")
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Insert a path to ignore (insert '!' to stop)",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Ignore = append(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Ignore, val)
+ d.Reload()
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ _, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Add an additional argument",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ d.SetEnd("!")
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Add another argument (insert '!' to stop)",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Args = append(r.Schema.Projects[len(r.Schema.Projects)-1].Args, val)
+ d.Reload()
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ _, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(none)"))
+ d.SetEnd("!")
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Add a 'before' custom command (insert '!' to stop)",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Insert a command",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts = append(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts, Command{Type: "before", Cmd: val})
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Launch from a specific path",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts[len(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts)-1].Path = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Tag as global command",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts[len(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts)-1].Global = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Display command output",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts[len(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts)-1].Output = val
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ if val {
+ d.Reload()
+ }
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(none)"))
+ d.SetEnd("!")
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Add an 'after' custom commands (insert '!' to stop)",
+ Resolve: func(d interact.Context) bool {
+ val, _ := d.Ans().Bool()
+ return val
+ },
+ },
+ Subs: []*interact.Question{
+ {
+ Before: func(d interact.Context) error {
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Insert a command",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts = append(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts, Command{Type: "after", Cmd: val})
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Launch from a specific path",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts[len(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts)-1].Path = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Tag as global command",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts[len(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts)-1].Global = val
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef(false, green.regular("(n)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[y/n]"),
+ Msg: "Display command output",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts[len(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Scripts)-1].Output = val
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().Bool()
+ if err != nil {
+ return d.Err()
+ }
+ if val {
+ d.Reload()
+ }
+ return nil
+ },
+ },
+ {
+ Before: func(d interact.Context) error {
+ d.SetDef("", green.regular("(none)"))
+ return nil
+ },
+ Quest: interact.Quest{
+ Options: yellow.regular("[string]"),
+ Msg: "Set an error output pattern",
+ },
+ Action: func(d interact.Context) interface{} {
+ val, err := d.Ans().String()
+ if err != nil {
+ return d.Err()
+ }
+ r.Schema.Projects[len(r.Schema.Projects)-1].ErrorOutputPattern = val
+ return nil
+ },
+ },
+ },
+ Action: func(d interact.Context) interface{} {
+ if val, err := d.Ans().Bool(); err != nil {
+ return d.Err()
+ } else if val {
+ d.Reload()
+ }
+ return nil
+ },
+ },
+ },
+ After: func(d interact.Context) error {
+ if val, _ := d.Qns().Get(0).Ans().Bool(); val {
+ err := r.Settings.Remove(RDir)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+ },
+ })
+ // create config
+ err = r.Settings.Write(r)
+ if err != nil {
+ return err
+ }
+ log.Println(r.Prefix(green.bold("Config successfully created")))
+ return nil
+}
+
+// Start realize workflow
+func start(c *cli.Context) (err error) {
+ r := Realize{}
+ r.Server = Server{&r, false, false, Port, Host}
+ // check no-config and read
+ if !c.Bool("no-config") {
+ // read a config if exist
+ err = r.Settings.Read(&r)
+ if err != nil {
+ return err
+ }
+ if c.String("name") != "" {
+ // filter by name flag if exist
+ r.Schema.Filter("name", c.String("name"))
+ }
+ // increase file limit
+ if r.Settings.FileLimit != 0 {
+ if err = r.Settings.Flimit(); err != nil {
+ return err
+ }
+ }
+
+ }
+ // check project list length
+ if len(r.Schema.Projects) <= 0 {
+ // create a new project based on given params
+ project := r.Schema.New(c)
+ // Add to projects list
+ r.Schema.Add(project)
+ }
+ // save config
+ if !c.Bool("no-config") {
+ err = r.Settings.Write(r)
+ if err != nil {
+ return err
+ }
+ }
+ // config and start server
+ if c.Bool("server") || r.Server.Status {
+ r.Server.Status = true
+ if c.Bool("open") || r.Server.Open {
+ r.Server.Open = true
+ r.Server.OpenURL()
+ }
+ err = r.Server.Start()
+ if err != nil {
+ return err
+ }
+ }
+ // start workflow
+ r.Start()
+ return
+}
+
+// Remove a project from an existing config
+func remove(c *cli.Context) (err error) {
+ r := Realize{}
+ // read a config if exist
+ err = r.Settings.Read(&r)
+ if err != nil {
+ return err
+ }
+ if c.String("name") != "" {
+ err := r.Schema.Remove(c.String("name"))
+ if err != nil {
+ return err
+ }
+ // update config
+ err = r.Settings.Write(r)
+ if err != nil {
+ return err
+ }
+ log.Println(r.Prefix(green.bold("project successfully removed")))
+ } else {
+ log.Println(r.Prefix(green.bold("project name not found")))
+ }
+ return nil
+}
diff --git a/cmd.go b/cmd.go
deleted file mode 100644
index 8a71296..0000000
--- a/cmd.go
+++ /dev/null
@@ -1,157 +0,0 @@
-package main
-
-import (
- "errors"
- "gopkg.in/urfave/cli.v2"
- "os"
- "path/filepath"
-)
-
-// Tool options customizable, should be moved in Cmd
-type tool struct {
- name, err, out string
- cmd, options []string
- dir, status bool
-}
-
-// Cmds list of go commands
-type Cmds struct {
- Fix Cmd `yaml:"fix,omitempty" json:"fix,omitempty"`
- Clean Cmd `yaml:"clean,omitempty" json:"clean,omitempty"`
- Vet Cmd `yaml:"vet,omitempty" json:"vet,omitempty"`
- Fmt Cmd `yaml:"fmt,omitempty" json:"fmt,omitempty"`
- Test Cmd `yaml:"test,omitempty" json:"test,omitempty"`
- Generate Cmd `yaml:"generate,omitempty" json:"generate,omitempty"`
- Install Cmd `yaml:"install,omitempty" json:"install,omitempty"`
- Build Cmd `yaml:"build,omitempty" json:"build,omitempty"`
- Run bool `yaml:"run,omitempty" json:"run,omitempty"`
-}
-
-// Cmd single command fields and options
-type Cmd struct {
- Method string `yaml:"method,omitempty" json:"method,omitempty"`
- Args []string `yaml:"args,omitempty" json:"args,omitempty"`
- Status bool `yaml:"status,omitempty" json:"status,omitempty"`
- tool bool
- method []string
- name, startTxt, endTxt string
-}
-
-// Clean duplicate projects
-func (r *realize) clean() error {
- if len(r.Schema) > 0 {
- 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
- }
- }
- return nil
- }
- return errors.New("there are no projects")
-}
-
-// Add a new project
-func (r *realize) add(p *cli.Context) (err error) {
- // project init
- name := filepath.Base(p.String("path"))
- if name == "." {
- name = filepath.Base(wdir())
- }
- project := Project{
- Name: name,
- Path: p.String("path"),
- Cmds: Cmds{
- Vet: Cmd{
- Status: p.Bool("vet"),
- },
- Fmt: Cmd{
- Status: p.Bool("fmt"),
- },
- Test: Cmd{
- Status: p.Bool("test"),
- },
- Generate: Cmd{
- Status: p.Bool("generate"),
- },
- Build: Cmd{
- Status: p.Bool("build"),
- },
- Install: Cmd{
- Status: p.Bool("install"),
- },
- Run: p.Bool("run"),
- },
- Args: params(p),
- Watcher: Watch{
- Paths: []string{"/"},
- Ignore: []string{".git", ".realize", "vendor"},
- Exts: []string{"go"},
- },
- }
- if _, err := duplicates(project, r.Schema); err != nil {
- return err
- }
- r.Schema = append(r.Schema, project)
- return nil
-}
-
-// Run launches the toolchain for each project
-func (r *realize) run(p *cli.Context) error {
- var match bool
- // check projects and remove duplicates
- if err := r.clean(); err != nil {
- return err
- }
- // set gobin
- if err := os.Setenv("GOBIN", filepath.Join(os.Getenv("GOPATH"), "bin")); err != nil {
- return err
- }
- // loop projects
- if p.String("name") != "" {
- wg.Add(1)
- } else {
- wg.Add(len(r.Schema))
- }
- for k, elm := range r.Schema {
- // command start using name flag
- if p.String("name") != "" && elm.Name != p.String("name") {
- continue
- }
- match = true
- r.Schema[k].config(r)
- go r.Schema[k].watch()
- }
- if !match {
- return errors.New("there is no project with the given name")
- }
- wg.Wait()
- return nil
-}
-
-// Remove a project
-func (r *realize) remove(p *cli.Context) error {
- for key, val := range r.Schema {
- if p.String("name") == val.Name {
- r.Schema = append(r.Schema[:key], r.Schema[key+1:]...)
- return nil
- }
- }
- return errors.New("no project found")
-}
-
-// Insert current project if there isn't already one
-func (r *realize) insert(c *cli.Context) error {
- if c.Bool("no-config") {
- r.Schema = []Project{}
- }
- if len(r.Schema) <= 0 {
- if err := r.add(c); err != nil {
- return err
- }
- }
- return nil
-}
diff --git a/cmd_test.go b/cmd_test.go
deleted file mode 100644
index 3ebe2d8..0000000
--- a/cmd_test.go
+++ /dev/null
@@ -1,156 +0,0 @@
-package main
-
-import (
- "flag"
- "gopkg.in/urfave/cli.v2"
- "log"
- "os"
- "path/filepath"
- "reflect"
- "testing"
- "time"
-)
-
-type loggerT struct{}
-
-func (loggerT) Write(bytes []byte) (int, error) {
- return 0, nil
-}
-
-func TestMain(m *testing.M) {
- log.SetFlags(0)
- log.SetOutput(loggerT{})
- os.Exit(m.Run())
-}
-
-func TestRealize_Clean(t *testing.T) {
- r := realize{}
- r.Schema = append(r.Schema, Project{Name: "test0"})
- r.Schema = append(r.Schema, Project{Name: "test0"})
- r.clean()
- if len(r.Schema) > 1 {
- t.Error("Expected only one project")
- }
- r.Schema = append(r.Schema, Project{Path: "test1"})
- r.Schema = append(r.Schema, Project{Path: "test1"})
- r.clean()
- if len(r.Schema) != 2 {
- t.Error("Expected two projects")
- }
-
-}
-
-func TestRealize_Add(t *testing.T) {
- r := realize{}
- // add all flags, test with expected
- set := flag.NewFlagSet("test", 0)
- set.Bool("fmt", false, "")
- set.Bool("vet", false, "")
- set.Bool("test", false, "")
- set.Bool("install", false, "")
- set.Bool("run", false, "")
- set.Bool("build", false, "")
- set.Bool("generate", false, "")
- set.String("path", wdir(), "")
- c := cli.NewContext(nil, set, nil)
- set.Parse([]string{"--fmt", "--install", "--run", "--build", "--generate", "--test", "--vet"})
- r.add(c)
- expected := Project{
- Name: filepath.Base(wdir()),
- Path: wdir(),
- Cmds: Cmds{
- Fmt: Cmd{
- Status: true,
- },
- Install: Cmd{
- Status: true,
- },
- Generate: Cmd{
- Status: true,
- },
- Test: Cmd{
- Status: true,
- },
- Build: Cmd{
- Status: true,
- },
- Vet: Cmd{
- Status: true,
- },
- Run: true,
- },
- Watcher: Watch{
- Paths: []string{"/"},
- Ignore: []string{".git", ".realize", "vendor"},
- Exts: []string{"go"},
- },
- }
- if !reflect.DeepEqual(r.Schema[0], expected) {
- t.Error("Expected equal struct")
- }
-}
-
-func TestRealize_Run(t *testing.T) {
- set := flag.NewFlagSet("test", 0)
- params := cli.NewContext(nil, set, nil)
- m := make(map[string]string)
- m["test"] = "test"
- r := realize{}
- r.Schema = []Project{
- {
- Name: "test0",
- Path: ".",
- },
- {
- Name: "test1",
- Path: "/test",
- },
- {
- Name: "test2",
- Path: "/test",
- },
- }
- go r.run(params)
- time.Sleep(1 * time.Second)
-}
-
-func TestRealize_Remove(t *testing.T) {
- r := realize{}
- set := flag.NewFlagSet("name", 0)
- set.String("name", "", "")
- c := cli.NewContext(nil, set, nil)
- set.Parse([]string{"--name=test0"})
- err := r.remove(c)
- if err == nil {
- t.Error("Expected an error, there are no projects")
- }
- // Append a new project
- r.Schema = append(r.Schema, Project{Name: "test0"})
- err = r.remove(c)
- if err != nil {
- t.Error("Error unexpected, the project should be remove", err)
- }
-}
-
-func TestRealize_Insert(t *testing.T) {
- r := realize{}
- // add all flags, test with expected
- set := flag.NewFlagSet("test", 0)
- set.Bool("no-config", false, "")
- c := cli.NewContext(nil, set, nil)
- set.Parse([]string{"--no-config"})
-
- r.insert(c)
- if len(r.Schema) != 1 {
- t.Error("Expected one project instead", len(r.Schema))
- }
-
- r.Schema = []Project{}
- r.Schema = append(r.Schema, Project{})
- r.Schema = append(r.Schema, Project{})
- c = cli.NewContext(nil, set, nil)
- r.insert(c)
- if len(r.Schema) != 1 {
- t.Error("Expected one project instead", len(r.Schema))
- }
-}
diff --git a/commands.go b/commands.go
new file mode 100644
index 0000000..3d66c90
--- /dev/null
+++ b/commands.go
@@ -0,0 +1,55 @@
+package main
+
+import (
+ "bytes"
+ "github.com/go-siris/siris/core/errors"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+// Command options
+type Command struct {
+ Type string `yaml:"type" json:"type"`
+ Cmd string `yaml:"command" json:"command"`
+ Path string `yaml:"path,omitempty" json:"path,omitempty"`
+ Global bool `yaml:"global,omitempty" json:"global,omitempty"`
+ Output bool `yaml:"output,omitempty" json:"output,omitempty"`
+}
+
+// Exec an additional command from a defined path if specified
+func (c *Command) Exec(base string, stop <-chan bool) (response Response) {
+ var stdout bytes.Buffer
+ var stderr bytes.Buffer
+ done := make(chan error)
+ args := strings.Split(strings.Replace(strings.Replace(c.Cmd, "'", "", -1), "\"", "", -1), " ")
+ ex := exec.Command(args[0], args[1:]...)
+ ex.Dir = base
+ // make cmd path
+ if c.Path != "" {
+ if strings.Contains(c.Path, base) {
+ ex.Dir = c.Path
+ } else {
+ ex.Dir = filepath.Join(base, c.Path)
+ }
+ }
+ ex.Stdout = &stdout
+ ex.Stderr = &stderr
+ // Start command
+ ex.Start()
+ go func() { done <- ex.Wait() }()
+ // Wait a result
+ select {
+ case <-stop:
+ // Stop running command
+ ex.Process.Kill()
+ case err := <-done:
+ // Command completed
+ response.Name = c.Cmd
+ response.Out = stdout.String()
+ if err != nil {
+ response.Err = errors.New(stderr.String())
+ }
+ }
+ return
+}
diff --git a/exec.go b/exec.go
deleted file mode 100644
index 09c4615..0000000
--- a/exec.go
+++ /dev/null
@@ -1,228 +0,0 @@
-package main
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "github.com/pkg/errors"
- "log"
- "os"
- "os/exec"
- "path/filepath"
- "regexp"
- "strings"
- "sync"
- "time"
-)
-
-// GoCompile is used for compile a project
-func (p *Project) goCompile(stop <-chan bool, method []string, args []string) (string, error) {
- var out bytes.Buffer
- var stderr bytes.Buffer
- done := make(chan error)
- args = append(method, args...)
- cmd := exec.Command(args[0], args[1:]...)
- cmd.Dir = p.Path
- cmd.Stdout = &out
- cmd.Stderr = &stderr
- // Start command
- cmd.Start()
- go func() { done <- cmd.Wait() }()
- // Wait a result
- select {
- case <-stop:
- // Stop running command
- cmd.Process.Kill()
- return msgStop, nil
- case err := <-done:
- // Command completed
- if err != nil {
- return stderr.String(), err
- }
- return "", nil
- }
- return "", nil
-}
-
-// GoRun is an implementation of the bin execution
-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
- }
- errRegexp, err := regexp.Compile(p.ErrorOutputPattern)
- if err != nil {
- msg := fmt.Sprintln(p.pname(p.Name, 3), ":", blue.regular(err.Error()))
- out := BufferOut{Time: time.Now(), Text: err.Error(), Type: "Go Run"}
- p.stamp("error", out, msg, "")
- } else {
- isErrorText = func(t string) bool {
- return errRegexp.MatchString(t)
- }
- }
-
- // add additional arguments
- for _, arg := range p.Args {
- a := strings.FieldsFunc(arg, func(i rune) bool {
- return i == '"' || i == '=' || i == '\''
- })
- args = append(args, a...)
- }
-
- gobin := os.Getenv("GOBIN")
- 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 {
- 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 {
- p.err(errors.New("Build not found"))
- return
- }
- }
-
- 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(err, "Failed to stop", ":")
- }
- msg := fmt.Sprintln(p.pname(p.Name, 2), ":", red.regular("Ended"))
- out := BufferOut{Time: time.Now(), Text: "Ended", Type: "Go Run"}
- p.stamp("log", out, msg, "")
- }()
-
- // scan project stream
- stdout, err := build.StdoutPipe()
- stderr, err := build.StderrPipe()
- if err != nil {
- log.Println(red.bold(err.Error()))
- return
- }
- if err := build.Start(); err != nil {
- log.Println(red.bold(err.Error()))
- return
- }
- close(runner)
-
- execOutput, execError := bufio.NewScanner(stdout), bufio.NewScanner(stderr)
- stopOutput, stopError := make(chan bool, 1), make(chan bool, 1)
- scanner := func(stop chan bool, output *bufio.Scanner, isError bool) {
- for output.Scan() {
- text := output.Text()
- msg := fmt.Sprintln(p.pname(p.Name, 3), ":", blue.regular(text))
- if isError && !isErrorText(text) {
- out := BufferOut{Time: time.Now(), Text: text, Type: "Go Run"}
- p.stamp("error", out, msg, "")
- } else {
- out := BufferOut{Time: time.Now(), Text: text, Type: "Go Run"}
- p.stamp("out", out, msg, "")
- }
- }
- close(stop)
- }
- go scanner(stopOutput, execOutput, false)
- go scanner(stopError, execError, true)
- for {
- select {
- case <-stop:
- return
- case <-stopOutput:
- return
- case <-stopError:
- return
- }
- }
-}
-
-// Exec an additional command from a defined path if specified
-func (p *Project) command(stop <-chan bool, cmd Command) (string, string) {
- var stdout bytes.Buffer
- var stderr bytes.Buffer
- done := make(chan error)
- args := strings.Split(strings.Replace(strings.Replace(cmd.Command, "'", "", -1), "\"", "", -1), " ")
- ex := exec.Command(args[0], args[1:]...)
- ex.Dir = p.Path
- // make cmd path
- if cmd.Path != "" {
- if strings.Contains(cmd.Path, p.Path) {
- ex.Dir = cmd.Path
- } else {
- ex.Dir = filepath.Join(p.Path, cmd.Path)
- }
- }
- ex.Stdout = &stdout
- ex.Stderr = &stderr
- // Start command
- ex.Start()
- go func() { done <- ex.Wait() }()
- // Wait a result
- select {
- case <-stop:
- // Stop running command
- ex.Process.Kill()
- return "", ""
- case err := <-done:
- // Command completed
- if err != nil {
- return stderr.String(), stdout.String()
- }
- }
- return "", stdout.String()
-}
-
-// GoTool is used for run go tools methods such as fmt, test, generate and so on
-func (p *Project) goTool(wg *sync.WaitGroup, stop <-chan bool, result chan<- tool, path string, tool tool) {
- defer wg.Done()
- if tool.status {
- if tool.dir && filepath.Ext(path) != "" {
- path = filepath.Dir(path)
- }
- if strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "") {
- if strings.HasSuffix(path, ".go") {
- tool.options = append(tool.options, path)
- path = p.Path
- }
- if s := ext(path); s == "" || s == "go" {
- var out, stderr bytes.Buffer
- done := make(chan error)
- tool.cmd = append(tool.cmd, tool.options...)
- cmd := exec.Command(tool.cmd[0], tool.cmd[1:]...)
- cmd.Dir = path
- cmd.Stdout = &out
- cmd.Stderr = &stderr
- // Start command
- cmd.Start()
- go func() { done <- cmd.Wait() }()
- // Wait a result
- select {
- case <-stop:
- // Stop running command
- cmd.Process.Kill()
- return
- case err := <-done:
- // Command completed
- if err != nil {
- tool.err = stderr.String() + out.String()
- // send command result
- result <- tool
- } else {
- tool.out = out.String()
- }
- return
- }
-
- }
- }
- }
-}
diff --git a/exec_test.go b/exec_test.go
deleted file mode 100644
index c7613e9..0000000
--- a/exec_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package main
-
-import (
- "testing"
- "time"
-)
-
-func TestProject_GoCompile(t *testing.T) {
- p := Project{}
- stop := make(chan bool)
- response := make(chan string)
- result, err := p.goCompile(stop, []string{"echo"}, []string{"test"})
- if err != nil {
- t.Error("Unexpected", err)
- }
- go func() {
- result, _ = p.goCompile(stop, []string{"sleep"}, []string{"20s"})
- response <- result
- }()
- close(stop)
- select {
- case v := <-response:
- if v != msgStop {
- t.Error("Unexpected result", response)
- }
- case <-time.After(2 * time.Second):
- t.Error("Channel doesn't works")
- }
-}
diff --git a/projects.go b/projects.go
new file mode 100644
index 0000000..c6d8b90
--- /dev/null
+++ b/projects.go
@@ -0,0 +1,529 @@
+package main
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "github.com/fsnotify/fsnotify"
+ "log"
+ "math/big"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var (
+ msg string
+ out BufferOut
+)
+
+// Watch info
+type Watch struct {
+ Paths []string `yaml:"paths" json:"paths"`
+ Exts []string `yaml:"extensions" json:"extensions"`
+ Ignore []string `yaml:"ignored_paths,omitempty" json:"ignored_paths,omitempty"`
+ Scripts []Command `yaml:"scripts,omitempty" json:"scripts,omitempty"`
+}
+
+// Project info
+type Project struct {
+ parent *Realize
+ watcher FileWatcher
+ init bool
+ files int64
+ folders int64
+ name string
+ lastFile string
+ paths []string
+ lastTime time.Time
+ Name string `yaml:"name" json:"name"`
+ Path string `yaml:"path" json:"path"`
+ Environment map[string]string `yaml:"environment,omitempty" json:"environment,omitempty"`
+ Tools Tools `yaml:"commands" json:"commands"`
+ Args []string `yaml:"args,omitempty" json:"args,omitempty"`
+ Watcher Watch `yaml:"watcher" json:"watcher"`
+ Buffer Buffer `yaml:"-" json:"buffer"`
+ ErrorOutputPattern string `yaml:"errorOutputPattern,omitempty" json:"errorOutputPattern,omitempty"`
+}
+
+// Response exec
+type Response struct {
+ Name string
+ Out string
+ Err error
+}
+
+// Buffer define an array buffer for each log files
+type Buffer struct {
+ StdOut []BufferOut `json:"stdOut"`
+ StdLog []BufferOut `json:"stdLog"`
+ StdErr []BufferOut `json:"stdErr"`
+}
+
+// BufferOut is used for exchange information between "realize cli" and "web realize"
+type BufferOut struct {
+ Time time.Time `json:"time"`
+ Text string `json:"text"`
+ Path string `json:"path"`
+ Type string `json:"type"`
+ Stream string `json:"stream"`
+ Errors []string `json:"errors"`
+}
+
+// Setup a project
+func (p *Project) Setup() {
+ // get base path
+ p.name = filepath.Base(p.Path)
+ // set env const
+ for key, item := range p.Environment {
+ if err := os.Setenv(key, item); err != nil {
+ p.Buffer.StdErr = append(p.Buffer.StdErr, BufferOut{Time: time.Now(), Text: err.Error(), Type: "Env error", Stream: ""})
+ }
+ }
+ p.Tools.Setup()
+}
+
+// Watch a project
+func (p *Project) Watch(exit chan os.Signal) {
+ var err error
+ stop := make(chan bool)
+ // init a new watcher
+ p.watcher, err = Watcher(p.parent.Settings.Legacy.Force, p.parent.Settings.Legacy.Interval)
+ if err != nil {
+ log.Fatal(err)
+ }
+ // global commands before
+ p.cmd(stop, "before", true)
+ // indexing files and dirs
+ for _, dir := range p.Watcher.Paths {
+ base, _ := filepath.Abs(p.Path)
+ base = filepath.Join(base, dir)
+ if _, err := os.Stat(base); err == nil {
+ if err := filepath.Walk(base, p.walk); err == nil {
+ p.tools(stop, base)
+ }
+ } else {
+ p.err(err)
+ }
+ }
+ // start message
+ msg = fmt.Sprintln(p.pname(p.Name, 1), ":", blue.bold("Watching"), magenta.bold(p.files), "file/s", magenta.bold(p.folders), "folder/s")
+ out = BufferOut{Time: time.Now(), Text: "Watching " + strconv.FormatInt(p.files, 10) + " files/s " + strconv.FormatInt(p.folders, 10) + " folder/s"}
+ p.stamp("log", out, msg, "")
+ // start watcher
+ go p.Reload(p.watcher, "", stop)
+L:
+ for {
+ 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:
+ p.watcher.Remove(event.Name)
+ if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) {
+ close(stop)
+ stop = make(chan bool)
+ p.stamp("log", out, msg, "")
+ go p.Reload(p.watcher, "", stop)
+ }
+ default:
+ file, err := os.Stat(event.Name)
+ if err != nil {
+ continue
+ }
+ if file.IsDir() {
+ filepath.Walk(event.Name, p.walk)
+ } else if file.Size() > 0 {
+ if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) {
+ // change watched
+ // check if a file is still writing #119
+ if event.Op != fsnotify.Write || (eventTime.Truncate(time.Millisecond).After(file.ModTime().Truncate(time.Millisecond)) || event.Name != p.lastFile) {
+ close(stop)
+ stop = make(chan bool)
+ // stop and start again
+ p.stamp("log", out, msg, "")
+ go p.Reload(p.watcher, event.Name, stop)
+ }
+ }
+ p.lastTime = time.Now().Truncate(time.Second)
+ p.lastFile = event.Name
+ }
+ }
+ }
+ case err := <-p.watcher.Errors():
+ p.err(err)
+ case <-exit:
+ p.cmd(nil, "after", true)
+ break L
+ }
+ }
+}
+
+// Reload launches the toolchain run, build, install
+func (p *Project) Reload(watcher FileWatcher, path string, stop <-chan bool) {
+ var done bool
+ var install, build Response
+ go func() {
+ for {
+ select {
+ case <-stop:
+ done = true
+ return
+ }
+ }
+ }()
+ if done {
+ return
+ }
+ // before command
+ p.cmd(stop, "before", false)
+ if done {
+ return
+ }
+ // Go supported tools
+ p.tools(stop, path)
+ // Prevent fake events on polling startup
+ p.init = true
+ // prevent errors using realize without config with only run flag
+ if p.Tools.Run && !p.Tools.Install.Status && !p.Tools.Build.Status {
+ p.Tools.Install.Status = true
+ }
+ if done {
+ return
+ }
+ if p.Tools.Install.Status {
+ msg = fmt.Sprintln(p.pname(p.Name, 1), ":", green.regular(p.Tools.Install.name), "started")
+ out = BufferOut{Time: time.Now(), Text: p.Tools.Install.name + " started"}
+ p.stamp("log", out, msg, "")
+ start := time.Now()
+ install = p.Tools.Install.Compile(p.Path, stop)
+ install.printAfter(start, p)
+ }
+ if done {
+ return
+ }
+ if p.Tools.Build.Status {
+ msg = fmt.Sprintln(p.pname(p.Name, 1), ":", green.regular(p.Tools.Build.name), "started")
+ out = BufferOut{Time: time.Now(), Text: p.Tools.Build.name + " started"}
+ p.stamp("log", out, msg, "")
+ start := time.Now()
+ build = p.Tools.Build.Compile(p.Path, stop)
+ build.printAfter(start, p)
+ }
+ if done {
+ return
+ }
+ if install.Err == nil && build.Err == nil && p.Tools.Run {
+ var start time.Time
+ result := make(chan Response)
+ go func() {
+ select {
+ case r := <-result:
+ if r.Err != nil {
+ msg := fmt.Sprintln(p.pname(p.Name, 2), ":", red.regular(r.Err))
+ out := BufferOut{Time: time.Now(), Text: r.Err.Error(), Type: "Go Run"}
+ p.stamp("error", out, msg, "")
+ }
+ if r.Out != "" {
+ msg := fmt.Sprintln(p.pname(p.Name, 3), ":", blue.regular(r.Out))
+ out := BufferOut{Time: time.Now(), Text: r.Out, Type: "Go Run"}
+ p.stamp("out", out, msg, "")
+ }
+ }
+ }()
+ go func() {
+ log.Println(p.pname(p.Name, 1), ":", "Running..")
+ start = time.Now()
+ p.Run(p.Path, stop)
+ }()
+ }
+ if done {
+ return
+ }
+ p.cmd(stop, "after", false)
+}
+
+// Run a project
+func (p *Project) Run(path string, stop <-chan bool) (response chan Response) {
+ var args []string
+ var build *exec.Cmd
+ var r Response
+ defer func() {
+ if err := build.Process.Kill(); err != nil {
+ r.Err = err
+ }
+ }()
+
+ // custom error pattern
+ isErrorText := func(string) bool {
+ return false
+ }
+ errRegexp, err := regexp.Compile(p.ErrorOutputPattern)
+ if err != nil {
+ r.Err = err
+ response <- r
+ r.Err = nil
+ } else {
+ isErrorText = func(t string) bool {
+ return errRegexp.MatchString(t)
+ }
+ }
+
+ // add additional arguments
+ for _, arg := range p.Args {
+ a := strings.FieldsFunc(arg, func(i rune) bool {
+ return i == '"' || i == '=' || i == '\''
+ })
+ args = append(args, a...)
+ }
+ gobin := os.Getenv("GOBIN")
+ dirPath := filepath.Base(path)
+ if 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 + RExtWin); err == nil {
+ build = exec.Command(path+RExtWin, args...)
+ } else {
+ if _, err = os.Stat(path); err == nil {
+ build = exec.Command(path, args...)
+ } else if _, err = os.Stat(path + RExtWin); err == nil {
+ build = exec.Command(path+RExtWin, args...)
+ } else {
+ r.Err = errors.New("project not found")
+ return
+ }
+ }
+ // scan project stream
+ stdout, err := build.StdoutPipe()
+ stderr, err := build.StderrPipe()
+ if err != nil {
+ r.Err = err
+ return
+ }
+ if err := build.Start(); err != nil {
+ r.Err = err
+ return
+ }
+ execOutput, execError := bufio.NewScanner(stdout), bufio.NewScanner(stderr)
+ stopOutput, stopError := make(chan bool, 1), make(chan bool, 1)
+ scanner := func(stop chan bool, output *bufio.Scanner, isError bool) {
+ for output.Scan() {
+ text := output.Text()
+ if isError && !isErrorText(text) {
+ r.Err = errors.New(text)
+ response <- r
+ r.Err = nil
+ } else {
+ r.Out = text
+ response <- r
+ r.Out = ""
+ }
+ }
+ close(stop)
+ }
+ go scanner(stopOutput, execOutput, false)
+ go scanner(stopError, execError, true)
+ for {
+ select {
+ case <-stop:
+ return
+ case <-stopOutput:
+ return
+ case <-stopError:
+ return
+ }
+ }
+}
+
+// Error occurred
+func (p *Project) err(err error) {
+ msg = fmt.Sprintln(p.pname(p.Name, 2), ":", red.regular(err.Error()))
+ out = BufferOut{Time: time.Now(), Text: err.Error()}
+ p.stamp("error", out, msg, "")
+}
+
+// Defines the colors scheme for the project name
+func (p *Project) pname(name string, color int) string {
+ switch color {
+ case 1:
+ name = yellow.regular("[") + strings.ToUpper(name) + yellow.regular("]")
+ break
+ case 2:
+ name = yellow.regular("[") + red.bold(strings.ToUpper(name)) + yellow.regular("]")
+ break
+ case 3:
+ name = yellow.regular("[") + blue.bold(strings.ToUpper(name)) + yellow.regular("]")
+ break
+ case 4:
+ name = yellow.regular("[") + magenta.bold(strings.ToUpper(name)) + yellow.regular("]")
+ break
+ case 5:
+ name = yellow.regular("[") + green.bold(strings.ToUpper(name)) + yellow.regular("]")
+ break
+ }
+ return name
+}
+
+// Tool logs the result of a go command
+func (p *Project) tools(stop <-chan bool, path string) {
+ if len(path) > 0 {
+ done := make(chan bool)
+ result := make(chan Response)
+ v := reflect.ValueOf(p.Tools)
+ go func() {
+ for i := 0; i < v.NumField()-1; i++ {
+ tool := v.Field(i).Interface().(Tool)
+ if tool.Status && tool.isTool {
+ result <- tool.Exec(path, stop)
+ }
+ }
+ close(done)
+ }()
+ for {
+ select {
+ case <-done:
+ return
+ case <-stop:
+ return
+ case r := <-result:
+ if r.Err != nil {
+ msg = fmt.Sprintln(p.pname(p.Name, 2), ":", red.bold(r.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: r.Name, Stream: r.Err.Error()}
+ p.stamp("error", buff, msg, r.Err.Error())
+ } else if r.Out != "" {
+ msg = fmt.Sprintln(p.pname(p.Name, 3), ":", red.bold(r.Name), red.regular("outputs"), ":", blue.bold(path))
+ buff := BufferOut{Time: time.Now(), Text: "outputs", Path: path, Type: r.Name, Stream: r.Out}
+ p.stamp("out", buff, msg, r.Out)
+ }
+ }
+ }
+ }
+}
+
+// Cmd after/before
+func (p *Project) cmd(stop <-chan bool, flag string, global bool) {
+ done := make(chan bool)
+ result := make(chan Response)
+ // commands sequence
+ go func() {
+ for _, cmd := range p.Watcher.Scripts {
+ if strings.ToLower(cmd.Type) == flag && cmd.Global == global {
+ result <- cmd.Exec(p.Path, stop)
+ }
+ }
+ close(done)
+ }()
+ for {
+ select {
+ case <-stop:
+ return
+ case <-done:
+ return
+ case r := <-result:
+ msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.bold("Command"), green.bold("\"")+r.Name+green.bold("\""))
+ out = BufferOut{Time: time.Now(), Text: r.Name, Type: flag}
+ if r.Err != nil {
+ p.stamp("error", out, msg, "")
+ out = BufferOut{Time: time.Now(), Text: r.Err.Error(), Type: flag}
+ p.stamp("error", out, "", fmt.Sprintln(red.regular(r.Err.Error())))
+ }
+ if r.Out != "" {
+ out = BufferOut{Time: time.Now(), Text: r.Out, Type: flag}
+ p.stamp("log", out, "", fmt.Sprintln(r.Out))
+ } else {
+ p.stamp("log", out, msg, "")
+ }
+ }
+ }
+}
+
+// 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 {
+ s := append([]string{p.Path}, strings.Split(v, string(os.PathSeparator))...)
+ if strings.Contains(path, filepath.Join(s...)) {
+ return nil
+ }
+ }
+ if !strings.HasPrefix(path, ".") && (info.IsDir() || array(ext(path), p.Watcher.Exts)) {
+ result := p.watcher.Walk(path, p.init)
+ if result != "" {
+ if info.IsDir() {
+ p.folders++
+ } else {
+ p.files++
+ }
+ }
+ }
+ return nil
+}
+
+// Print on files, cli, ws
+func (p *Project) stamp(t string, o BufferOut, msg string, stream string) {
+ time := time.Now()
+ content := []string{time.Format("2006-01-02 15:04:05"), strings.ToUpper(p.Name), ":", o.Text, "\r\n", stream}
+ switch t {
+ case "out":
+ p.Buffer.StdOut = append(p.Buffer.StdOut, o)
+ if p.parent.Settings.Files.Outputs.Status {
+ f := p.parent.Settings.Create(p.Path, p.parent.Settings.Files.Outputs.Name)
+ if _, err := f.WriteString(strings.Join(content, " ")); err != nil {
+ p.parent.Settings.Fatal(err, "")
+ }
+ }
+ case "log":
+ p.Buffer.StdLog = append(p.Buffer.StdLog, o)
+ if p.parent.Settings.Files.Logs.Status {
+ f := p.parent.Settings.Create(p.Path, p.parent.Settings.Files.Logs.Name)
+ if _, err := f.WriteString(strings.Join(content, " ")); err != nil {
+ p.parent.Settings.Fatal(err, "")
+ }
+ }
+ case "error":
+ p.Buffer.StdErr = append(p.Buffer.StdErr, o)
+ if p.parent.Settings.Files.Errors.Status {
+ f := p.parent.Settings.Create(p.Path, p.parent.Settings.Files.Errors.Name)
+ if _, err := f.WriteString(strings.Join(content, " ")); err != nil {
+ p.parent.Settings.Fatal(err, "")
+ }
+ }
+ }
+ if msg != "" {
+ log.Print(msg)
+ }
+ if stream != "" {
+ fmt.Fprint(output, stream)
+ }
+}
+
+func (r *Response) printAfter(start time.Time, p *Project) {
+ if r.Err != nil {
+ msg = fmt.Sprintln(p.pname(p.Name, 2), ":", red.bold(r.Name), red.regular(r.Err.Error()))
+ out = BufferOut{Time: time.Now(), Text: r.Err.Error(), Type: r.Name, Stream: r.Out}
+ p.stamp("error", out, msg, r.Out)
+ } else {
+ msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.bold(r.Name), "completed in", magenta.regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
+ out = BufferOut{Time: time.Now(), Text: r.Name + " in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"}
+ p.stamp("log", out, msg, r.Out)
+ }
+}
diff --git a/realize.go b/realize.go
index 5e33324..933cbc6 100644
--- a/realize.go
+++ b/realize.go
@@ -1,48 +1,61 @@
package main
import (
- "errors"
"fmt"
- "github.com/fatih/color"
- "github.com/tockins/interact"
"go/build"
"gopkg.in/urfave/cli.v2"
"log"
"os"
+ "os/signal"
"path/filepath"
- "strconv"
+ "strings"
+ "syscall"
"time"
)
const (
- version = "1.5.2"
+ RPrefix = "realize"
+ RVersion = "2.0"
+ RExt = ".yaml"
+ RFile = RPrefix + RExt
+ RDir = "." + RPrefix
+ RExtWin = ".exe"
)
-// New realize instance
-var r realize
+type (
+ Realize struct {
+ Settings Settings `yaml:"settings" json:"settings"`
+ Server Server `yaml:"server" json:"server"`
+ Schema `yaml:",inline"`
+ sync chan string
+ exit chan os.Signal
+ }
+ LogWriter struct{}
+)
-// Log struct
-type logWriter struct{}
-
-// Realize struct contains the general app informations
-type realize struct {
- Settings Settings `yaml:"settings" json:"settings"`
- Server Server `yaml:"server" json:"server"`
- Schema []Project `yaml:"schema" json:"schema"`
- sync chan string
+func init() {
+ // custom log
+ log.SetFlags(0)
+ log.SetOutput(LogWriter{})
+ if build.Default.GOPATH == "" {
+ log.Fatal("$GOPATH isn't set properly")
+ }
+ if err := os.Setenv("GOBIN", filepath.Join(build.Default.GOPATH, "bin")); err != nil {
+ log.Fatal(err)
+ }
}
-// Cli commands
+// Realize cli commands
func main() {
app := &cli.App{
- 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",
+ Name: strings.Title(RPrefix),
+ Version: RVersion,
+ Description: "Realize is the #1 Golang Task Runner which enhance your workflow by automating the most common tasks and using the best performing Golang live reloading.",
Commands: []*cli.Command{
{
Name: "start",
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 " + strings.Title(RPrefix) + " on a given path. 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"},
&cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: "", Usage: "Run a project by its name"},
@@ -56,27 +69,15 @@ func main() {
&cli.BoolFlag{Name: "run", Aliases: []string{"nr"}, Value: false, Usage: "Enable go run"},
&cli.BoolFlag{Name: "no-config", Aliases: []string{"nc"}, Value: false, Usage: "Ignore existing config and doesn't create a new one"},
},
- Action: func(p *cli.Context) error {
- if err := r.insert(p); err != nil {
- return err
- }
- if !p.Bool("no-config") && p.String("name") == "" {
- if err := r.Settings.record(r); err != nil {
- return err
- }
- }
- if err := r.Server.start(p); err != nil {
- return err
- }
- return r.run(p)
+ Action: func(c *cli.Context) error {
+ return start(c)
},
- Before: before,
},
{
Name: "add",
Category: "Configuration",
Aliases: []string{"a"},
- Description: "Add a project to an existing config file or create a new one",
+ Description: "Add a project to an existing config or to a new one.",
Flags: []cli.Flag{
&cli.StringFlag{Name: "path", Aliases: []string{"p"}, Value: wdir(), Usage: "Project base path"},
&cli.BoolFlag{Name: "fmt", Aliases: []string{"f"}, Value: false, Usage: "Enable go fmt"},
@@ -87,1156 +88,88 @@ func main() {
&cli.BoolFlag{Name: "build", Aliases: []string{"b"}, Value: false, Usage: "Enable go build"},
&cli.BoolFlag{Name: "run", Aliases: []string{"nr"}, Value: false, Usage: "Enable go run"},
},
- Action: func(p *cli.Context) error {
- if err := r.add(p); err != nil {
- return err
- }
- if err := r.Settings.record(r); err != nil {
- return err
- }
- log.Println(prefix(green.bold("Your project was successfully added")))
- return nil
+ Action: func(c *cli.Context) error {
+ return add(c)
},
- Before: before,
},
{
Name: "init",
Category: "Configuration",
Aliases: []string{"i"},
- Description: "Define a new config file with all options step by step",
- Action: func(p *cli.Context) (actErr error) {
- interact.Run(&interact.Interact{
- Before: func(context interact.Context) error {
- context.SetErr(red.bold("INVALID INPUT"))
- context.SetPrfx(color.Output, yellow.regular("[")+time.Now().Format("15:04:05")+yellow.regular("]")+yellow.bold("[")+"REALIZE"+yellow.bold("]"))
- return nil
- },
- Questions: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- if _, err := os.Stat(directory + "/" + file); err != nil {
- d.Skip()
- }
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Would you want to overwrite existing " + magenta.bold("Realize") + " config?",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- } else if val {
- r = new()
- }
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Would you want to customize settings?",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef(0, green.regular("(os default)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[int]"),
- Msg: "Set max number of open files (root required)",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Int()
- if err != nil {
- return d.Err()
- }
- r.Settings.FileLimit = int32(val)
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Force polling watcher?",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef(100, green.regular("(100ms)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[int]"),
- Msg: "Set polling interval",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Int()
- if err != nil {
- return d.Err()
- }
- r.Settings.Legacy.Interval = time.Duration(int(val)) * time.Millisecond
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Settings.Legacy.Force = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable logging files",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Settings.Files.Errors = Resource{Name: fileErr, Status: val}
- r.Settings.Files.Outputs = Resource{Name: fileOut, Status: val}
- r.Settings.Files.Logs = Resource{Name: fileLog, Status: val}
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable web server",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef(port, green.regular("("+strconv.Itoa(port)+")"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[int]"),
- Msg: "Server port",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Int()
- if err != nil {
- return d.Err()
- }
- r.Server.Port = int(val)
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(host, green.regular("("+host+")"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Server host",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Server.Host = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Open in current browser",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Server.Open = val
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Server.Status = val
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- _, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(true, green.regular("(y)"))
- d.SetEnd("!")
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Would you want to " + magenta.regular("add a new project") + "? (insert '!' to stop)",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- if val {
- r.add(p)
- }
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef(wdir(), green.regular("("+wdir()+")"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Project name",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Name = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- dir := wdir()
- d.SetDef(dir, green.regular("("+dir+")"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Project path",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Path = filepath.Clean(val)
- return nil
- },
- },
-
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable go vet",
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(none)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Vet additional arguments",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- if val != "" {
- r.Schema[len(r.Schema)-1].Cmds.Vet.Args = append(r.Schema[len(r.Schema)-1].Cmds.Vet.Args, val)
- }
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Cmds.Vet.Status = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable go fmt",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(none)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Fmt additional arguments",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- if val != "" {
- r.Schema[len(r.Schema)-1].Cmds.Fmt.Args = append(r.Schema[len(r.Schema)-1].Cmds.Fmt.Args, val)
- }
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Cmds.Fmt.Status = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable go test",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(none)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Test additional arguments",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- if val != "" {
- r.Schema[len(r.Schema)-1].Cmds.Test.Args = append(r.Schema[len(r.Schema)-1].Cmds.Test.Args, val)
- }
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Cmds.Test.Status = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable go fix",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(none)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Fix additional arguments",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- if val != "" {
- r.Schema[len(r.Schema)-1].Cmds.Fix.Args = append(r.Schema[len(r.Schema)-1].Cmds.Fix.Args, val)
- }
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Cmds.Fix.Status = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable go clean",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(none)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Clean additional arguments",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- if val != "" {
- r.Schema[len(r.Schema)-1].Cmds.Clean.Args = append(r.Schema[len(r.Schema)-1].Cmds.Clean.Args, val)
- }
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Cmds.Clean.Status = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable go generate",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(none)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Generate additional arguments",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- if val != "" {
- r.Schema[len(r.Schema)-1].Cmds.Generate.Args = append(r.Schema[len(r.Schema)-1].Cmds.Generate.Args, val)
- }
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Cmds.Generate.Status = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(true, green.regular("(y)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable go install",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(none)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Install additional arguments",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- if val != "" {
- r.Schema[len(r.Schema)-1].Cmds.Install.Args = append(r.Schema[len(r.Schema)-1].Cmds.Install.Args, val)
- }
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Cmds.Install.Status = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable go build",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(none)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Build additional arguments",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- if val != "" {
- r.Schema[len(r.Schema)-1].Cmds.Build.Args = append(r.Schema[len(r.Schema)-1].Cmds.Build.Args, val)
- }
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Cmds.Build.Status = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(true, green.regular("(y)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Enable go run",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Cmds.Run = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Customize watching paths",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- if val {
- r.Schema[len(r.Schema)-1].Watcher.Paths = r.Schema[len(r.Schema)-1].Watcher.Paths[:len(r.Schema[len(r.Schema)-1].Watcher.Paths)-1]
- }
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetEnd("!")
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Insert a path to watch (insert '!' to stop)",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Paths = append(r.Schema[len(r.Schema)-1].Watcher.Paths, val)
- d.Reload()
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- _, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Customize ignore paths",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- if val {
- r.Schema[len(r.Schema)-1].Watcher.Ignore = r.Schema[len(r.Schema)-1].Watcher.Ignore[:len(r.Schema[len(r.Schema)-1].Watcher.Ignore)-1]
- }
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetEnd("!")
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Insert a path to ignore (insert '!' to stop)",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Ignore = append(r.Schema[len(r.Schema)-1].Watcher.Ignore, val)
- d.Reload()
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- _, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Add an additional argument",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- d.SetEnd("!")
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Add another argument (insert '!' to stop)",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Args = append(r.Schema[len(r.Schema)-1].Args, val)
- d.Reload()
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- _, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(none)"))
- d.SetEnd("!")
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Add a 'before' custom command (insert '!' to stop)",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Insert a command",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Scripts = append(r.Schema[len(r.Schema)-1].Watcher.Scripts, Command{Type: "before", Command: val})
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Launch from a specific path",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Scripts[len(r.Schema[len(r.Schema)-1].Watcher.Scripts)-1].Path = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Tag as global command",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Scripts[len(r.Schema[len(r.Schema)-1].Watcher.Scripts)-1].Global = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Display command output",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Scripts[len(r.Schema[len(r.Schema)-1].Watcher.Scripts)-1].Output = val
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- if val {
- d.Reload()
- }
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(none)"))
- d.SetEnd("!")
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Add an 'after' custom commands (insert '!' to stop)",
- Resolve: func(d interact.Context) bool {
- val, _ := d.Ans().Bool()
- return val
- },
- },
- Subs: []*interact.Question{
- {
- Before: func(d interact.Context) error {
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Insert a command",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Scripts = append(r.Schema[len(r.Schema)-1].Watcher.Scripts, Command{Type: "after", Command: val})
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Launch from a specific path",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Scripts[len(r.Schema[len(r.Schema)-1].Watcher.Scripts)-1].Path = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Tag as global command",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Scripts[len(r.Schema[len(r.Schema)-1].Watcher.Scripts)-1].Global = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Display command output",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Scripts[len(r.Schema[len(r.Schema)-1].Watcher.Scripts)-1].Output = val
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- if val {
- d.Reload()
- }
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef(false, green.regular("(n)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[y/n]"),
- Msg: "Print watched files on startup",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().Bool()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].Watcher.Preview = val
- return nil
- },
- },
- {
- Before: func(d interact.Context) error {
- d.SetDef("", green.regular("(none)"))
- return nil
- },
- Quest: interact.Quest{
- Options: yellow.regular("[string]"),
- Msg: "Set an error output pattern",
- },
- Action: func(d interact.Context) interface{} {
- val, err := d.Ans().String()
- if err != nil {
- return d.Err()
- }
- r.Schema[len(r.Schema)-1].ErrorOutputPattern = val
- return nil
- },
- },
- },
- Action: func(d interact.Context) interface{} {
- if val, err := d.Ans().Bool(); err != nil {
- return d.Err()
- } else if val {
- d.Reload()
- }
- return nil
- },
- },
- },
- After: func(d interact.Context) error {
- if val, _ := d.Qns().Get(0).Ans().Bool(); val {
- actErr = r.Settings.del(directory)
- if actErr != nil {
- return actErr
- }
- }
- return nil
- },
- })
- if err := r.Settings.record(r); err != nil {
- return err
- }
- log.Println(prefix(green.bold("Your configuration was successful")))
- return nil
+ Description: "Make a new config file step by step.",
+ Action: func(c *cli.Context) error {
+ return setup(c)
},
- Before: before,
},
{
Name: "remove",
Category: "Configuration",
Aliases: []string{"r"},
- Description: "Remove a project from a realize configuration",
+ Description: "Remove a project from an existing config.",
Flags: []cli.Flag{
&cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: ""},
},
- Action: func(p *cli.Context) error {
- if err := r.remove(p); err != nil {
- return err
- }
- if err := r.Settings.record(r); err != nil {
- return err
- }
- log.Println(prefix(green.bold("Your project was successfully removed")))
- return nil
+ Action: func(c *cli.Context) error {
+ return remove(c)
},
- Before: before,
},
{
Name: "clean",
Category: "Configuration",
Aliases: []string{"c"},
- Description: "Remove realize folder",
- Action: func(p *cli.Context) error {
- if err := r.Settings.del(directory); err != nil {
- return err
- }
- log.Println(prefix(green.bold("Realize folder successfully removed")))
- return nil
+ Description: "Remove " + strings.Title(RPrefix) + " folder.",
+ Action: func(c *cli.Context) error {
+ return clean()
},
- Before: before,
},
{
Name: "version",
Aliases: []string{"v"},
- Description: "Realize version",
+ Description: "Print " + strings.Title(RPrefix) + " version.",
Action: func(p *cli.Context) error {
- log.Println(prefix(green.bold(version)))
+ version()
return nil
},
- Before: before,
},
},
}
if err := app.Run(os.Args); err != nil {
- log.Println(prefix(red.bold(err)))
+ log.Fatal(err)
os.Exit(1)
}
}
-// New return default realize config
-func new() realize {
- return realize{
- sync: make(chan string),
- Settings: Settings{
- file: file,
- Legacy: Legacy{
- Interval: 100 * time.Millisecond,
- },
- },
- Server: Server{
- parent: &r,
- Status: false,
- Open: false,
- Host: host,
- Port: port,
- },
- }
+// Stop realize workflow
+func (r *Realize) Stop() {
+ close(r.exit)
}
-// Prefix a given string
-func prefix(s string) string {
- if s != "" {
- return fmt.Sprint(yellow.bold("["), "REALIZE", yellow.bold("]"), " : ", s)
+// Run realize workflow
+func (r *Realize) Start() {
+ r.exit = make(chan os.Signal, 2)
+ signal.Notify(r.exit, os.Interrupt, syscall.SIGTERM)
+ for k := range r.Schema.Projects {
+ r.Schema.Projects[k].parent = r
+ r.Schema.Projects[k].Setup()
+ go r.Schema.Projects[k].Watch(r.exit)
}
- return s
-}
-
-// Before is launched before each command
-func before(*cli.Context) (err error) {
- // custom log
- log.SetFlags(0)
- log.SetOutput(logWriter{})
- // Before of every exec of a cli method
- gopath := build.Default.GOPATH
- if gopath == "" {
- return errors.New("$GOPATH isn't set properly")
- }
- if err = os.Setenv("GOPATH", gopath); err != nil {
- return err
- }
- // new realize instance
- r = new()
- // read if exist
- r.Settings.read(&r)
- // increase the file limit
- if r.Settings.FileLimit != 0 {
- if err = r.Settings.flimit(); err != nil {
- return err
+ for {
+ select {
+ case <-r.exit:
+ return
}
}
- return
+}
+
+// Prefix a given string with tool name
+func (r *Realize) Prefix(input string) string {
+ if len(input) > 0 {
+ return fmt.Sprint(yellow.bold("["), strings.ToUpper(RPrefix), yellow.bold("]"), " : ", input)
+ }
+ return input
}
// Rewrite the layout of the log timestamp
-func (w logWriter) Write(bytes []byte) (int, error) {
+func (w LogWriter) Write(bytes []byte) (int, error) {
return fmt.Fprint(output, yellow.regular("["), time.Now().Format("15:04:05"), yellow.regular("]"), string(bytes))
}
diff --git a/realize_test.go b/realize_test.go
deleted file mode 100644
index f22439b..0000000
--- a/realize_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package main
-
-import (
- "fmt"
- "gopkg.in/urfave/cli.v2"
- "reflect"
- "testing"
-)
-
-func TestPrefix(t *testing.T) {
- input := random(10)
- value := fmt.Sprint(yellow.bold("["), "REALIZE", yellow.bold("]"), " : ", input)
- result := prefix(input)
- if result == "" {
- t.Fatal("Expected a string")
- }
- if result != value {
- t.Fatal("Expected", value, "Instead", result)
- }
-}
-
-func TestBefore(t *testing.T) {
- context := cli.Context{}
- if err := before(&context); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestNew(t *testing.T) {
- r := new()
- if reflect.TypeOf(r).String() != "main.realize" {
- t.Error("Expected a realize struct")
- }
-}
diff --git a/schema.go b/schema.go
new file mode 100644
index 0000000..338298b
--- /dev/null
+++ b/schema.go
@@ -0,0 +1,76 @@
+package main
+
+import (
+ "errors"
+ "gopkg.in/urfave/cli.v2"
+ "path/filepath"
+ "reflect"
+)
+
+type Schema struct {
+ Projects []Project `yaml:"projects" json:"projects"`
+}
+
+// Add a project if unique
+func (s *Schema) Add(p Project) {
+ for _, val := range s.Projects {
+ if reflect.DeepEqual(val, p) {
+ return
+ }
+ }
+ s.Projects = append(s.Projects, p)
+}
+
+// Remove a project
+func (s *Schema) Remove(name string) error {
+ for key, val := range s.Projects {
+ if name == val.Name {
+ s.Projects = append(s.Projects[:key], s.Projects[key+1:]...)
+ return nil
+ }
+ }
+ return errors.New("project not found")
+}
+
+// New create a project using cli fields
+func (s *Schema) New(c *cli.Context) Project {
+ name := filepath.Base(c.String("path"))
+ if name == "." {
+ name = filepath.Base(wdir())
+ }
+ project := Project{
+ Name: name,
+ Path: c.String("path"),
+ Tools: Tools{
+ Vet: Tool{
+ Status: c.Bool("vet"),
+ },
+ Fmt: Tool{
+ Status: c.Bool("fmt"),
+ },
+ Test: Tool{
+ Status: c.Bool("test"),
+ },
+ Generate: Tool{
+ Status: c.Bool("generate"),
+ },
+ Build: Tool{
+ Status: c.Bool("build"),
+ },
+ Install: Tool{
+ Status: c.Bool("install"),
+ },
+ Run: c.Bool("run"),
+ },
+ Args: params(c),
+ Watcher: Watch{
+ Paths: []string{"/"},
+ Ignore: []string{".git", ".realize", "vendor"},
+ Exts: []string{"go"},
+ },
+ }
+ return project
+}
+
+// Filter project list by field
+func (s *Schema) Filter(field string, value interface{}) {}
diff --git a/server.go b/server.go
index 98fd7ed..386b935 100644
--- a/server.go
+++ b/server.go
@@ -2,12 +2,9 @@ package main
import (
"bytes"
- "encoding/json"
"fmt"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
- "golang.org/x/net/websocket"
- "gopkg.in/urfave/cli.v2"
"io"
"net/http"
"os/exec"
@@ -17,13 +14,13 @@ import (
// Dafault host and port
const (
- host = "localhost"
- port = 5001
+ Host = "localhost"
+ Port = 5001
)
// Server settings
type Server struct {
- parent *realize
+ parent *Realize
Status bool `yaml:"status" json:"status"`
Open bool `yaml:"open" json:"open"`
Port int `yaml:"port" json:"port"`
@@ -32,138 +29,40 @@ type Server struct {
// Websocket projects
func (s *Server) projects(c echo.Context) (err error) {
- websocket.Handler(func(ws *websocket.Conn) {
- msg, _ := json.Marshal(s.parent)
- err = websocket.Message.Send(ws, string(msg))
- go func() {
- for {
- select {
- case <-s.parent.sync:
- msg, _ := json.Marshal(s.parent)
- err = websocket.Message.Send(ws, string(msg))
- if err != nil {
- break
- }
- }
- }
- }()
- for {
- // Read
- text := ""
- err = websocket.Message.Receive(ws, &text)
- if err != nil {
- break
- } else {
- err := json.Unmarshal([]byte(text), &s.parent)
- if err == nil {
- s.parent.Settings.record(s.parent)
- break
- }
- }
- }
- ws.Close()
- }).ServeHTTP(c.Response(), c.Request())
+ //websocket.Handler(func(ws *websocket.Conn) {
+ // msg, _ := json.Marshal(s.parent)
+ // err = websocket.Message.Send(ws, string(msg))
+ // go func() {
+ // for {
+ // select {
+ // case <-s.parent.sync:
+ // msg, _ := json.Marshal(s.parent)
+ // err = websocket.Message.Send(ws, string(msg))
+ // if err != nil {
+ // break
+ // }
+ // }
+ // }
+ // }()
+ // for {
+ // // Read
+ // text := ""
+ // err = websocket.Message.Receive(ws, &text)
+ // if err != nil {
+ // break
+ // } else {
+ // err := json.Unmarshal([]byte(text), &s.parent)
+ // if err == nil {
+ // s.parent.Settings.record(s.parent)
+ // break
+ // }
+ // }
+ // }
+ // ws.Close()
+ //}).ServeHTTP(c.Response(), c.Request())
return nil
}
-// Start the web server
-func (s *Server) start(p *cli.Context) (err error) {
- if p.Bool("server") {
- s.parent.Server.Status = true
- }
- if p.Bool("open") {
- s.parent.Server.Open = true
- }
-
- if s.parent.Server.Status {
- e := echo.New()
- e.Use(middleware.GzipWithConfig(middleware.GzipConfig{
- Level: 2,
- }))
- e.Use(middleware.Recover())
-
- // web panel
- e.GET("/", func(c echo.Context) error {
- return s.render(c, "assets/index.html", 1)
- })
- e.GET("/assets/js/all.min.js", func(c echo.Context) error {
- return s.render(c, "assets/assets/js/all.min.js", 2)
- })
- e.GET("/assets/css/app.css", func(c echo.Context) error {
- return s.render(c, "assets/assets/css/app.css", 3)
- })
- e.GET("/app/components/settings/index.html", func(c echo.Context) error {
- return s.render(c, "assets/app/components/settings/index.html", 1)
- })
- e.GET("/app/components/project/index.html", func(c echo.Context) error {
- return s.render(c, "assets/app/components/project/index.html", 1)
- })
- e.GET("/app/components/index.html", func(c echo.Context) error {
- return s.render(c, "assets/app/components/index.html", 1)
- })
- e.GET("/assets/img/svg/settings.svg", func(c echo.Context) error {
- return s.render(c, "assets/assets/img/svg/settings.svg", 4)
- })
- e.GET("/assets/img/svg/fullscreen.svg", func(c echo.Context) error {
- return s.render(c, "assets/assets/img/svg/fullscreen.svg", 4)
- })
- e.GET("/assets/img/svg/add.svg", func(c echo.Context) error {
- return s.render(c, "assets/assets/img/svg/add.svg", 4)
- })
- e.GET("/assets/img/svg/backspace.svg", func(c echo.Context) error {
- return s.render(c, "assets/assets/img/svg/backspace.svg", 4)
- })
- e.GET("/assets/img/svg/error.svg", func(c echo.Context) error {
- return s.render(c, "assets/assets/img/svg/error.svg", 4)
- })
- e.GET("/assets/img/svg/remove.svg", func(c echo.Context) error {
- return s.render(c, "assets/assets/img/remove.svg", 4)
- })
- e.GET("/assets/img/svg/logo.svg", func(c echo.Context) error {
- return s.render(c, "assets/assets/img/svg/logo.svg", 4)
- })
- e.GET("/assets/img/fav.png", func(c echo.Context) error {
- return s.render(c, "assets/assets/img/fav.png", 5)
- })
- e.GET("/assets/img/svg/circle.svg", func(c echo.Context) error {
- return s.render(c, "assets/assets/img/svg/circle.svg", 4)
- })
-
- //websocket
- e.GET("/ws", s.projects)
- e.HideBanner = true
- e.Debug = false
- go e.Start(string(s.parent.Server.Host) + ":" + strconv.Itoa(s.parent.Server.Port))
- _, err = s.openURL("http://" + string(s.parent.Server.Host) + ":" + strconv.Itoa(s.parent.Server.Port))
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-// OpenURL in a new tab of default browser
-func (s *Server) openURL(url string) (io.Writer, error) {
- stderr := bytes.Buffer{}
- cmd := map[string]string{
- "windows": "start",
- "darwin": "open",
- "linux": "xdg-open",
- }
- if s.Open {
- open, err := cmd[runtime.GOOS]
- if !err {
- return nil, fmt.Errorf("operating system %q is not supported", runtime.GOOS)
- }
- cmd := exec.Command(open, url)
- cmd.Stderr = &stderr
- if err := cmd.Run(); err != nil {
- return cmd.Stderr, err
- }
- }
- return nil, nil
-}
-
// Render return a web pages defined in bindata
func (s *Server) render(c echo.Context, path string, mime int) error {
data, err := Asset(path)
@@ -193,3 +92,89 @@ func (s *Server) render(c echo.Context, path string, mime int) error {
rs.Write(data)
return nil
}
+
+// Start the web server
+func (s *Server) Start() (err error) {
+ e := echo.New()
+ e.Use(middleware.GzipWithConfig(middleware.GzipConfig{
+ Level: 2,
+ }))
+ e.Use(middleware.Recover())
+
+ // web panel
+ e.GET("/", func(c echo.Context) error {
+ return s.render(c, "assets/index.html", 1)
+ })
+ e.GET("/assets/js/all.min.js", func(c echo.Context) error {
+ return s.render(c, "assets/assets/js/all.min.js", 2)
+ })
+ e.GET("/assets/css/app.css", func(c echo.Context) error {
+ return s.render(c, "assets/assets/css/app.css", 3)
+ })
+ e.GET("/app/components/settings/index.html", func(c echo.Context) error {
+ return s.render(c, "assets/app/components/settings/index.html", 1)
+ })
+ e.GET("/app/components/project/index.html", func(c echo.Context) error {
+ return s.render(c, "assets/app/components/project/index.html", 1)
+ })
+ e.GET("/app/components/index.html", func(c echo.Context) error {
+ return s.render(c, "assets/app/components/index.html", 1)
+ })
+ e.GET("/assets/img/svg/settings.svg", func(c echo.Context) error {
+ return s.render(c, "assets/assets/img/svg/settings.svg", 4)
+ })
+ e.GET("/assets/img/svg/fullscreen.svg", func(c echo.Context) error {
+ return s.render(c, "assets/assets/img/svg/fullscreen.svg", 4)
+ })
+ e.GET("/assets/img/svg/add.svg", func(c echo.Context) error {
+ return s.render(c, "assets/assets/img/svg/add.svg", 4)
+ })
+ e.GET("/assets/img/svg/backspace.svg", func(c echo.Context) error {
+ return s.render(c, "assets/assets/img/svg/backspace.svg", 4)
+ })
+ e.GET("/assets/img/svg/error.svg", func(c echo.Context) error {
+ return s.render(c, "assets/assets/img/svg/error.svg", 4)
+ })
+ e.GET("/assets/img/svg/remove.svg", func(c echo.Context) error {
+ return s.render(c, "assets/assets/img/remove.svg", 4)
+ })
+ e.GET("/assets/img/svg/logo.svg", func(c echo.Context) error {
+ return s.render(c, "assets/assets/img/svg/logo.svg", 4)
+ })
+ e.GET("/assets/img/fav.png", func(c echo.Context) error {
+ return s.render(c, "assets/assets/img/fav.png", 5)
+ })
+ e.GET("/assets/img/svg/circle.svg", func(c echo.Context) error {
+ return s.render(c, "assets/assets/img/svg/circle.svg", 4)
+ })
+
+ //websocket
+ e.GET("/ws", s.projects)
+ e.HideBanner = true
+ e.Debug = false
+ go e.Start(string(s.parent.Server.Host) + ":" + strconv.Itoa(s.parent.Server.Port))
+ return nil
+}
+
+// OpenURL in a new tab of default browser
+func (s *Server) OpenURL() (io.Writer, error) {
+ url := "http://" + string(s.parent.Server.Host) + ":" + strconv.Itoa(s.parent.Server.Port)
+ stderr := bytes.Buffer{}
+ cmd := map[string]string{
+ "windows": "start",
+ "darwin": "open",
+ "linux": "xdg-open",
+ }
+ if s.Open {
+ open, err := cmd[runtime.GOOS]
+ if !err {
+ return nil, fmt.Errorf("operating system %q is not supported", runtime.GOOS)
+ }
+ cmd := exec.Command(open, url)
+ cmd.Stderr = &stderr
+ if err := cmd.Run(); err != nil {
+ return cmd.Stderr, err
+ }
+ }
+ return nil, nil
+}
diff --git a/server_test.go b/server_test.go
deleted file mode 100644
index 3ff9780..0000000
--- a/server_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package main
-
-//import (
-// "fmt"
-// "net/http"
-// "testing"
-//)
-//
-//func TestServer_Start(t *testing.T) {
-// s := Server{
-// Status: true,
-// Open: false,
-// Host: "localhost",
-// Port: 5000,
-// }
-// err := s.start(nil)
-// if err != nil {
-// t.Fatal(err)
-// }
-// host := "http://localhost:5000/"
-// urls := []string{
-// host,
-// host + "assets/js/all.min.js",
-// host + "assets/css/app.css",
-// host + "app/components/settings/index.html",
-// host + "app/components/project/index.html",
-// host + "app/components/project/index.html",
-// host + "app/components/index.html",
-// host + "assets/img/svg/ic_settings_black_24px.svg",
-// host + "assets/img/svg/ic_fullscreen_black_24px.svg",
-// host + "assets/img/svg/ic_add_black_24px.svg",
-// host + "assets/img/svg/ic_keyboard_backspace_black_24px.svg",
-// host + "assets/img/svg/ic_error_black_48px.svg",
-// host + "assets/img/svg/ic_remove_black_24px.svg",
-// host + "assets/img/svg/logo.svg",
-// host + "assets/img/favicon-32x32.png",
-// host + "assets/img/svg/ic_swap_vertical_circle_black_48px.svg",
-// }
-// for _, elm := range urls {
-// resp, err := http.Get(elm)
-// if err != nil || resp.StatusCode != 200 {
-// t.Fatal(err, resp.StatusCode, elm)
-// }
-// }
-//}
-//
-//func TestServer_Open(t *testing.T) {
-// c := Server{
-// Open: true,
-// }
-// url := "open_test"
-// out, err := c.openURL(url)
-// if err == nil {
-// t.Fatal("Unexpected, invalid url", url, err)
-// }
-// output := fmt.Sprint(out)
-// if output == "" {
-// t.Fatal("Unexpected, invalid url", url, output)
-// }
-//}
diff --git a/settings.go b/settings.go
index e941874..e553500 100644
--- a/settings.go
+++ b/settings.go
@@ -12,12 +12,11 @@ import (
// settings const
const (
- permission = 0775
- directory = ".realize"
- file = "realize.yaml"
- fileOut = "outputs.log"
- fileErr = "errors.log"
- fileLog = "logs.log"
+ Permission = 0775
+ File = "realize.yaml"
+ FileOut = "outputs.log"
+ FileErr = "errors.log"
+ FileLog = "logs.log"
)
// random string preference
@@ -30,7 +29,6 @@ const (
// Settings defines a group of general settings and options
type Settings struct {
- file string
Files `yaml:"files,omitempty" json:"files,omitempty"`
Legacy Legacy `yaml:"legacy" json:"legacy"`
FileLimit int32 `yaml:"flimit,omitempty" json:"flimit,omitempty"`
@@ -75,8 +73,8 @@ func random(n int) string {
return string(b)
}
-// Delete realize folder
-func (s *Settings) del(d string) error {
+// Remove realize folder
+func (s *Settings) Remove(d string) error {
_, err := os.Stat(d)
if !os.IsNotExist(err) {
return os.RemoveAll(d)
@@ -84,23 +82,17 @@ func (s *Settings) del(d string) error {
return err
}
-// Validate checks a fatal error
-func (s Settings) validate(err error) error {
- if err != nil {
- s.fatal(err, "")
- }
- return nil
-}
-
-// Read from config file
-func (s *Settings) read(out interface{}) error {
- localConfigPath := s.file
+// Read config file
+func (s *Settings) Read(out interface{}) error {
+ localConfigPath := RFile
// backward compatibility
- path := filepath.Join(directory, s.file)
+ path := filepath.Join(RDir, RFile)
if _, err := os.Stat(path); err == nil {
localConfigPath = path
+ } else {
+ return nil
}
- content, err := s.stream(localConfigPath)
+ content, err := s.Stream(localConfigPath)
if err == nil {
err = yaml.Unmarshal(content, out)
return err
@@ -108,55 +100,52 @@ func (s *Settings) read(out interface{}) error {
return err
}
-// Record create and unmarshal the yaml config file
-func (s *Settings) record(out interface{}) error {
+// Write config file
+func (s *Settings) Write(out interface{}) error {
y, err := yaml.Marshal(out)
if err != nil {
return err
}
- if _, err := os.Stat(directory); os.IsNotExist(err) {
- if err = os.Mkdir(directory, permission); err != nil {
- return s.write(s.file, y)
+ if _, err := os.Stat(RDir); os.IsNotExist(err) {
+ if err = os.Mkdir(RDir, Permission); err != nil {
+ s.Fatal(ioutil.WriteFile(RFile, y, Permission))
}
}
- return s.write(filepath.Join(directory, s.file), y)
+ s.Fatal(ioutil.WriteFile(filepath.Join(RDir, RFile), y, Permission))
+ return nil
}
// Stream return a byte stream of a given file
-func (s Settings) stream(file string) ([]byte, error) {
+func (s Settings) Stream(file string) ([]byte, error) {
_, err := os.Stat(file)
if err != nil {
return nil, err
}
content, err := ioutil.ReadFile(file)
- s.validate(err)
+ s.Fatal(err)
return content, err
}
// Fatal prints a fatal error with its additional messages
-func (s Settings) fatal(err error, msg ...interface{}) {
- if len(msg) > 0 && err != nil {
- log.Fatalln(red.regular(msg...), err.Error())
- } else if err != nil {
- log.Fatalln(err.Error())
+func (s Settings) Fatal(err error, msg ...interface{}) {
+ if err != nil {
+ if len(msg) > 0 {
+ log.Fatalln(red.regular(msg...), err.Error())
+ } else {
+ log.Fatalln(err.Error())
+ }
}
}
-// Write a file
-func (s Settings) write(name string, data []byte) error {
- err := ioutil.WriteFile(name, data, permission)
- return s.validate(err)
-}
-
// Create a new file and return its pointer
-func (s Settings) create(path string, name string) *os.File {
+func (s Settings) Create(path string, name string) *os.File {
var file string
- if _, err := os.Stat(directory); err == nil {
- file = filepath.Join(path, directory, name)
+ if _, err := os.Stat(RDir); err == nil {
+ file = filepath.Join(path, RDir, name)
} else {
file = filepath.Join(path, name)
}
- out, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE|os.O_SYNC, permission)
- s.validate(err)
+ out, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE|os.O_SYNC, Permission)
+ s.Fatal(err)
return out
}
diff --git a/settings_test.go b/settings_test.go
deleted file mode 100644
index f084abf..0000000
--- a/settings_test.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package main
-
-import (
- "errors"
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-)
-
-func TestSettings_Flimit(t *testing.T) {
- s := Settings{}
- s.FileLimit = 100
- if err := s.flimit(); err != nil {
- t.Fatal("Unable to increase limit", err)
- }
-}
-
-func TestSettings_Stream(t *testing.T) {
- s := Settings{}
- filename := random(4)
- if _, err := s.stream(filename); err == nil {
- t.Fatal("Error expected, none found", filename, err)
- }
-
- filename = "settings.go"
- if _, err := s.stream(filename); err != nil {
- t.Fatal("Error unexpected", filename, err)
- }
-}
-
-func TestSettings_Write(t *testing.T) {
- s := Settings{}
- data := "abcdefgh"
- d, err := ioutil.TempFile("", "io_test")
- if err != nil {
- t.Fatal(err)
- }
- if err := s.write(d.Name(), []byte(data)); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestSettings_Create(t *testing.T) {
- s := Settings{}
- p, err := os.Getwd()
- if err != nil {
- t.Fatal(err)
- }
- f := s.create(p, "io_test")
- os.Remove(f.Name())
-}
-
-func TestSettings_Read(t *testing.T) {
- s := Settings{}
- var a interface{}
- s.file = "settings_b"
- if err := s.read(a); err == nil {
- t.Fatal("Error unexpected", err)
- }
-
- s.file = "settings_test.yaml"
- dir, err := ioutil.TempDir("", directory)
- if err != nil {
- t.Fatal(err)
- }
- d, err := ioutil.TempFile(dir, "settings_test.yaml")
- if err != nil {
- t.Fatal(err)
- }
- s.file = d.Name()
- if err := s.read(a); err != nil {
- t.Fatal("Error unexpected", err)
- }
-}
-
-func TestSettings_Del(t *testing.T) {
- s := Settings{}
- if err := s.del("abcd"); err == nil {
- t.Fatal("Error unexpected, dir dosn't exist", err)
- }
-
- d, err := ioutil.TempDir("", "settings_test")
- if err != nil {
- t.Fatal(err)
- }
- if err := s.del(d); err != nil {
- t.Fatal("Error unexpected, dir exist", err)
- }
-}
-
-func TestSettings_Record(t *testing.T) {
- s := Settings{}
- s.file = "settings_test.yaml"
- var a interface{}
- if err := s.record(a); err != nil {
- t.Fatal(err)
- }
- s.del(filepath.Join(directory, s.file))
-}
-
-func TestSettings_Validate(t *testing.T) {
- s := Settings{}
- input := errors.New("")
- input = nil
- if err := s.validate(input); err != nil {
- t.Error("Expected", input, "instead", err)
- }
-}
-
-func TestSettings_Fatal(t *testing.T) {
- s := Settings{}
- s.fatal(nil, "test")
-}
diff --git a/settings_unix.go b/settings_unix.go
index 9e72710..1afb3f0 100644
--- a/settings_unix.go
+++ b/settings_unix.go
@@ -5,7 +5,7 @@ package main
import "syscall"
// Flimit defines the max number of watched files
-func (s *Settings) flimit() error {
+func (s *Settings) Flimit() error {
var rLimit syscall.Rlimit
rLimit.Max = uint64(s.FileLimit)
rLimit.Cur = uint64(s.FileLimit)
diff --git a/settings_windows.go b/settings_windows.go
index 3a11d85..13dc18a 100644
--- a/settings_windows.go
+++ b/settings_windows.go
@@ -3,6 +3,6 @@
package main
// Flimit defines the max number of watched files
-func (s *Settings) flimit() error {
+func (s *Settings) Flimit() error {
return nil
}
diff --git a/tools.go b/tools.go
new file mode 100644
index 0000000..7313a58
--- /dev/null
+++ b/tools.go
@@ -0,0 +1,160 @@
+package main
+
+import (
+ "bytes"
+ "errors"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+type Tool struct {
+ Args []string `yaml:"args,omitempty" json:"args,omitempty"`
+ Method string `yaml:"method,omitempty" json:"method,omitempty"`
+ Status bool `yaml:"status,omitempty" json:"status,omitempty"`
+ dir bool
+ isTool bool
+ method []string
+ cmd []string
+ name string
+}
+
+type Tools struct {
+ Fix Tool `yaml:"fix,omitempty" json:"fix,omitempty"`
+ Clean Tool `yaml:"clean,omitempty" json:"clean,omitempty"`
+ Vet Tool `yaml:"vet,omitempty" json:"vet,omitempty"`
+ Fmt Tool `yaml:"fmt,omitempty" json:"fmt,omitempty"`
+ Test Tool `yaml:"test,omitempty" json:"test,omitempty"`
+ Generate Tool `yaml:"generate,omitempty" json:"generate,omitempty"`
+ Install Tool `yaml:"install,omitempty" json:"install,omitempty"`
+ Build Tool `yaml:"build,omitempty" json:"build,omitempty"`
+ Run bool `yaml:"run,omitempty" json:"run,omitempty"`
+}
+
+func (t *Tools) Setup() {
+ // go clean
+ if t.Clean.Status {
+ t.Clean.name = "Clean"
+ t.Clean.isTool = true
+ t.Clean.cmd = replace([]string{"go clean"}, t.Clean.Method)
+ t.Clean.Args = split([]string{}, t.Clean.Args)
+ }
+ // go generate
+ if t.Generate.Status {
+ t.Generate.dir = true
+ t.Generate.isTool = true
+ t.Generate.name = "Generate"
+ t.Generate.cmd = replace([]string{"go", "generate"}, t.Generate.Method)
+ t.Generate.Args = split([]string{}, t.Generate.Args)
+ }
+ // go fix
+ if t.Fix.Status {
+ t.Fix.name = "Fix"
+ t.Fix.isTool = true
+ t.Fix.cmd = replace([]string{"go fix"}, t.Fix.Method)
+ t.Fix.Args = split([]string{}, t.Fix.Args)
+ }
+ // go fmt
+ if t.Fmt.Status {
+ if len(t.Fmt.Args) == 0 {
+ t.Fmt.Args = []string{"-s", "-w", "-e", "./"}
+ }
+ t.Fmt.name = "Fmt"
+ t.Fmt.isTool = true
+ t.Fmt.cmd = replace([]string{"gofmt"}, t.Fmt.Method)
+ t.Fmt.Args = split([]string{}, t.Fmt.Args)
+ }
+ // go vet
+ if t.Vet.Status {
+ t.Vet.dir = true
+ t.Vet.name = "Vet"
+ t.Vet.isTool = true
+ t.Vet.cmd = replace([]string{"go", "vet"}, t.Vet.Method)
+ t.Vet.Args = split([]string{}, t.Vet.Args)
+ }
+ // go test
+ if t.Test.Status {
+ t.Test.dir = true
+ t.Test.isTool = true
+ t.Test.name = "Test"
+ t.Test.cmd = replace([]string{"go", "test"}, t.Test.Method)
+ t.Test.Args = split([]string{}, t.Test.Args)
+ }
+ // go install
+ t.Install.name = "Install"
+ t.Install.cmd = replace([]string{"go", "install"}, t.Install.Method)
+ t.Install.Args = split([]string{}, t.Install.Args)
+ // go build
+ if t.Build.Status {
+ t.Build.name = "Build"
+ t.Build.cmd = replace([]string{"go", "build"}, t.Build.Method)
+ t.Build.Args = split([]string{}, t.Build.Args)
+ }
+}
+
+func (t *Tool) Exec(path string, stop <-chan bool) (response Response) {
+ if t.dir && filepath.Ext(path) != "" {
+ path = filepath.Dir(path)
+ }
+ if strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "") {
+ args := []string{}
+ if strings.HasSuffix(path, ".go") {
+ args = append(t.Args, path)
+ path = filepath.Dir(path)
+ }
+ if s := ext(path); s == "" || s == "go" {
+ var out, stderr bytes.Buffer
+ done := make(chan error)
+ args = append(t.cmd, t.Args...)
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Dir = path
+ cmd.Stdout = &out
+ cmd.Stderr = &stderr
+ // Start command
+ cmd.Start()
+ go func() { done <- cmd.Wait() }()
+ // Wait a result
+ select {
+ case <-stop:
+ // Stop running command
+ cmd.Process.Kill()
+ case err := <-done:
+ // Command completed
+ response.Name = t.name
+ if err != nil {
+ response.Err = errors.New(stderr.String() + out.String())
+ } else {
+ response.Out = out.String()
+ }
+ }
+ }
+ }
+ return
+}
+
+func (t *Tool) Compile(path string, stop <-chan bool) (response Response) {
+ var out bytes.Buffer
+ var stderr bytes.Buffer
+ done := make(chan error)
+ args := append(t.cmd, t.Args...)
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Dir = filepath.Dir(path)
+ cmd.Stdout = &out
+ cmd.Stderr = &stderr
+ // Start command
+ cmd.Start()
+ go func() { done <- cmd.Wait() }()
+ // Wait a result
+ select {
+ case <-stop:
+ // Stop running command
+ cmd.Process.Kill()
+ case err := <-done:
+ // Command completed
+ response.Name = t.name
+ if err != nil {
+ response.Err = errors.New(stderr.String() + err.Error())
+ }
+ }
+ return
+}
diff --git a/utils.go b/utils.go
index 94fa86e..27419ae 100644
--- a/utils.go
+++ b/utils.go
@@ -76,7 +76,7 @@ func replace(a []string, b string) []string {
func wdir() string {
dir, err := os.Getwd()
if err != nil {
- log.Fatal(prefix(err.Error()))
+ log.Fatal(err.Error())
}
return dir
}
diff --git a/watcher.go b/watcher.go
deleted file mode 100644
index 0e6d5e2..0000000
--- a/watcher.go
+++ /dev/null
@@ -1,532 +0,0 @@
-package main
-
-import (
- "fmt"
- "github.com/fsnotify/fsnotify"
- "log"
- "math/big"
- "os"
- "os/signal"
- "path/filepath"
- "strconv"
- "strings"
- "sync"
- "syscall"
- "time"
-)
-
-var (
- msg string
- out BufferOut
- wg sync.WaitGroup
-)
-
-const (
- msgStop = "killed"
- extWindows = ".exe"
-)
-
-// Watch struct defines options for livereload
-type Watch struct {
- Preview bool `yaml:"preview,omitempty" json:"preview,omitempty"`
- Paths []string `yaml:"paths" json:"paths"`
- Exts []string `yaml:"extensions" json:"extensions"`
- Ignore []string `yaml:"ignored_paths,omitempty" json:"ignored_paths,omitempty"`
- Scripts []Command `yaml:"scripts,omitempty" json:"scripts,omitempty"`
-}
-
-// Result channel with data stream and errors
-type Result struct {
- stream string
- err error
-}
-
-// Buffer define an array buffer for each log files
-type Buffer struct {
- StdOut []BufferOut `json:"stdOut"`
- StdLog []BufferOut `json:"stdLog"`
- StdErr []BufferOut `json:"stdErr"`
-}
-
-// Project defines the informations of a single project
-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
- Name string `yaml:"name" json:"name"`
- Path string `yaml:"path" json:"path"`
- Environment map[string]string `yaml:"environment,omitempty" json:"environment,omitempty"`
- Cmds Cmds `yaml:"commands" json:"commands"`
- Args []string `yaml:"args,omitempty" json:"args,omitempty"`
- Watcher Watch `yaml:"watcher" json:"watcher"`
- Buffer Buffer `yaml:"-" json:"buffer"`
- ErrorOutputPattern string `yaml:"errorOutputPattern,omitempty" json:"errorOutputPattern,omitempty"`
-}
-
-// Command options
-type Command struct {
- Type string `yaml:"type" json:"type"`
- Command string `yaml:"command" json:"command"`
- Path string `yaml:"path,omitempty" json:"path,omitempty"`
- Global bool `yaml:"global,omitempty" json:"global,omitempty"`
- Output bool `yaml:"output,omitempty" json:"output,omitempty"`
-}
-
-// BufferOut is used for exchange information between "realize cli" and "web realize"
-type BufferOut struct {
- Time time.Time `json:"time"`
- Text string `json:"text"`
- Path string `json:"path"`
- Type string `json:"type"`
- Stream string `json:"stream"`
- Errors []string `json:"errors"`
-}
-
-// Watch the project
-func (p *Project) watch() {
- p.watcher, _ = Watcher(r.Settings.Legacy.Force, r.Settings.Legacy.Interval)
- stop, exit := make(chan bool), make(chan os.Signal, 2)
- signal.Notify(exit, os.Interrupt, syscall.SIGTERM)
- // before global commands
- p.cmd(stop, "before", true)
- // indexing files and dirs
- for _, dir := range p.Watcher.Paths {
- base, _ := filepath.Abs(p.Path)
- base = filepath.Join(base, dir)
- if _, err := os.Stat(base); err == nil {
- if err := filepath.Walk(base, p.walk); err == nil {
- p.tool(stop, base)
- }
- } else {
- p.err(err)
- }
- }
- // indexing done, files and folders
- msg = fmt.Sprintln(p.pname(p.Name, 1), ":", blue.bold("Watching"), magenta.bold(p.files), "file/s", magenta.bold(p.folders), "folder/s")
- out = BufferOut{Time: time.Now(), Text: "Watching " + strconv.FormatInt(p.files, 10) + " files/s " + strconv.FormatInt(p.folders, 10) + " folder/s"}
- p.stamp("log", out, msg, "")
- // start
- go p.routines(stop, p.watcher, "")
- //is watching
-L:
- for {
- 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:
- p.watcher.Remove(event.Name)
- if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) {
- close(stop)
- stop = make(chan bool)
- p.stamp("log", out, msg, "")
- go p.routines(stop, p.watcher, "")
- }
- default:
- file, err := os.Stat(event.Name)
- if err != nil {
- continue
- }
- 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)
- }
- if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) {
- // change watched
- // check if a file is still writing #119
- if event.Op != fsnotify.Write || (eventTime.Truncate(time.Millisecond).After(file.ModTime().Truncate(time.Millisecond)) || event.Name != p.lastFile) {
- 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
- }
- }
- }
- case err := <-p.watcher.Errors():
- p.err(err)
- case <-exit:
- p.cmd(nil, "after", true)
- break L
- }
- }
- wg.Done()
- return
-}
-
-// Error occurred
-func (p *Project) err(err error) {
- msg = fmt.Sprintln(p.pname(p.Name, 2), ":", red.regular(err.Error()))
- out = BufferOut{Time: time.Now(), Text: err.Error()}
- p.stamp("error", out, msg, "")
-}
-
-// Config project init
-func (p *Project) config(r *realize) {
- // get basepath name
- p.name = filepath.Base(p.Path)
- // env variables
- for key, item := range p.Environment {
- if err := os.Setenv(key, item); err != nil {
- p.Buffer.StdErr = append(p.Buffer.StdErr, BufferOut{Time: time.Now(), Text: err.Error(), Type: "Env error", Stream: ""})
- }
- }
- // init commands
- if len(p.Cmds.Fmt.Args) == 0 {
- p.Cmds.Fmt.Args = []string{"-s", "-w", "-e", "./"}
- }
- 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.Generate.Status,
- cmd: replace([]string{"go", "generate"}, p.Cmds.Generate.Method),
- options: split([]string{}, p.Cmds.Generate.Args),
- name: "Generate",
- dir: true,
- })
- 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.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,
- cmd: replace([]string{"go", "vet"}, p.Cmds.Vet.Method),
- options: split([]string{}, p.Cmds.Vet.Args),
- 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...),
- method: replace([]string{"go", "install"}, p.Cmds.Install.Method),
- name: "Install",
- startTxt: "Installing...",
- endTxt: "Installed",
- }
- p.Cmds.Build = Cmd{
- Status: p.Cmds.Build.Status,
- Args: append([]string{}, p.Cmds.Build.Args...),
- method: replace([]string{"go", "build"}, p.Cmds.Build.Method),
- name: "Build",
- startTxt: "Building...",
- endTxt: "Built",
- }
- p.parent = r
-}
-
-// Cmd calls the method that execute commands after/before and display the results
-func (p *Project) cmd(stop <-chan bool, flag string, global bool) {
- done := make(chan bool)
- // cmds are scheduled in sequence
- go func() {
- for _, cmd := range p.Watcher.Scripts {
- if strings.ToLower(cmd.Type) == flag && cmd.Global == global {
- err, logs := p.command(stop, cmd)
- if err == "" && logs == "" {
- continue
- }
- msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.bold("Command"), green.bold("\"")+cmd.Command+green.bold("\""))
- out = BufferOut{Time: time.Now(), Text: cmd.Command, Type: flag}
- if err != "" {
- p.stamp("error", out, msg, "")
-
- msg = fmt.Sprintln(red.regular(err))
- out = BufferOut{Time: time.Now(), Text: err, Type: flag}
- p.stamp("error", out, "", msg)
-
- } else if logs != "" && cmd.Output {
- msg = fmt.Sprintln(logs)
- out = BufferOut{Time: time.Now(), Text: logs, Type: flag}
- p.stamp("log", out, "", msg)
- } else {
- p.stamp("log", out, msg, "")
- }
- }
- }
- close(done)
- }()
- for {
- select {
- case <-stop:
- return
- case <-done:
- return
- }
- }
-}
-
-// 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 {
- 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}
- }
- }()
- select {
- case r := <-channel:
- if r.err != nil {
- msg = fmt.Sprintln(p.pname(p.Name, 2), ":", red.bold(cmd.name), red.regular(r.err.Error()))
- out = BufferOut{Time: time.Now(), Text: r.err.Error(), Type: cmd.name, Stream: r.stream}
- p.stamp("error", out, msg, r.stream)
- } else {
- msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.regular(cmd.endTxt), "in", magenta.regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
- out = BufferOut{Time: time.Now(), Text: cmd.name + " in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"}
- p.stamp("log", out, msg, r.stream)
- }
- return r.err
- case <-stop:
- return nil
- }
- }
- return nil
-}
-
-// Defines the colors scheme for the project name
-func (p *Project) pname(name string, color int) string {
- switch color {
- case 1:
- name = yellow.regular("[") + strings.ToUpper(name) + yellow.regular("]")
- break
- case 2:
- name = yellow.regular("[") + red.bold(strings.ToUpper(name)) + yellow.regular("]")
- break
- case 3:
- name = yellow.regular("[") + blue.bold(strings.ToUpper(name)) + yellow.regular("]")
- break
- case 4:
- name = yellow.regular("[") + magenta.bold(strings.ToUpper(name)) + yellow.regular("]")
- break
- case 5:
- name = yellow.regular("[") + green.bold(strings.ToUpper(name)) + yellow.regular("]")
- break
- }
- return name
-}
-
-// Tool logs the result of a go command
-func (p *Project) tool(stop <-chan bool, path string) error {
- if len(path) > 0 {
- done := make(chan bool)
- result := make(chan tool)
- go func() {
- var wg sync.WaitGroup
- for _, element := range p.tools {
- // no need a sequence, these commands can be asynchronous
- if element.status {
- wg.Add(1)
- p.goTool(&wg, stop, result, path, element)
- }
- }
- wg.Wait()
- close(done)
- }()
- loop:
- for {
- select {
- case tool := <-result:
- 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:
- break loop
- }
- }
- }
- return nil
-}
-
-// 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 {
- s := append([]string{p.Path}, strings.Split(v, string(os.PathSeparator))...)
- if strings.Contains(path, filepath.Join(s...)) {
- return nil
- }
- }
- if !strings.HasPrefix(path, ".") && (info.IsDir() || array(ext(path), p.Watcher.Exts)) {
- result := p.watcher.Walk(path, p.init)
- if result != "" {
- if info.IsDir() {
- p.folders++
- } else {
- p.files++
- }
- if p.Watcher.Preview {
- log.Println(p.pname(p.Name, 1), ":", path)
- }
- }
- }
- return nil
-}
-
-// Print on files, cli, ws
-func (p *Project) stamp(t string, o BufferOut, msg string, stream string) {
- switch t {
- case "out":
- p.Buffer.StdOut = append(p.Buffer.StdOut, o)
- if p.Files.Outputs.Status {
- f := p.create(p.Path, p.Files.Outputs.Name)
- t := time.Now()
- s := []string{t.Format("2006-01-02 15:04:05"), strings.ToUpper(p.Name), ":", o.Text, "\r\n"}
- if _, err := f.WriteString(strings.Join(s, " ")); err != nil {
- p.fatal(err, "")
- }
- }
- case "log":
- p.Buffer.StdLog = append(p.Buffer.StdLog, o)
- if p.Files.Logs.Status {
- f := p.create(p.Path, p.Files.Logs.Name)
- t := time.Now()
- s := []string{t.Format("2006-01-02 15:04:05"), strings.ToUpper(p.Name), ":", o.Text, "\r\n"}
- if stream != "" {
- s = []string{t.Format("2006-01-02 15:04:05"), strings.ToUpper(p.Name), ":", o.Text, "\r\n", stream}
- }
- if _, err := f.WriteString(strings.Join(s, " ")); err != nil {
- p.fatal(err, "")
- }
- }
- case "error":
- p.Buffer.StdErr = append(p.Buffer.StdErr, o)
- if p.Files.Errors.Status {
- f := p.create(p.Path, p.Files.Errors.Name)
- t := time.Now()
- s := []string{t.Format("2006-01-02 15:04:05"), strings.ToUpper(p.Name), ":", o.Type, o.Text, o.Path, "\r\n"}
- if stream != "" {
- s = []string{t.Format("2006-01-02 15:04:05"), strings.ToUpper(p.Name), ":", o.Type, o.Text, o.Path, "\r\n", stream}
- }
- if _, err := f.WriteString(strings.Join(s, " ")); err != nil {
- p.fatal(err, "")
- }
- }
- }
- if msg != "" {
- log.Print(msg)
- }
- if stream != "" {
- fmt.Fprint(output, stream)
- }
- go func() {
- p.parent.sync <- "sync"
- }()
-}
-
-// 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 {
- case <-stop:
- done = true
- return
- }
- }
- }()
- if done {
- return
- }
- // 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 {
- return
- }
- install = p.compile(stop, p.Cmds.Install)
- if done {
- 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 {
- return
- }
- p.cmd(stop, "after", false)
-}
diff --git a/watcher_test.go b/watcher_test.go
deleted file mode 100644
index 223711f..0000000
--- a/watcher_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package main
-
-import (
- "testing"
-
- "os"
-)
-
-type fileWatcherMock struct {
- FileWatcher
-}
-
-func (f *fileWatcherMock) Walk(path string, _ bool) string {
- return path
-}
-
-type fileInfoMock struct {
- os.FileInfo
- FileIsDir bool
-}
-
-func (m *fileInfoMock) IsDir() bool { return m.FileIsDir }
-
-func TestWalk(t *testing.T) {
- p := Project{
- Name: "Test Project",
- Watcher: Watch{
- Paths: []string{"/"},
- Ignore: []string{"vendor"},
- Exts: []string{"go"},
- },
- Path: "/go/project",
- watcher: &fileWatcherMock{},
- init: true,
- }
-
- files := []struct {
- Path string
- IsDir bool
- }{
- // valid files
- {"/go/project", true},
- {"/go/project/main.go", false},
- {"/go/project/main_test.go", false},
- // invalid relative path
- {"./relative/path", true},
- {"./relative/path/file.go", false},
- // invalid extension
- {"/go/project/settings.yaml", false},
- // invalid vendor files
- {"/go/project/vendor/foo", true},
- {"/go/project/vendor/foo/main.go", false},
- }
-
- for _, file := range files {
- fileInfo := fileInfoMock{
- FileIsDir: file.IsDir,
- }
- err := p.walk(file.Path, &fileInfo, nil)
- if err != nil {
- t.Errorf("Error not expected: %s", err)
- }
- }
-
- if p.files != 2 {
- t.Errorf("Exepeted %d files, but was %d", 2, p.files)
- }
-
- if p.folders != 1 {
- t.Errorf("Exepeted %d folders, but was %d", 2, p.folders)
- }
-}