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: "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: "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"},
},
Action: func(c *cli.Context) error {
@ -794,7 +795,7 @@ func setup(c *cli.Context) (err error) {
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]
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
},
@ -814,7 +815,7 @@ func setup(c *cli.Context) (err error) {
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)
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()
return nil
},
@ -1080,7 +1081,7 @@ func setup(c *cli.Context) (err error) {
if err != nil {
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
},
},
@ -1116,7 +1117,14 @@ func setup(c *cli.Context) (err error) {
// Start realize workflow
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
if !c.Bool("no-config") {
// 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
if len(r.Schema.Projects) <= 0 {
println("len", r.Schema.Projects)
// create a new project based on given params
project := r.Schema.New(c)
// 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
return r.Start()
}

View File

@ -18,7 +18,7 @@ var (
// RPrefix tool name
RPrefix = "realize"
// RVersion current version
RVersion = "2.0.1"
RVersion = "2.1"
// RExt file extension
RExt = ".yaml"
// RFile config file name
@ -34,7 +34,7 @@ type (
// Realize main struct
Realize struct {
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"`
Sync chan string `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,
// 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 (
"errors"
@ -57,7 +57,7 @@ type (
// PollingWatcher returns a poll-based file watcher
func PollingWatcher(interval time.Duration) FileWatcher {
if interval == 0 {
interval = 100 * time.Millisecond
interval = time.Duration(1) * time.Second
}
return &filePoller{
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
func NewFileWatcher(force bool, interval time.Duration) (FileWatcher, error) {
if !force {
func NewFileWatcher(l Legacy) (FileWatcher, error) {
if !l.Force {
if w, err := EventWatcher(); err == nil {
return w, nil
}
}
return PollingWatcher(interval), nil
return PollingWatcher(l.Interval), nil
}
// EventWatcher returns an fs-event based file watcher

View File

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

View File

@ -48,7 +48,7 @@ func TestProject_Before(t *testing.T) {
r = Realize{}
r.Projects = append(r.Projects, Project{
parent: &r,
Environment: map[string]string{
Env: map[string]string{
input: input,
},
})
@ -100,7 +100,9 @@ func TestProject_Reload(t *testing.T) {
parent: &r,
})
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) {
log.Println(context.Path)
}
@ -126,12 +128,16 @@ func TestProject_Validate(t *testing.T) {
r.Projects = append(r.Projects, Project{
parent: &r,
Watcher: Watch{
Ignore: []string{"/test/ignore"},
Exts: []string{},
Ignore: Ignore{
Paths:[]string{"/test/ignore"},
},
},
})
for i, v := range data {
if r.Projects[0].Validate(i, false) != v {
t.Error("Unexpected error", i, "expected", v)
result := r.Projects[0].Validate(i, false)
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),
Watcher: Watch{
Paths: []string{"/"},
Ignore: []string{".git", ".realize", "vendor"},
Ignore: Ignore{
Paths:[]string{".git", ".realize", "vendor"},
},
Exts: []string{"go"},
},
}

View File

@ -9,12 +9,12 @@ import (
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"golang.org/x/net/websocket"
"io"
"log"
"net/http"
"os/exec"
"runtime"
"strconv"
"github.com/go-siris/siris/core/errors"
)
// Dafault host and port
@ -98,8 +98,16 @@ func (s *Server) render(c echo.Context, path string, mime int) error {
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
func (s *Server) Start() (err error) {
if s.Status {
e := echo.New()
e.Use(middleware.GzipWithConfig(middleware.GzipConfig{
Level: 2,
@ -146,17 +154,18 @@ func (s *Server) Start() (err error) {
//websocket
e.GET("/ws", s.projects)
// e.HideBanner = true
e.HideBanner = true
e.Debug = false
go func() {
log.Println(s.Parent.Prefix("Started on " + string(s.Host) + ":" + strconv.Itoa(s.Port)))
e.Start(string(s.Host) + ":" + strconv.Itoa(s.Port))
}()
}
return nil
}
// 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)
stderr := bytes.Buffer{}
cmd := map[string]string{
@ -167,13 +176,13 @@ func (s *Server) OpenURL() (io.Writer, error) {
if s.Open {
open, err := cmd[runtime.GOOS]
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.Stderr = &stderr
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
}
// 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
func (s *Settings) Remove(d string) error {
_, err := os.Stat(d)

View File

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

View File

@ -8,16 +8,6 @@ import (
"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
func params(params *cli.Context) []string {
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) {
expected, err := os.Getwd()
if err != nil {