Merge pull request #176 from oxequa/develop

#156 #175 #160
This commit is contained in:
Alessio Pracchia 2018-04-14 16:25:41 +02:00 committed by GitHub
commit 13ee93c5f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 169 additions and 138 deletions

View File

@ -38,6 +38,7 @@ func main() {
&cli.BoolFlag{Name: "install", Aliases: []string{"i"}, Value: false, Usage: "Enable go install"}, &cli.BoolFlag{Name: "install", Aliases: []string{"i"}, Value: false, Usage: "Enable go install"},
&cli.BoolFlag{Name: "build", Aliases: []string{"b"}, Value: false, Usage: "Enable go build"}, &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"}, &cli.BoolFlag{Name: "run", Aliases: []string{"nr"}, Value: false, Usage: "Enable go run"},
&cli.BoolFlag{Name: "legacy", Aliases: []string{"l"}, Value: false, Usage: "Legacy watch by polling instead fsnotify"},
&cli.BoolFlag{Name: "no-config", Aliases: []string{"nc"}, Value: false, Usage: "Ignore existing config and doesn't create a new one"}, &cli.BoolFlag{Name: "no-config", Aliases: []string{"nc"}, Value: false, Usage: "Ignore existing config and doesn't create a new one"},
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
@ -794,7 +795,7 @@ func setup(c *cli.Context) (err error) {
Resolve: func(d interact.Context) bool { Resolve: func(d interact.Context) bool {
val, _ := d.Ans().Bool() val, _ := d.Ans().Bool()
if val { 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] r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Ignore.Paths = r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Ignore.Paths[:len(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Ignore.Paths)-1]
} }
return val return val
}, },
@ -814,7 +815,7 @@ func setup(c *cli.Context) (err error) {
if err != nil { if err != nil {
return d.Err() 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) r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Ignore.Paths = append(r.Schema.Projects[len(r.Schema.Projects)-1].Watcher.Ignore.Paths, val)
d.Reload() d.Reload()
return nil return nil
}, },
@ -1080,7 +1081,7 @@ func setup(c *cli.Context) (err error) {
if err != nil { if err != nil {
return d.Err() return d.Err()
} }
r.Schema.Projects[len(r.Schema.Projects)-1].ErrorOutputPattern = val r.Schema.Projects[len(r.Schema.Projects)-1].ErrPattern = val
return nil return nil
}, },
}, },
@ -1116,7 +1117,14 @@ func setup(c *cli.Context) (err error) {
// Start realize workflow // Start realize workflow
func start(c *cli.Context) (err error) { func start(c *cli.Context) (err error) {
r.Server = realize.Server{Parent: &r, Status: false, Open: false, Port: realize.Port, Host: realize.Host} // set legacy watcher
if c.Bool("legacy") {
r.Settings.Legacy.Set(c.Bool("legacy"), 1)
}
// set server
if c.Bool("server") {
r.Server.Set(c.Bool("server"), c.Bool("open"), realize.Port, realize.Host)
}
// check no-config and read // check no-config and read
if !c.Bool("no-config") { if !c.Bool("no-config") {
// read a config if exist // read a config if exist
@ -1133,20 +1141,9 @@ func start(c *cli.Context) (err error) {
} }
} }
// 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
}
}
// check project list length // check project list length
if len(r.Schema.Projects) <= 0 { if len(r.Schema.Projects) <= 0 {
println("len", r.Schema.Projects)
// create a new project based on given params // create a new project based on given params
project := r.Schema.New(c) project := r.Schema.New(c)
// Add to projects list // Add to projects list
@ -1159,6 +1156,18 @@ func start(c *cli.Context) (err error) {
} }
} }
} }
// Start web server
if r.Server.Status {
r.Server.Parent = &r
err = r.Server.Start()
if err != nil {
return err
}
err = r.Server.OpenURL()
if err != nil {
return err
}
}
// start workflow // start workflow
return r.Start() return r.Start()
} }

View File

@ -18,7 +18,7 @@ var (
// RPrefix tool name // RPrefix tool name
RPrefix = "realize" RPrefix = "realize"
// RVersion current version // RVersion current version
RVersion = "2.0.1" RVersion = "2.1"
// RExt file extension // RExt file extension
RExt = ".yaml" RExt = ".yaml"
// RFile config file name // RFile config file name
@ -34,7 +34,7 @@ type (
// Realize main struct // Realize main struct
Realize struct { Realize struct {
Settings Settings `yaml:"settings" json:"settings"` Settings Settings `yaml:"settings" json:"settings"`
Server Server `yaml:"server" json:"server"` Server Server `yaml:"server,omitempty" json:"server,omitempty"`
Schema `yaml:",inline" json:",inline"` Schema `yaml:",inline" json:",inline"`
Sync chan string `yaml:"-" json:"-"` Sync chan string `yaml:"-" json:"-"`
Err Func `yaml:"-" json:"-"` Err Func `yaml:"-" json:"-"`

View File

@ -2,7 +2,7 @@ package realize
// this code is imported from moby, unfortunately i can't import it directly as dependencies from its repo, // this code is imported from moby, unfortunately i can't import it directly as dependencies from its repo,
// cause there was a problem between moby vendor and fsnotify // cause there was a problem between moby vendor and fsnotify
// i have just added only walk methods and some little changes to polling interval, originally set as static. // i have just added only the walk methods and some little changes to polling interval, originally set as static.
import ( import (
"errors" "errors"
@ -57,7 +57,7 @@ type (
// PollingWatcher returns a poll-based file watcher // PollingWatcher returns a poll-based file watcher
func PollingWatcher(interval time.Duration) FileWatcher { func PollingWatcher(interval time.Duration) FileWatcher {
if interval == 0 { if interval == 0 {
interval = 100 * time.Millisecond interval = time.Duration(1) * time.Second
} }
return &filePoller{ return &filePoller{
interval: interval, interval: interval,
@ -67,13 +67,13 @@ func PollingWatcher(interval time.Duration) FileWatcher {
} }
// NewFileWatcher tries to use an fs-event watcher, and falls back to the poller if there is an error // NewFileWatcher tries to use an fs-event watcher, and falls back to the poller if there is an error
func NewFileWatcher(force bool, interval time.Duration) (FileWatcher, error) { func NewFileWatcher(l Legacy) (FileWatcher, error) {
if !force { if !l.Force {
if w, err := EventWatcher(); err == nil { if w, err := EventWatcher(); err == nil {
return w, nil return w, nil
} }
} }
return PollingWatcher(interval), nil return PollingWatcher(l.Interval), nil
} }
// EventWatcher returns an fs-event based file watcher // EventWatcher returns an fs-event based file watcher

View File

@ -26,17 +26,22 @@ var (
// Watch info // Watch info
type Watch struct { type Watch struct {
Paths []string `yaml:"paths" json:"paths"`
Exts []string `yaml:"extensions" json:"extensions"` Exts []string `yaml:"extensions" json:"extensions"`
Ignore []string `yaml:"ignored_paths,omitempty" json:"ignored_paths,omitempty"` Paths []string `yaml:"paths" json:"paths"`
Scripts []Command `yaml:"scripts,omitempty" json:"scripts,omitempty"` Scripts []Command `yaml:"scripts,omitempty" json:"scripts,omitempty"`
Hidden bool `yaml:"skip_hidden,omitempty" json:"skip_hidden,omitempty"` Hidden bool `yaml:"hidden,omitempty" json:"hidden,omitempty"`
Ignore Ignore `yaml:"ignore,omitempty" json:"ignore,omitempty"`
}
type Ignore struct{
Exts []string `yaml:"exts,omitempty" json:"exts,omitempty"`
Paths []string `yaml:"paths,omitempty" json:"paths,omitempty"`
} }
// Command fields // Command fields
type Command struct { type Command struct {
Type string `yaml:"type" json:"type"`
Cmd string `yaml:"command" json:"command"` Cmd string `yaml:"command" json:"command"`
Type string `yaml:"type" json:"type"`
Path string `yaml:"path,omitempty" json:"path,omitempty"` Path string `yaml:"path,omitempty" json:"path,omitempty"`
Global bool `yaml:"global,omitempty" json:"global,omitempty"` Global bool `yaml:"global,omitempty" json:"global,omitempty"`
Output bool `yaml:"output,omitempty" json:"output,omitempty"` Output bool `yaml:"output,omitempty" json:"output,omitempty"`
@ -46,21 +51,21 @@ type Command struct {
type Project struct { type Project struct {
parent *Realize parent *Realize
watcher FileWatcher watcher FileWatcher
init bool
exit chan os.Signal
stop chan bool stop chan bool
exit chan os.Signal
paths []string
last last
files int64 files int64
folders int64 folders int64
last last init bool
paths []string
Name string `yaml:"name" json:"name"` Name string `yaml:"name" json:"name"`
Path string `yaml:"path" json:"path"` Path string `yaml:"path" json:"path"`
Environment map[string]string `yaml:"environment,omitempty" json:"environment,omitempty"` Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
Tools Tools `yaml:"commands" json:"commands"`
Args []string `yaml:"args,omitempty" json:"args,omitempty"` Args []string `yaml:"args,omitempty" json:"args,omitempty"`
Tools Tools `yaml:"commands" json:"commands"`
Watcher Watch `yaml:"watcher" json:"watcher"` Watcher Watch `yaml:"watcher" json:"watcher"`
Buffer Buffer `yaml:"-" json:"buffer"` Buffer Buffer `yaml:"-" json:"buffer"`
ErrorOutputPattern string `yaml:"errorOutputPattern,omitempty" json:"errorOutputPattern,omitempty"` ErrPattern string `yaml:"pattern,omitempty" json:"pattern,omitempty"`
} }
// Last is used to save info about last file changed // Last is used to save info about last file changed
@ -111,7 +116,7 @@ func (p *Project) Before() {
// setup go tools // setup go tools
p.Tools.Setup() p.Tools.Setup()
// set env const // set env const
for key, item := range p.Environment { for key, item := range p.Env {
if err := os.Setenv(key, item); err != nil { 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.Buffer.StdErr = append(p.Buffer.StdErr, BufferOut{Time: time.Now(), Text: err.Error(), Type: "Env error", Stream: ""})
} }
@ -191,7 +196,7 @@ func (p *Project) Reload(path string, stop <-chan bool) {
} }
// Go supported tools // Go supported tools
if len(path) > 0 { if len(path) > 0 {
fi, err := os.Stat(filepath.Dir(path)) fi, err := os.Stat(path)
if filepath.Ext(path) == "" { if filepath.Ext(path) == "" {
fi, err = os.Stat(path) fi, err = os.Stat(path)
} }
@ -274,7 +279,7 @@ func (p *Project) Watch(wg *sync.WaitGroup) {
// change channel // change channel
p.stop = make(chan bool) p.stop = make(chan bool)
// init a new watcher // init a new watcher
p.watcher, err = NewFileWatcher(p.parent.Settings.Legacy.Force, p.parent.Settings.Legacy.Interval) p.watcher, err = NewFileWatcher(p.parent.Settings.Legacy)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -347,14 +352,28 @@ func (p *Project) Validate(path string, fcheck bool) bool {
} }
// check for a valid ext or path // check for a valid ext or path
if e := ext(path); e != "" { if e := ext(path); e != "" {
// supported exts if len(p.Watcher.Exts) == 0{
if !array(e, p.Watcher.Exts) {
return false return false
} }
// check ignored
for _, v := range p.Watcher.Ignore.Exts {
if v == e {
return false
}
}
// supported extensions
for index, v := range p.Watcher.Exts{
if e == v {
break
}
if index == len(p.Watcher.Exts)-1{
return false
}
}
} }
separator := string(os.PathSeparator) separator := string(os.PathSeparator)
// supported paths // supported paths
for _, v := range p.Watcher.Ignore { for _, v := range p.Watcher.Ignore.Paths {
s := append([]string{p.Path}, strings.Split(v, separator)...) s := append([]string{p.Path}, strings.Split(v, separator)...)
abs, _ := filepath.Abs(filepath.Join(s...)) abs, _ := filepath.Abs(filepath.Join(s...))
if path == abs || strings.HasPrefix(path, abs+separator) { if path == abs || strings.HasPrefix(path, abs+separator) {
@ -364,16 +383,9 @@ func (p *Project) Validate(path string, fcheck bool) bool {
// file check // file check
if fcheck { if fcheck {
fi, err := os.Stat(path) fi, err := os.Stat(path)
if err != nil { if err != nil || fi.Mode()&os.ModeSymlink != 0 || !fi.IsDir() && ext(path) == "" || fi.Size() <= 0{
return false return false
} }
if !fi.IsDir() && ext(path) == "" {
return false
}
if fi.Size() > 0 {
return true
}
return false
} }
return true return true
@ -485,9 +497,9 @@ func (p *Project) walk(path string, info os.FileInfo, err error) error {
if p.parent.Settings.Recovery.Index { if p.parent.Settings.Recovery.Index {
log.Println("Indexing", path) log.Println("Indexing", path)
} }
p.tools(p.stop, path, info)
if info.IsDir() { if info.IsDir() {
// tools dir // tools dir
p.tools(p.stop, path, info)
p.folders++ p.folders++
} else { } else {
// tools files // tools files
@ -532,7 +544,7 @@ func (p *Project) stamp(t string, o BufferOut, msg string, stream string) {
log.Print(msg) log.Print(msg)
} }
if stream != "" { if stream != "" {
fmt.Fprint(Output, stream) fmt.Fprintln(Output, stream)
} }
go func() { go func() {
p.parent.Sync <- "sync" p.parent.Sync <- "sync"
@ -547,14 +559,16 @@ func (p *Project) run(path string, stream chan Response, stop <-chan bool) (err
defer func() { defer func() {
// https://github.com/golang/go/issues/5615 // https://github.com/golang/go/issues/5615
// https://github.com/golang/go/issues/6720 // https://github.com/golang/go/issues/6720
if build != nil {
build.Process.Signal(os.Interrupt) build.Process.Signal(os.Interrupt)
}
}() }()
// custom error pattern // custom error pattern
isErrorText := func(string) bool { isErrorText := func(string) bool {
return false return false
} }
errRegexp, err := regexp.Compile(p.ErrorOutputPattern) errRegexp, err := regexp.Compile(p.ErrPattern)
if err != nil { if err != nil {
r.Err = err r.Err = err
stream <- r stream <- r

View File

@ -48,7 +48,7 @@ func TestProject_Before(t *testing.T) {
r = Realize{} r = Realize{}
r.Projects = append(r.Projects, Project{ r.Projects = append(r.Projects, Project{
parent: &r, parent: &r,
Environment: map[string]string{ Env: map[string]string{
input: input, input: input,
}, },
}) })
@ -100,7 +100,9 @@ func TestProject_Reload(t *testing.T) {
parent: &r, parent: &r,
}) })
input := "test/path" input := "test/path"
r.Projects[0].watcher, _ = NewFileWatcher(false, 0) r.Settings.Legacy.Force = false
r.Settings.Legacy.Interval = 0
r.Projects[0].watcher, _ = NewFileWatcher(r.Settings.Legacy)
r.Reload = func(context Context) { r.Reload = func(context Context) {
log.Println(context.Path) log.Println(context.Path)
} }
@ -126,12 +128,16 @@ func TestProject_Validate(t *testing.T) {
r.Projects = append(r.Projects, Project{ r.Projects = append(r.Projects, Project{
parent: &r, parent: &r,
Watcher: Watch{ Watcher: Watch{
Ignore: []string{"/test/ignore"}, Exts: []string{},
Ignore: Ignore{
Paths:[]string{"/test/ignore"},
},
}, },
}) })
for i, v := range data { for i, v := range data {
if r.Projects[0].Validate(i, false) != v { result := r.Projects[0].Validate(i, false)
t.Error("Unexpected error", i, "expected", v) if result != v {
t.Error("Unexpected error", i, "expected", v, result)
} }
} }
} }

View File

@ -68,7 +68,9 @@ func (s *Schema) New(c *cli.Context) Project {
Args: params(c), Args: params(c),
Watcher: Watch{ Watcher: Watch{
Paths: []string{"/"}, Paths: []string{"/"},
Ignore: []string{".git", ".realize", "vendor"}, Ignore: Ignore{
Paths:[]string{".git", ".realize", "vendor"},
},
Exts: []string{"go"}, Exts: []string{"go"},
}, },
} }

View File

@ -9,12 +9,12 @@ import (
"github.com/labstack/echo" "github.com/labstack/echo"
"github.com/labstack/echo/middleware" "github.com/labstack/echo/middleware"
"golang.org/x/net/websocket" "golang.org/x/net/websocket"
"io"
"log" "log"
"net/http" "net/http"
"os/exec" "os/exec"
"runtime" "runtime"
"strconv" "strconv"
"github.com/go-siris/siris/core/errors"
) )
// Dafault host and port // Dafault host and port
@ -98,8 +98,16 @@ func (s *Server) render(c echo.Context, path string, mime int) error {
return nil return nil
} }
func (s *Server) Set(status bool, open bool, port int, host string) {
s.Open = open
s.Port = port
s.Host = host
s.Status = status
}
// Start the web server // Start the web server
func (s *Server) Start() (err error) { func (s *Server) Start() (err error) {
if s.Status {
e := echo.New() e := echo.New()
e.Use(middleware.GzipWithConfig(middleware.GzipConfig{ e.Use(middleware.GzipWithConfig(middleware.GzipConfig{
Level: 2, Level: 2,
@ -146,17 +154,18 @@ func (s *Server) Start() (err error) {
//websocket //websocket
e.GET("/ws", s.projects) e.GET("/ws", s.projects)
// e.HideBanner = true e.HideBanner = true
e.Debug = false e.Debug = false
go func() { go func() {
log.Println(s.Parent.Prefix("Started on " + string(s.Host) + ":" + strconv.Itoa(s.Port))) log.Println(s.Parent.Prefix("Started on " + string(s.Host) + ":" + strconv.Itoa(s.Port)))
e.Start(string(s.Host) + ":" + strconv.Itoa(s.Port)) e.Start(string(s.Host) + ":" + strconv.Itoa(s.Port))
}() }()
}
return nil return nil
} }
// OpenURL in a new tab of default browser // OpenURL in a new tab of default browser
func (s *Server) OpenURL() (io.Writer, error) { func (s *Server) OpenURL() error {
url := "http://" + string(s.Parent.Server.Host) + ":" + strconv.Itoa(s.Parent.Server.Port) url := "http://" + string(s.Parent.Server.Host) + ":" + strconv.Itoa(s.Parent.Server.Port)
stderr := bytes.Buffer{} stderr := bytes.Buffer{}
cmd := map[string]string{ cmd := map[string]string{
@ -167,13 +176,13 @@ func (s *Server) OpenURL() (io.Writer, error) {
if s.Open { if s.Open {
open, err := cmd[runtime.GOOS] open, err := cmd[runtime.GOOS]
if !err { if !err {
return nil, fmt.Errorf("operating system %q is not supported", runtime.GOOS) return fmt.Errorf("operating system %q is not supported", runtime.GOOS)
} }
cmd := exec.Command(open, url) cmd := exec.Command(open, url)
cmd.Stderr = &stderr cmd.Stderr = &stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return cmd.Stderr, err return errors.New(stderr.String())
} }
} }
return nil, nil return nil
} }

View File

@ -61,6 +61,12 @@ type Resource struct {
Name string Name string
} }
// Set legacy watcher with an interval
func (l *Legacy) Set(status bool, interval int){
l.Force = true
l.Interval = time.Duration(interval) * time.Second
}
// Remove realize folder // Remove realize folder
func (s *Settings) Remove(d string) error { func (s *Settings) Remove(d string) error {
_, err := os.Stat(d) _, err := os.Stat(d)

View File

@ -43,7 +43,7 @@ func (t *Tools) Setup() {
if t.Clean.Status { if t.Clean.Status {
t.Clean.name = "Clean" t.Clean.name = "Clean"
t.Clean.isTool = true t.Clean.isTool = true
t.Clean.cmd = replace([]string{"go clean"}, t.Clean.Method) t.Clean.cmd = replace([]string{"go", "clean"}, t.Clean.Method)
t.Clean.Args = split([]string{}, t.Clean.Args) t.Clean.Args = split([]string{}, t.Clean.Args)
} }
// go generate // go generate
@ -134,7 +134,12 @@ func (t *Tool) Exec(path string, stop <-chan bool) (response Response) {
cmd.Stdout = &out cmd.Stdout = &out
cmd.Stderr = &stderr cmd.Stderr = &stderr
// Start command // Start command
cmd.Start() err := cmd.Start()
if err != nil{
response.Name = t.name
response.Err = err
return
}
go func() { done <- cmd.Wait() }() go func() { done <- cmd.Wait() }()
// Wait a result // Wait a result
select { select {
@ -145,7 +150,7 @@ func (t *Tool) Exec(path string, stop <-chan bool) (response Response) {
// Command completed // Command completed
response.Name = t.name response.Name = t.name
if err != nil { if err != nil {
response.Err = errors.New(stderr.String() + out.String()) response.Err = errors.New(stderr.String() + out.String() + err.Error())
} else { } else {
if t.Output { if t.Output {
response.Out = out.String() response.Out = out.String()

View File

@ -8,16 +8,6 @@ import (
"strings" "strings"
) )
// Array check if a string is in given array
func array(str string, list []string) bool {
for _, v := range list {
if v == str {
return true
}
}
return false
}
// Params parse one by one the given argumentes // Params parse one by one the given argumentes
func params(params *cli.Context) []string { func params(params *cli.Context) []string {
argsN := params.NArg() argsN := params.NArg()

View File

@ -43,16 +43,6 @@ func TestDuplicates(t *testing.T) {
} }
func TestArray(t *testing.T) {
arr := []string{"a", "b", "c"}
if !array(arr[0], arr) {
t.Fatal("Unexpected", arr[0], "should be in", arr)
}
if array("d", arr) {
t.Fatal("Unexpected", "d", "shouldn't be in", arr)
}
}
func TestWdir(t *testing.T) { func TestWdir(t *testing.T) {
expected, err := os.Getwd() expected, err := os.Getwd()
if err != nil { if err != nil {