commit
13ee93c5f7
41
realize.go
41
realize.go
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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:"-"`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue