Merge pull request #46 from tockins/dev

v1.3
This commit is contained in:
Alessio Pracchia 2017-04-01 20:55:51 +02:00 committed by GitHub
commit b1d3486324
15 changed files with 1524 additions and 505 deletions

View File

@ -6,4 +6,10 @@ go:
- tip - tip
matrix: matrix:
allow_failures: allow_failures:
- go: tip - go: tip
install:
- go get ./...
script:
- go install .

328
README.md
View File

@ -17,176 +17,206 @@ A Go build system with file watchers, output streams and live reload. Run, build
#### Features #### Features
- Highly customizable - Highly customizable
- Config your project Step by Step
- Build, Install, Test, Fmt, Generate and Run at the same time - Build, Install, Test, Fmt, Generate and Run at the same time
- Live reload on file changes (re-build, re-install...) - Live reload on file changes (re-build, re-install...)
- Watch custom paths and specific file extensions - Watch custom paths and specific file extensions
- Watch by FsNotify or by polling
- Support for multiple projects - Support for multiple projects
- Output streams and error logs (Watch them in console or save them on a file) - Output streams and error logs (support for save on a file)
- Web Panel (Watch all projects, edit the config settings, download each type of log) - Web Panel (projects list, config settings, logs)
#### Installation and usage #### Wiki
- Run this to get/install: - [Getting Started](#installation-and-usage)
- [Run cmd](#run) - Run a project
- [Add cmd](#add) - Add a new project
- [Init cmd](#init) - Make a custom config step by step
- [Remove cmd](#remove) - Remove a project
- [List cmd](#list) - List the projects
- [Config sample](#config-sample)
- [Support](#support)
```
$ go get github.com/tockins/realize
```
- From project/projects root execute: - ##### Installation
Run this to get/install:
``` ```
$ realize add $ go get github.com/tockins/realize
``` ```
#### Commands
It will create a realize.yaml file if it doesn't exist already and adds the working directory as project.
Otherwise if a config file already exists it adds the working project to the existing config file.
The Add command supports the following custom parameters:
```
--name="Project Name" -> Name, if not specified takes the working directory name
--path="server" -> Custom Path, if not specified takes the working directory name
--build -> Enables the build
--test -> Enables the tests
--no-bin -> Disables the installation
--no-run -> Disables the run
--no-fmt -> Disables the fmt (go fmt)
--no-server -> Disables the web panel (default port 5001)
--open -> Open the web panel in a new browser window
```
Examples:
```
$ realize add
$ realize add --path="mypath"
$ realize add --name="My Project" --build
$ realize add --name="My Project" --path="/projects/package" --build
$ realize add --name="My Project" --path="projects/package" --build --no-run
$ realize add --path="/Users/alessio/go/src/github.com/tockins/realize-examples/coin/"
```
If you want, you can specify additional arguments for your project.
**The additional arguments must go after the options of "Realize"**
```
$ realize add --path="/print/printer" --no-run yourParams --yourFlags // correct
$ realize add yourParams --yourFlags --path="/print/printer" --no-run // wrong
```
- Remove a project by its name
```
$ realize remove --name="Project Name"
```
- Lists all projects
```
$ realize list
```
- Build, Run and watch file changes. Realize will re-build and re-run your projects on each change.
- ##### Run
From project/projects root execute:
``` ```
$ realize run $ realize run
``` ```
Run can also launch a project from its working directory with or without make a config file (--no-config option). It will create a realize.yaml file if it doesn't exist already, adds the working directory as project and run the pipeline.
It supports the following custom parameters:
The Run command supports the following custom parameters:
``` ```
--path="server" -> Custom Path, if not specified takes the working directory name --path="realize/server" -> Custom Path, if not specified takes the working directory name
--build -> Enables the build --build -> Enable go build
--test -> Enables the tests --no-run -> Disable go run
--config -> Take the defined settings if exist a config file --no-install -> Disable go install
--no-bin -> Disables the installation --no-config -> Ignore an existing config / skip the creation of a new one
--no-run -> Disables the run --server -> Enable the web server
--no-fmt -> Disables the fmt (go fmt) --legacy -> Enable legacy watch instead of Fsnotify watch
--no-server -> Disables the web panel (port :5000) --generate -> Enable go generate
--no-config -> Doesn't create any configuration files --test -> Enable go test
--open -> Open the web panel in a new browser window ```
``` Examples:
And additional arguments as the "add" command.
``` ```
$ realize run --no-run yourParams --yourFlags // correct $ realize run
$ realize run --path="mypath"
$ realize run yourParams --yourFlags --no-run // wrong $ realize run --name="My Project" --build
$ realize run --path="realize" --no-run --no-config
$ realize run --path="/Users/alessio/go/src/github.com/tockins/realize-examples/coin/" $ realize run --path="/Users/alessio/go/src/github.com/tockins/realize-examples/coin/"
``` ```
If you want, you can specify additional arguments for your project.
**The additional arguments must go after the params**
**Run can run a project from its working directory without make a config file (--no-config).**
```
$ realize run --path="/print/printer" --no-run yourParams --yourFlags // right
$ realize run yourParams --yourFlags --path="/print/printer" --no-run // wrong
```
- ##### Add
Add a project to an existing config file or create a new one without run the pipeline.
"Add" supports the same parameters of the "Run" command.
```
$ realize add
```
#### Color reference - ##### Init
Like add, but with this command you can create a configuration step by step and customize each option.
**Init is the only command that supports a complete customization of all the options supported**
```
$ realize init
```
- Blue: outputs of the project - ##### Remove
- Red: errors Remove a project by its name
- Magenta: times or changed files ```
- Green: successfully completed action $ realize remove --name="myname"
```
- ##### List
Projects list in cli
```
$ realize list
```
- #### Color reference
- Blue: outputs of the project
- Red: errors
- Magenta: times or changed files
- Green: successfully completed action
#### Config file example - #### Config sample
- For more examples check [Realize Examples](https://github.com/tockins/realize-examples) For more examples check [Realize Examples](https://github.com/tockins/realize-examples)
``` ```
settings: settings:
resources: legacy:
output: outputs.log // name of the output file status: true // legacy watch status
log: logs.log // name of the log file (errors included) interval: 10s // polling interval
server: resources: // files names related to streams
enable: true // enables the web server outputs: outputs.log
open: false // opens the web server in a new tab logs: logs.log
host: localhost // web server host errors: errors.log
port: 5000 // wev server port server:
config: status: true // server status
flimit: 0 // increases the maximum number of open files - supported only on linux or os x, sudo required open: false // auto open in browser on start
projects: host: localhost // server host
- name: printer // project name port: 5001 // server port
path: / // project path projects:
run: true // enables go run (require bin) - name: realize
bin: true // enables go install path: . // project path
generate: false // enables go generate fmt: true
build: false // enables go build generate: false
fmt: true // enables go fmt test: false
test: false // enables go test bin: true
params: [] // array of additionals params. the project will be launched with these parameters build: false
watcher: run: false
before: [] // custom commands launched before the execution of the project params: // additional params
after: [] // custom commands launched after the execution of the project - --myarg
paths: // paths to observe for live reload watcher:
- / preview: false // wached files preview
ignore_paths: // paths to ignore paths: // paths to watch
- vendor - /
exts: // file extensions to observe for live reload ignore_paths: // paths to ignore
- .go - vendor
preview: true // prints the observed files on startup exts: // exts to watch
cli: - .go
streams: true // prints the output streams of the project in the cli scripts: // custom commands after/before
file: - type: after // type after/before
streams: false // saves the output stream of the project in a file command: go run mycmd after // command
logs: false // saves the logs of the project in a file path: "" // run from a custom path or from the working dir
errors: false // saves the errors of the project in a file streams: // enable/disable streams
``` cli_out: true
file_out: false
#### Next features, in progress... file_log: false
file_err: false
- [ ] Web panel - edit settings (full support)
- [ ] Web panel - logs download ```
- [ ] Schedule - reload a project after a specific time This is the configuration used for develop realize
- [ ] Easy dependencies - automatically resolve the project dependencies
- [ ] Import license - retrieve the license for each imported library ```
- [ ] Tests settings:
resources:
outputs: outputs.log
logs: logs.log
errors: errors.log
server:
status: false
open: false
host: localhost
port: 5001
projects:
- name: realize
path: /Users/alessio/go/src/github.com/tockins/realize
fmt: true
generate: false
test: false
bin: true
build: false
run: false
watcher:
preview: false
paths:
- /
ignore_paths:
- server/assets
exts:
- .go
scripts:
- type: before
command: go-bindata -pkg="server" assets/...
path: server
- type: after
command: go-bindata -pkg="server" assets/...
path: server
streams:
cli_out: true
file_out: false
file_log: false
file_err: false
```
#### Contacts ###### Support us and suggest an improvement
- Start the project
- Chat with us [Gitter](https://gitter.im/tockins/realize) - Chat with us [Gitter](https://gitter.im/tockins/realize)
- Suggest a new [Feature](https://github.com/tockins/realize/issues/new)
- [Alessio Pracchia](https://www.linkedin.com/in/alessio-pracchia-38a70673)
- [Daniele Conventi](https://www.linkedin.com/in/conventi)

140
lock.json Normal file
View File

@ -0,0 +1,140 @@
{
"memo": "0dae7877c338a668c3b0b2c64112895cd2076b90c0792ca2a0edf22fb10d14d2",
"projects": [
{
"name": "github.com/dgrijalva/jwt-go",
"version": "v3.0.0",
"revision": "d2709f9f1f31ebcda9651b03077758c1f3a0018c",
"packages": [
"."
]
},
{
"name": "github.com/fatih/color",
"version": "v1.4.1",
"revision": "9131ab34cf20d2f6d83fdc67168a5430d1c7dc23",
"packages": [
"."
]
},
{
"name": "github.com/fsnotify/fsnotify",
"branch": "master",
"revision": "4da3e2cfbabc9f751898f250b49f2439785783a1",
"packages": [
"."
]
},
{
"name": "github.com/labstack/echo",
"version": "v3.0.3",
"revision": "8d504c1b699c757b267255c53b3e5219f9974abc",
"packages": [
".",
"middleware"
]
},
{
"name": "github.com/labstack/gommon",
"version": "v0.2.0",
"revision": "9cedb429ffbe71a32a3ae7c65fd109cb7ae07804",
"packages": [
"bytes",
"color",
"log",
"random"
]
},
{
"name": "github.com/mattn/go-colorable",
"version": "v0.0.7",
"revision": "d228849504861217f796da67fae4f6e347643f15",
"packages": [
"."
]
},
{
"name": "github.com/mattn/go-isatty",
"version": "v0.0.2",
"revision": "fc9e8d8ef48496124e79ae0df75490096eccf6fe",
"packages": [
"."
]
},
{
"name": "github.com/tockins/interact",
"branch": "master",
"revision": "7dfb115b4849a7d87b2b8368a076959c2bf56e35",
"packages": [
"."
]
},
{
"name": "github.com/tylerb/graceful",
"version": "v1.2.15",
"revision": "4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb",
"packages": [
"."
]
},
{
"name": "github.com/valyala/bytebufferpool",
"branch": "master",
"revision": "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7",
"packages": [
"."
]
},
{
"name": "github.com/valyala/fasttemplate",
"branch": "master",
"revision": "dcecefd839c4193db0d35b88ec65b4c12d360ab0",
"packages": [
"."
]
},
{
"name": "golang.org/x/crypto",
"branch": "master",
"revision": "3cb07270c9455e8ad27956a70891c962d121a228",
"packages": [
"acme",
"acme/autocert"
]
},
{
"name": "golang.org/x/net",
"branch": "master",
"revision": "ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d",
"packages": [
"context",
"context/ctxhttp",
"websocket"
]
},
{
"name": "golang.org/x/sys",
"branch": "master",
"revision": "9a7256cb28ed514b4e1e5f68959914c4c28a92e0",
"packages": [
"unix"
]
},
{
"name": "gopkg.in/urfave/cli.v2",
"branch": "v2",
"revision": "04b2f4ff79cf1fd71e138bafc67df8bbdb5b81c2",
"packages": [
"."
]
},
{
"name": "gopkg.in/yaml.v2",
"branch": "v2",
"revision": "a3f3340b5840cee44f372bddb5880fcbc419b46a",
"packages": [
"."
]
}
]
}

25
manifest.json Normal file
View File

@ -0,0 +1,25 @@
{
"dependencies": {
"github.com/fatih/color": {
"branch": "master"
},
"github.com/fsnotify/fsnotify": {
"branch": "master"
},
"github.com/labstack/echo": {
"version": "^3.0.3"
},
"github.com/tockins/interact": {
"branch": "master"
},
"golang.org/x/net": {
"branch": "master"
},
"gopkg.in/urfave/cli.v2": {
"branch": "v2"
},
"gopkg.in/yaml.v2": {
"branch": "v2"
}
}
}

View File

@ -3,25 +3,27 @@ package main
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/fatih/color"
i "github.com/tockins/interact"
s "github.com/tockins/realize/server" s "github.com/tockins/realize/server"
c "github.com/tockins/realize/settings" c "github.com/tockins/realize/settings"
w "github.com/tockins/realize/watcher" w "github.com/tockins/realize/watcher"
"gopkg.in/urfave/cli.v2" "gopkg.in/urfave/cli.v2"
"os" "os"
"time"
) )
const ( const (
name = "Realize" name = "Realize"
version = "1.2.1" version = "1.3"
description = "A Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths" description = "A Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths"
config = "realize.yaml" config = "realize.yaml"
streams = "streams.log" outputs = "outputs.log"
errs = "errors.log" errs = "errors.log"
logs = "logs.log" logs = "logs.log"
host = "localhost" host = "localhost"
port = 5001 port = 5001
server = true interval = 200
open = false
) )
var r realize var r realize
@ -45,19 +47,19 @@ func init() {
Sync: make(chan string), Sync: make(chan string),
Settings: c.Settings{ Settings: c.Settings{
Config: c.Config{ Config: c.Config{
Flimit: 0, Create: true,
}, },
Resources: c.Resources{ Resources: c.Resources{
Config: config, Config: config,
Streams: streams, Outputs: outputs,
Logs: logs, Logs: logs,
Errors: errs, Errors: errs,
}, },
Server: c.Server{ Server: c.Server{
Enabled: server, Status: false,
Open: open, Open: false,
Host: host, Host: host,
Port: port, Port: port,
}, },
}, },
} }
@ -83,8 +85,6 @@ func init() {
// Before of every exec of a cli method // Before of every exec of a cli method
func before() error { func before() error {
fmt.Println(r.Blue.Bold(name) + " - " + r.Blue.Bold(version))
fmt.Println(r.Blue.Regular(description) + "\n")
gopath := os.Getenv("GOPATH") gopath := os.Getenv("GOPATH")
if gopath == "" { if gopath == "" {
return handle(errors.New("$GOPATH isn't set up properly")) return handle(errors.New("$GOPATH isn't set up properly"))
@ -103,59 +103,49 @@ func handle(err error) error {
// Cli commands // Cli commands
func main() { func main() {
c := &cli.App{ app := &cli.App{
Name: r.Name, Name: r.Name,
Version: r.Version, Version: r.Version,
Authors: []*cli.Author{ Authors: []*cli.Author{
{ {
Name: "Alessio Pracchia", Name: "Alessio Pracchia",
Email: "pracchia@hastegit", Email: "pracchia@hastega.it",
}, },
{ {
Name: "Daniele Conventi", Name: "Daniele Conventi",
Email: "conventi@hastegit", Email: "conventi@hastega.it",
}, },
}, },
Usage: r.Description, Usage: r.Description,
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "run", Name: "run",
Usage: "Build and watch file changes. Can be used even with a single project or without the config file", Aliases: []string{"r"},
Usage: "Run a toolchain on a project or a list of projects. If not exist a config file it creates a new one.",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{Name: "path", Aliases: []string{"b"}, Value: "", Usage: "Project base path"}, &cli.StringFlag{Name: "path", Aliases: []string{"p"}, Value: "", Usage: "Project base path."},
&cli.BoolFlag{Name: "build", Value: false, Usage: "Enables the build"}, &cli.BoolFlag{Name: "test", Aliases: []string{"t"}, Value: false, Usage: "Enable go test."},
&cli.BoolFlag{Name: "no-run", Usage: "Disables the run"}, &cli.BoolFlag{Name: "generate", Aliases: []string{"g"}, Value: false, Usage: "Enable go generate."},
&cli.BoolFlag{Name: "no-bin", Usage: "Disables the installation"}, &cli.BoolFlag{Name: "build", Aliases: []string{"b"}, Value: false, Usage: "Enable go build."},
&cli.BoolFlag{Name: "no-fmt", Usage: "Disables the fmt (go fmt)"}, &cli.BoolFlag{Name: "legacy", Aliases: []string{"l"}, Value: false, Usage: "Watch by polling instead of Watch by fsnotify."},
&cli.BoolFlag{Name: "no-server", Usage: "Disables the web panel"}, &cli.BoolFlag{Name: "server", Aliases: []string{"s"}, Value: false, Usage: "Enable server and open into the default browser."},
&cli.BoolFlag{Name: "no-config", Value: false, Usage: "Uses the config settings"}, &cli.BoolFlag{Name: "no-run", Aliases: []string{"nr"}, Value: false, Usage: "Disable go run"},
&cli.BoolFlag{Name: "open", Usage: "Automatically opens the web panel"}, &cli.BoolFlag{Name: "no-install", Aliases: []string{"ni"}, Value: false, Usage: "Disable go install"},
&cli.IntFlag{Name: "port", Usage: "Sets the web panel port"}, &cli.BoolFlag{Name: "no-config", Aliases: []string{"nc"}, Value: false, Usage: "Ignore existing configurations."},
&cli.BoolFlag{Name: "test", Value: false, Usage: "Enables the tests"},
}, },
Action: func(p *cli.Context) error { Action: func(p *cli.Context) error {
if p.Bool("no-config") { if p.Bool("legacy") {
r.Settings = c.Settings{ r.Config.Legacy = c.Legacy{
Config: c.Config{ Status: p.Bool("legacy"),
Flimit: 0, Interval: interval,
},
Resources: c.Resources{
Config: config,
Streams: streams,
Logs: logs,
Errors: errs,
},
Server: c.Server{
Enabled: server,
Open: open,
Host: host,
Port: port,
},
} }
r.Blueprint.Projects = r.Blueprint.Projects[:0] }
r.Blueprint.Add(p) if p.Bool("no-config") || len(r.Blueprint.Projects) <= 0 {
} else if len(r.Blueprint.Projects) <= 0 { if p.Bool("no-config") {
r.Blueprint.Add(p) r.Config.Create = false
}
r.Blueprint.Projects = []w.Project{}
handle(r.Blueprint.Add(p))
} }
handle(r.Server.Start(p)) handle(r.Server.Start(p))
handle(r.Blueprint.Run()) handle(r.Blueprint.Run())
@ -170,20 +160,717 @@ func main() {
Name: "add", Name: "add",
Category: "Configuration", Category: "Configuration",
Aliases: []string{"a"}, Aliases: []string{"a"},
Usage: "Add another project", Usage: "Add a project to an existing config file or create a new one.",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: r.Wdir(), Usage: "Project name"}, &cli.StringFlag{Name: "path", Aliases: []string{"p"}, Value: "", Usage: "Project base path."},
&cli.StringFlag{Name: "path", Aliases: []string{"b"}, Value: "/", Usage: "Project base path"}, &cli.BoolFlag{Name: "test", Aliases: []string{"t"}, Value: false, Usage: "Enable go test."},
&cli.BoolFlag{Name: "build", Value: false, Usage: "Enable the build"}, &cli.BoolFlag{Name: "generate", Aliases: []string{"g"}, Value: false, Usage: "Enable go generate."},
&cli.BoolFlag{Name: "no-run", Usage: "Disables the run"}, &cli.BoolFlag{Name: "build", Aliases: []string{"b"}, Value: false, Usage: "Enable go build."},
&cli.BoolFlag{Name: "no-bin", Usage: "Disables the installation"}, &cli.BoolFlag{Name: "legacy", Aliases: []string{"l"}, Value: false, Usage: "Watch by polling instead of Watch by fsnotify."},
&cli.BoolFlag{Name: "no-fmt", Usage: "Disables the fmt (go fmt)"}, &cli.BoolFlag{Name: "server", Aliases: []string{"s"}, Value: false, Usage: "Enable server and open into the default browser."},
&cli.BoolFlag{Name: "test", Value: false, Usage: "Enables the tests"}, &cli.BoolFlag{Name: "no-run", Aliases: []string{"nr"}, Value: false, Usage: "Disable go run"},
&cli.BoolFlag{Name: "no-fmt", Aliases: []string{"nf"}, Value: false, Usage: "Disable go fmt."},
&cli.BoolFlag{Name: "no-install", Aliases: []string{"ni"}, Value: false, Usage: "Disable go install"},
&cli.BoolFlag{Name: "no-config", Aliases: []string{"nc"}, Value: false, Usage: "Ignore existing configurations."},
}, },
Action: func(p *cli.Context) (err error) { Action: func(p *cli.Context) (err error) {
handle(r.Blueprint.Insert(p)) fmt.Println(p.String("path"))
handle(r.Blueprint.Add(p))
handle(r.Record(r)) handle(r.Record(r))
fmt.Println(r.Green.Bold("Your project was successfully added")) fmt.Println(r.Yellow.Bold("[")+"REALIZE"+r.Yellow.Bold("]"), r.Green.Bold("Your project was successfully added."))
return nil
},
Before: func(c *cli.Context) error {
return before()
},
},
{
Name: "init",
Category: "Configuration",
Aliases: []string{"a"},
Usage: "Define a new config file with all options step by step",
Action: func(p *cli.Context) (err error) {
i.Run(&i.Interact{
Before: func(context i.Context) error {
context.SetErr(r.Red.Bold("INVALID INPUT"))
context.SetPrfx(color.Output, r.Yellow.Bold("[")+"REALIZE"+r.Yellow.Bold("]"))
return nil
},
Questions: []*i.Question{
{
Before: func(d i.Context) error {
if _, err := os.Stat(".realize/" + config); err != nil {
d.Skip()
}
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Would you want to overwrite the existing " + r.Colors.Magenta.Bold("Realize") + " config?",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
} else if val {
r.Settings = c.Settings{
Config: c.Config{
Create: true,
},
Resources: c.Resources{
Config: config,
Outputs: outputs,
Logs: logs,
Errors: errs,
},
Server: c.Server{
Status: false,
Open: false,
Host: host,
Port: port,
},
}
r.Blueprint.Projects = r.Blueprint.Projects[len(r.Blueprint.Projects):]
}
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Would you want to customize the " + r.Colors.Magenta.Bold("settings") + "?",
Resolve: func(d i.Context) bool {
val, _ := d.Ans().Bool()
return val
},
},
Subs: []*i.Question{
{
Before: func(d i.Context) error {
d.SetDef(0, r.Green.Regular("(os default)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[int]"),
Msg: "Max number of open files (root required)",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Int()
if err != nil {
return d.Err()
}
r.Config.Flimit = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable legacy watch by polling",
Resolve: func(d i.Context) bool {
val, _ := d.Ans().Bool()
return val
},
},
Subs: []*i.Question{
{
Before: func(d i.Context) error {
d.SetDef(1, r.Green.Regular("(1s)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[seconds]"),
Msg: "Set polling interval in seconds",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Int()
if err != nil {
return d.Err()
}
r.Config.Legacy.Interval = time.Duration(val * 1000000000)
return nil
},
},
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Config.Legacy.Status = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable web server",
Resolve: func(d i.Context) bool {
val, _ := d.Ans().Bool()
return val
},
},
Subs: []*i.Question{
{
Before: func(d i.Context) error {
d.SetDef(5001, r.Green.Regular("(5001)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[int]"),
Msg: "Server port",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Int()
if err != nil {
return d.Err()
}
r.Server.Port = int(val)
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef("localhost", r.Green.Regular("(localhost)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[string]"),
Msg: "Server host",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().String()
if err != nil {
return d.Err()
}
r.Server.Host = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Open in the current browser",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Server.Open = val
return nil
},
},
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Server.Status = val
return nil
},
},
},
Action: func(d i.Context) interface{} {
_, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(true, r.Green.Regular("(y)"))
d.SetEnd("!")
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Would you want to " + r.Colors.Magenta.Regular("add a new project") + "? (insert '!' to stop)",
Resolve: func(d i.Context) bool {
val, _ := d.Ans().Bool()
if val {
r.Blueprint.Add(p)
}
return val
},
},
Subs: []*i.Question{
{
Before: func(d i.Context) error {
d.SetDef(r.Settings.Wdir(), r.Green.Regular("("+r.Settings.Wdir()+")"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[string]"),
Msg: "Project name",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().String()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Name = val
return nil
},
},
{
Before: func(d i.Context) error {
dir, _ := os.Getwd()
d.SetDef(dir, r.Green.Regular("("+dir+")"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[string]"),
Msg: "Project path",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().String()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Path = r.Settings.Path(val)
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(true, r.Green.Regular("(y)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable go fmt",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Fmt = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable go test",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Test = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable go generate",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Generate = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(true, r.Green.Regular("(y)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable go install",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Bin = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable go build",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Build = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(true, r.Green.Regular("(y)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable go run",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Run = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Customize the watched paths",
Resolve: func(d i.Context) bool {
val, _ := d.Ans().Bool()
if val {
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Paths = r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Paths[:len(r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Paths)-1]
}
return val
},
},
Subs: []*i.Question{
{
Before: func(d i.Context) error {
d.SetEnd("!")
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[string]"),
Msg: "Insert a path to watch (insert '!' to stop)",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().String()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Paths = append(r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Paths, val)
d.Reload()
return nil
},
},
},
Action: func(d i.Context) interface{} {
_, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Customize the ignored paths",
Resolve: func(d i.Context) bool {
val, _ := d.Ans().Bool()
if val {
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Ignore = r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Ignore[:len(r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Ignore)-1]
}
return val
},
},
Subs: []*i.Question{
{
Before: func(d i.Context) error {
d.SetEnd("!")
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[string]"),
Msg: "Insert a path to ignore (insert '!' to stop)",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().String()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Ignore = append(r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Ignore, val)
d.Reload()
return nil
},
},
},
Action: func(d i.Context) interface{} {
_, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Add additionals arguments",
Resolve: func(d i.Context) bool {
val, _ := d.Ans().Bool()
return val
},
},
Subs: []*i.Question{
{
Before: func(d i.Context) error {
d.SetEnd("!")
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[string]"),
Msg: "Insert an argument (insert '!' to stop)",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().String()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Params = append(r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Params, val)
d.Reload()
return nil
},
},
},
Action: func(d i.Context) interface{} {
_, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Add 'before' custom commands",
Resolve: func(d i.Context) bool {
val, _ := d.Ans().Bool()
return val
},
},
Subs: []*i.Question{
{
Before: func(d i.Context) error {
d.SetEnd("!")
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[string]"),
Msg: "Insert a command (insert '!' to stop)",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().String()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Scripts = append(r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Scripts, w.Command{Type: "before", Command: val})
d.Reload()
return nil
},
},
},
Action: func(d i.Context) interface{} {
_, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Add 'after' custom commands",
Resolve: func(d i.Context) bool {
val, _ := d.Ans().Bool()
return val
},
},
Subs: []*i.Question{
{
Before: func(d i.Context) error {
d.SetEnd("!")
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[string]"),
Msg: "Insert a command (insert '!' to stop)",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().String()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Scripts = append(r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Scripts, w.Command{Type: "after", Command: val})
d.Reload()
return nil
},
},
},
Action: func(d i.Context) interface{} {
_, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable watcher files preview",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Watcher.Preview = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable file output history",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Streams.FileOut = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable file logs history",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Streams.FileLog = val
return nil
},
},
{
Before: func(d i.Context) error {
d.SetDef(false, r.Green.Regular("(n)"))
return nil
},
Quest: i.Quest{
Options: r.Yellow.Regular("[y/n]"),
Msg: "Enable file errors history",
},
Action: func(d i.Context) interface{} {
val, err := d.Ans().Bool()
if err != nil {
return d.Err()
}
r.Blueprint.Projects[len(r.Blueprint.Projects)-1].Streams.FileErr = val
return nil
},
},
},
Action: func(d i.Context) interface{} {
if val, err := d.Ans().Bool(); err != nil {
return d.Err()
} else if val {
d.Reload()
}
return nil
},
},
},
After: func(d i.Context) error {
if val, _ := d.Qns().Get(0).Ans().Bool(); val {
err = r.Settings.Remove()
if err != nil {
return err
}
}
return nil
},
})
handle(r.Record(r))
fmt.Println(r.Yellow.Bold("[")+"REALIZE"+r.Yellow.Bold("]"), r.Green.Bold("Your configuration was successful."))
return nil return nil
}, },
Before: func(c *cli.Context) error { Before: func(c *cli.Context) error {
@ -194,14 +881,14 @@ func main() {
Name: "remove", Name: "remove",
Category: "Configuration", Category: "Configuration",
Aliases: []string{"r"}, Aliases: []string{"r"},
Usage: "Remove a project", Usage: "Remove a project from a realize configuration.",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: ""}, &cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: ""},
}, },
Action: func(p *cli.Context) error { Action: func(p *cli.Context) error {
handle(r.Blueprint.Remove(p)) handle(r.Blueprint.Remove(p))
handle(r.Record(r)) handle(r.Record(r))
fmt.Println(r.Green.Bold("Your project was successfully removed.")) fmt.Println(r.Yellow.Bold("[")+"REALIZE"+r.Yellow.Bold("]"), r.Green.Bold("Your project was successfully removed."))
return nil return nil
}, },
Before: func(c *cli.Context) error { Before: func(c *cli.Context) error {
@ -212,7 +899,7 @@ func main() {
Name: "list", Name: "list",
Category: "Configuration", Category: "Configuration",
Aliases: []string{"l"}, Aliases: []string{"l"},
Usage: "Projects list", Usage: "Print projects list.",
Action: func(p *cli.Context) error { Action: func(p *cli.Context) error {
return handle(r.Blueprint.List()) return handle(r.Blueprint.List())
}, },
@ -220,7 +907,21 @@ func main() {
return before() return before()
}, },
}, },
{
Name: "clean",
Category: "Configuration",
Aliases: []string{"c"},
Usage: "Remove realize folder.",
Action: func(p *cli.Context) error {
handle(r.Settings.Remove())
fmt.Println(r.Yellow.Bold("[")+"REALIZE"+r.Yellow.Bold("]"), r.Green.Bold("Realize folder successfully removed."))
return nil
},
Before: func(c *cli.Context) error {
return before()
},
},
}, },
} }
c.Run(os.Args) app.Run(os.Args)
} }

File diff suppressed because one or more lines are too long

View File

@ -48,12 +48,11 @@ func render(c echo.Context, path string, mime int) error {
// Start the web server // Start the web server
func (s *Server) Start(p *cli.Context) (err error) { func (s *Server) Start(p *cli.Context) (err error) {
if p.Int("port") != 0 { if s.Server.Status || p.Bool("server") {
s.Settings.Server.Port = p.Int("port")
}
if !p.Bool("no-server") && s.Enabled {
e := echo.New() e := echo.New()
e.Use(middleware.Gzip()) e.Use(middleware.GzipWithConfig(middleware.GzipConfig{
Level: 2,
}))
e.Use(middleware.Recover()) e.Use(middleware.Recover())
// web panel // web panel
@ -95,8 +94,6 @@ func (s *Server) Start(p *cli.Context) (err error) {
return err return err
} }
} }
} else {
s.Server.Enabled = false
} }
return nil return nil
} }

View File

@ -7,8 +7,8 @@ import "syscall"
// Flimit defines the max number of watched files // Flimit defines the max number of watched files
func (s *Settings) Flimit() { func (s *Settings) Flimit() {
var rLimit syscall.Rlimit var rLimit syscall.Rlimit
rLimit.Max = s.Config.Flimit rLimit.Max = uint64(s.Config.Flimit)
rLimit.Cur = s.Config.Flimit rLimit.Cur = uint64(s.Config.Flimit)
err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
if err != nil { if err != nil {
s.Fatal(err, "Error setting rlimit") s.Fatal(err, "Error setting rlimit")

View File

@ -3,33 +3,42 @@ package settings
import ( import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"os" "os"
"time"
) )
// Settings defines a group of general settings // Settings defines a group of general settings
type Settings struct { type Settings struct {
Colors `yaml:"-"` Colors `yaml:"-"`
Config `yaml:",inline" json:"config"`
Resources `yaml:"resources" json:"resources"` Resources `yaml:"resources" json:"resources"`
Server `yaml:"server" json:"server"` Server `yaml:"server,omitempty" json:"server,omitempty"`
Config `yaml:"config" json:"config"`
} }
// Config defines structural options // Config defines structural options
type Config struct { type Config struct {
Flimit uint64 `yaml:"flimit" json:"flimit"` Create bool `yaml:"-" json:"-"`
Flimit int64 `yaml:"flimit,omitempty" json:"flimit,omitempty"`
Legacy `yaml:"legacy,omitempty" json:"legacy,omitempty"`
}
// Legacy configuration
type Legacy struct {
Status bool `yaml:"status" json:"status"`
Interval time.Duration `yaml:"interval" json:"interval"`
} }
// Server settings, used for the web panel // Server settings, used for the web panel
type Server struct { type Server struct {
Enabled bool `yaml:"enable" json:"enable"` Status bool `yaml:"status" json:"status"`
Open bool `yaml:"open" json:"open"` Open bool `yaml:"open" json:"open"`
Host string `yaml:"host" json:"host"` Host string `yaml:"host" json:"host"`
Port int `yaml:"port" json:"port"` Port int `yaml:"port" json:"port"`
} }
// Resources defines the files generated by realize // Resources defines the files generated by realize
type Resources struct { type Resources struct {
Config string `yaml:"-" json:"-"` Config string `yaml:"-" json:"-"`
Streams string `yaml:"streams" json:"output"` Outputs string `yaml:"outputs" json:"outputs"`
Logs string `yaml:"logs" json:"log"` Logs string `yaml:"logs" json:"log"`
Errors string `yaml:"errors" json:"error"` Errors string `yaml:"errors" json:"error"`
} }
@ -50,14 +59,25 @@ func (s *Settings) Read(out interface{}) error {
// Record create and unmarshal the yaml config file // Record create and unmarshal the yaml config file
func (s *Settings) Record(out interface{}) error { func (s *Settings) Record(out interface{}) error {
y, err := yaml.Marshal(out) if s.Config.Create {
if err != nil { y, err := yaml.Marshal(out)
return err if err != nil {
} return err
if _, err := os.Stat(".realize/"); os.IsNotExist(err) {
if err = os.Mkdir(".realize/", 0770); err != nil {
return s.Write(s.Resources.Config, y)
} }
if _, err := os.Stat(".realize/"); os.IsNotExist(err) {
if err = os.Mkdir(".realize/", 0770); err != nil {
return s.Write(s.Resources.Config, y)
}
}
return s.Write(".realize/"+s.Resources.Config, y)
} }
return s.Write(".realize/"+s.Resources.Config, y) return nil
}
// Remove realize folder
func (s *Settings) Remove() error {
if _, err := os.Stat(".realize/"); !os.IsNotExist(err) {
return os.RemoveAll(".realize/")
}
return nil
} }

View File

@ -4,6 +4,7 @@ import (
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"strings"
) )
// Wdir return the current working directory // Wdir return the current working directory
@ -23,8 +24,24 @@ func (s Settings) Validate(err error) error {
// Fatal prints a fatal error with its additional messages // Fatal prints a fatal error with its additional messages
func (s Settings) Fatal(err error, msg ...interface{}) { func (s Settings) Fatal(err error, msg ...interface{}) {
if len(msg) > 0 { if len(msg) > 0 && err != nil {
log.Fatalln(s.Red.Regular(msg...), err.Error()) log.Fatalln(s.Red.Regular(msg...), err.Error())
} else if err != nil {
log.Fatalln(err.Error())
} }
log.Fatalln(err.Error()) }
// Name return the project name or the path of the working dir
func (s Settings) Name(name string, path string) string {
if name == "" && path == "" {
return s.Wdir()
} else if path != "/" {
return filepath.Base(path)
}
return name
}
// Path cleaner
func (s Settings) Path(path string) string {
return strings.Replace(filepath.Clean(path), "\\", "/", -1)
} }

View File

@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"gopkg.in/urfave/cli.v2" "gopkg.in/urfave/cli.v2"
"path/filepath"
"strings" "strings"
) )
@ -16,7 +15,12 @@ func (h *Blueprint) Run() error {
wg.Add(len(h.Projects)) wg.Add(len(h.Projects))
for k := range h.Projects { for k := range h.Projects {
h.Projects[k].parent = h h.Projects[k].parent = h
go h.Projects[k].watching() h.Projects[k].path = h.Projects[k].Path
if h.Legacy.Status {
go h.Projects[k].watchByPolling()
} else {
go h.Projects[k].watchByNotify()
}
} }
wg.Wait() wg.Wait()
return nil return nil
@ -27,27 +31,27 @@ func (h *Blueprint) Run() error {
// Add a new project // Add a new project
func (h *Blueprint) Add(p *cli.Context) error { func (h *Blueprint) Add(p *cli.Context) error {
project := Project{ project := Project{
Name: h.name(p), Name: h.Name(p.String("name"), p.String("path")),
Path: filepath.Clean(p.String("path")), Path: h.Path(p.String("path")),
Build: p.Bool("build"), Fmt: !p.Bool("no-fmt"),
Bin: !p.Bool("no-bin"), Generate: p.Bool("generate"),
Run: !p.Bool("no-run"), Test: p.Bool("test"),
Fmt: !p.Bool("no-fmt"), Build: p.Bool("build"),
Test: p.Bool("test"), Bin: !p.Bool("no-bin"),
Params: argsParam(p), Run: !p.Bool("no-run"),
Params: argsParam(p),
Watcher: Watcher{ Watcher: Watcher{
Paths: []string{"/"}, Paths: []string{"/"},
Ignore: []string{"vendor"}, Ignore: []string{"vendor"},
Exts: []string{".go"}, Exts: []string{".go"},
Preview: false, Preview: p.Bool("preview"),
Scripts: []Command{},
}, },
Cli: Cli{ Streams: Streams{
Streams: true, CliOut: true,
}, FileOut: false,
File: File{ FileLog: false,
Streams: false, FileErr: false,
Logs: false,
Errors: false,
}, },
} }
if _, err := duplicates(project, h.Projects); err != nil { if _, err := duplicates(project, h.Projects); err != nil {
@ -68,12 +72,6 @@ func (h *Blueprint) Clean() {
} }
} }
// Insert a new project in projects list
func (h *Blueprint) Insert(p *cli.Context) error {
err := h.Add(p)
return err
}
// Remove a project // Remove a project
func (h *Blueprint) Remove(p *cli.Context) error { func (h *Blueprint) Remove(p *cli.Context) error {
for key, val := range h.Projects { for key, val := range h.Projects {
@ -90,39 +88,49 @@ func (h *Blueprint) List() error {
err := h.check() err := h.check()
if err == nil { if err == nil {
for _, val := range h.Projects { for _, val := range h.Projects {
fmt.Println(h.Blue.Bold("|"), h.Blue.Bold(strings.ToUpper(val.Name))) fmt.Println(h.Blue.Bold("[") + strings.ToUpper(val.Name) + h.Blue.Bold("]"))
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Base Path"), ":", h.Magenta.Regular(val.Path)) name := h.Magenta.Bold("[") + strings.ToUpper(val.Name) + h.Magenta.Bold("]")
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Run"), ":", h.Magenta.Regular(val.Run))
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Build"), ":", h.Magenta.Regular(val.Build)) fmt.Println(name, h.Yellow.Regular("Base Path"), ":", h.Magenta.Regular(val.Path))
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Install"), ":", h.Magenta.Regular(val.Bin)) fmt.Println(name, h.Yellow.Regular("Fmt"), ":", h.Magenta.Regular(val.Fmt))
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Fmt"), ":", h.Magenta.Regular(val.Fmt)) fmt.Println(name, h.Yellow.Regular("Generate"), ":", h.Magenta.Regular(val.Generate))
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Test"), ":", h.Magenta.Regular(val.Test)) fmt.Println(name, h.Yellow.Regular("Test"), ":", h.Magenta.Regular(val.Test))
fmt.Println(name, h.Yellow.Regular("Install"), ":", h.Magenta.Regular(val.Bin))
fmt.Println(name, h.Yellow.Regular("Build"), ":", h.Magenta.Regular(val.Build))
fmt.Println(name, h.Yellow.Regular("Run"), ":", h.Magenta.Regular(val.Run))
if len(val.Params) > 0 { if len(val.Params) > 0 {
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Params"), ":", h.Magenta.Regular(val.Params)) fmt.Println(name, h.Yellow.Regular("Params"), ":", h.Magenta.Regular(val.Params))
}
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Watcher"), ":")
if len(val.Watcher.After) > 0 {
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("After"), ":", h.Magenta.Regular(val.Watcher.After))
}
if len(val.Watcher.Before) > 0 {
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Before"), ":", h.Magenta.Regular(val.Watcher.Before))
} }
fmt.Println(name, h.Yellow.Regular("Watcher"), ":")
fmt.Println(name, "\t", h.Yellow.Regular("Preview"), ":", h.Magenta.Regular(val.Watcher.Preview))
if len(val.Watcher.Exts) > 0 { if len(val.Watcher.Exts) > 0 {
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Extensions"), ":", h.Magenta.Regular(val.Watcher.Exts)) fmt.Println(name, "\t", h.Yellow.Regular("Extensions"), ":", h.Magenta.Regular(val.Watcher.Exts))
} }
if len(val.Watcher.Paths) > 0 { if len(val.Watcher.Paths) > 0 {
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Paths"), ":", h.Magenta.Regular(val.Watcher.Paths)) fmt.Println(name, "\t", h.Yellow.Regular("Paths"), ":", h.Magenta.Regular(val.Watcher.Paths))
} }
if len(val.Watcher.Ignore) > 0 { if len(val.Watcher.Ignore) > 0 {
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Ignored paths"), ":", h.Magenta.Regular(val.Watcher.Ignore)) fmt.Println(name, "\t", h.Yellow.Regular("Ignored paths"), ":", h.Magenta.Regular(val.Watcher.Ignore))
} }
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Files preview"), ":", h.Magenta.Regular(val.Watcher.Preview)) if len(val.Watcher.Scripts) > 0 {
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Cli"), ":") fmt.Println(name, "\t", h.Yellow.Regular("Scripts"), ":")
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Streams"), ":", h.Magenta.Regular(val.Cli.Streams)) for _, v := range val.Watcher.Scripts {
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("File"), ":") if v.Command != "" {
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Streams"), ":", h.Magenta.Regular(val.File.Streams)) fmt.Println(name, "\t\t", h.Magenta.Regular("-"), h.Yellow.Regular("Command"), ":", h.Magenta.Regular(v.Command))
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Logs"), ":", h.Magenta.Regular(val.File.Logs)) if v.Path != "" {
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Errors"), ":", h.Magenta.Regular(val.File.Errors)) fmt.Println(name, "\t\t", h.Yellow.Regular("Path"), ":", h.Magenta.Regular(v.Path))
}
if v.Type != "" {
fmt.Println(name, "\t\t", h.Yellow.Regular("Type"), ":", h.Magenta.Regular(v.Type))
}
}
}
}
fmt.Println(name, h.Yellow.Regular("Streams"), ":")
fmt.Println(name, "\t", h.Yellow.Regular("Cli Out"), ":", h.Magenta.Regular(val.Streams.CliOut))
fmt.Println(name, "\t", h.Yellow.Regular("File Out"), ":", h.Magenta.Regular(val.Streams.FileOut))
fmt.Println(name, "\t", h.Yellow.Regular("File Log"), ":", h.Magenta.Regular(val.Streams.FileLog))
fmt.Println(name, "\t", h.Yellow.Regular("File Err"), ":", h.Magenta.Regular(val.Streams.FileErr))
} }
return nil return nil
} }
@ -137,16 +145,3 @@ func (h *Blueprint) check() error {
} }
return errors.New("There are no projects. The config file is empty.") return errors.New("There are no projects. The config file is empty.")
} }
// NameParam check the project name presence. If empty takes the working directory name
func (h *Blueprint) name(p *cli.Context) string {
var name string
if p.String("name") == "" && p.String("path") == "" {
return h.Wdir()
} else if p.String("path") != "/" {
name = filepath.Base(p.String("path"))
} else {
name = p.String("name")
}
return name
}

View File

@ -34,6 +34,8 @@ func (p *Project) goRun(channel chan bool, runner chan bool, wr *sync.WaitGroup)
} else { } else {
if _, err := os.Stat(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path))); err == nil { if _, err := os.Stat(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path))); err == nil {
build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path)), params...) build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path)), params...)
} else if _, err := os.Stat(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path)) + ".exe"); err == nil {
build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path))+".exe", params...)
} else { } else {
p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Can't run a not compiled project"}) p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Can't run a not compiled project"})
p.Fatal(err, "Can't run a not compiled project", ":") p.Fatal(err, "Can't run a not compiled project", ":")
@ -139,14 +141,21 @@ func (p *Project) goTools(dir string, name string, cmd ...string) (string, error
return "", nil return "", nil
} }
// Cmds exec a list of defined commands // Exec an additional command from a defined path if specified
func (p *Project) afterBefore(command string) (errors string, logs string) { func (p *Project) command(cmd Command) (errors string, logs string) {
var stdout bytes.Buffer var stdout bytes.Buffer
var stderr bytes.Buffer var stderr bytes.Buffer
command = strings.Replace(strings.Replace(command, "'", "", -1), "\"", "", -1) command := strings.Replace(strings.Replace(cmd.Command, "'", "", -1), "\"", "", -1)
c := strings.Split(command, " ") c := strings.Split(command, " ")
build := exec.Command(c[0], c[1:]...) build := exec.Command(c[0], c[1:]...)
build.Dir = p.base build.Dir = p.base
if cmd.Path != "" {
if strings.Contains(cmd.Path, p.base) {
build.Dir = cmd.Path
} else {
build.Dir = filepath.Join(p.base, cmd.Path)
}
}
build.Stdout = &stdout build.Stdout = &stdout
build.Stderr = &stderr build.Stderr = &stderr
err := build.Run() err := build.Run()

View File

@ -9,6 +9,16 @@ import (
var wg sync.WaitGroup var wg sync.WaitGroup
// Watcher interface used by polling/fsnotify watching
type watcher interface {
Add(path string) error
}
// Polling watcher
type pollWatcher struct {
paths map[string]bool
}
// Log struct // Log struct
type logWriter struct { type logWriter struct {
c.Colors c.Colors
@ -29,15 +39,14 @@ type Project struct {
Name string `yaml:"name" json:"name"` Name string `yaml:"name" json:"name"`
Path string `yaml:"path" json:"path"` Path string `yaml:"path" json:"path"`
Fmt bool `yaml:"fmt" json:"fmt"` Fmt bool `yaml:"fmt" json:"fmt"`
Test bool `yaml:"test" json:"test"`
Generate bool `yaml:"generate" json:"generate"` Generate bool `yaml:"generate" json:"generate"`
Test bool `yaml:"test" json:"test"`
Bin bool `yaml:"bin" json:"bin"` Bin bool `yaml:"bin" json:"bin"`
Build bool `yaml:"build" json:"build"` Build bool `yaml:"build" json:"build"`
Run bool `yaml:"run" json:"run"` Run bool `yaml:"run" json:"run"`
Params []string `yaml:"params" json:"params"` Params []string `yaml:"params,omitempty" json:"params,omitempty"`
Watcher Watcher `yaml:"watcher" json:"watcher"` Watcher Watcher `yaml:"watcher" json:"watcher"`
Cli Cli `yaml:"cli" json:"cli"` Streams Streams `yaml:"streams" json:"streams"`
File File `yaml:"file" json:"file"`
Buffer Buffer `yaml:"-" json:"buffer"` Buffer Buffer `yaml:"-" json:"buffer"`
parent *Blueprint parent *Blueprint
path string path string
@ -45,25 +54,26 @@ type Project struct {
// Watcher struct defines the livereload's logic // Watcher struct defines the livereload's logic
type Watcher struct { type Watcher struct {
// different before and after on re-run? Preview bool `yaml:"preview" json:"preview"`
Before []string `yaml:"before" json:"before"` Paths []string `yaml:"paths" json:"paths"`
After []string `yaml:"after" json:"after"` Ignore []string `yaml:"ignore_paths" json:"ignore"`
Paths []string `yaml:"paths" json:"paths"` Exts []string `yaml:"exts" json:"exts"`
Ignore []string `yaml:"ignore_paths" json:"ignore"` Scripts []Command `yaml:"scripts,omitempty" json:"scripts,omitempty"`
Exts []string `yaml:"exts" json:"exts"`
Preview bool `yaml:"preview" json:"preview"`
} }
// Cli output status, enables or disables // Command options
type Cli struct { type Command struct {
Streams bool `yaml:"streams" json:"streams"` Type string `yaml:"type" json:"type"`
Command string `yaml:"command" json:"command"`
Path string `yaml:"path" json:"path"`
} }
// File determinates the status of each log files (streams, logs, errors) // Streams is a collection of names and values for the logs functionality
type File struct { type Streams struct {
Streams bool `yaml:"streams" json:"streams"` CliOut bool `yaml:"cli_out" json:"cli_out"`
Logs bool `yaml:"logs" json:"logs"` FileOut bool `yaml:"file_out" json:"file_out"`
Errors bool `yaml:"errors" json:"errors"` FileLog bool `yaml:"file_log" json:"file_log"`
FileErr bool `yaml:"file_err" json:"file_err"`
} }
// Buffer define an array buffer for each log files // Buffer define an array buffer for each log files

View File

@ -23,8 +23,8 @@ func argsParam(params *cli.Context) []string {
// Duplicates check projects with same name or same combinations of main/path // Duplicates check projects with same name or same combinations of main/path
func duplicates(value Project, arr []Project) (Project, error) { func duplicates(value Project, arr []Project) (Project, error) {
for _, val := range arr { for _, val := range arr {
if value.Path == val.Path || value.Name == val.Name { if value.Path == val.Path {
return val, errors.New("There is a duplicate of '" + val.Name + "'. Check your config file!") return val, errors.New("There is already a project for '" + val.Path + "'. Check your config file!")
} }
} }
return Project{}, nil return Project{}, nil

View File

@ -16,28 +16,113 @@ import (
"time" "time"
) )
// Watching method is the main core. It manages the livereload and the watching var msg string
func (p *Project) watching() { var out BufferOut
func (w *pollWatcher) Add(path string) error {
if w.paths == nil {
w.paths = map[string]bool{}
}
w.paths[path] = true
return nil
}
func (w *pollWatcher) isWatching(path string) bool {
a, b := w.paths[path]
return a && b
}
// Watch the project by polling
func (p *Project) watchByPolling() {
var wr sync.WaitGroup var wr sync.WaitGroup
var watcher *fsnotify.Watcher var watcher = new(pollWatcher)
channel, exit := make(chan bool, 1), make(chan bool, 1) channel, exit := make(chan bool, 1), make(chan os.Signal, 2)
p.path = p.Path signal.Notify(exit, os.Interrupt, syscall.SIGTERM)
watcher, err := fsnotify.NewWatcher()
defer func() { defer func() {
p.cmd("after")
wg.Done() wg.Done()
}() }()
if err != nil {
log.Fatalln(p.pname(p.Name, 2), ":", p.Red.Bold(err.Error())) p.cmd("before")
return p.Fatal(p.watch(watcher))
} go p.routines(channel, &wr)
p.cmd(exit) p.LastChangedOn = time.Now().Truncate(time.Second)
if p.walks(watcher) != nil { walk := func(changed string, info os.FileInfo, err error) error {
log.Fatalln(p.pname(p.Name, 2), ":", p.Red.Bold(err.Error())) var ext string
return if err != nil {
} return err
} else if !watcher.isWatching(changed) {
return nil
} else if !info.ModTime().Truncate(time.Second).After(p.LastChangedOn) {
return nil
}
if index := strings.Index(filepath.Ext(changed), "_"); index == -1 {
ext = filepath.Ext(changed)
} else {
ext = filepath.Ext(changed)[0:index]
}
i := strings.Index(changed, filepath.Ext(changed))
file := changed[:i] + ext
path := filepath.Dir(changed[:i])
if changed[:i] != "" && inArray(ext, p.Watcher.Exts) {
if p.Run {
close(channel)
channel = make(chan bool)
}
p.LastChangedOn = time.Now().Truncate(time.Second)
// repeat the initial cycle
msg = fmt.Sprintln(p.pname(p.Name, 4), ":", p.Magenta.Bold(strings.ToUpper(ext[1:])+" changed"), p.Magenta.Bold(file))
out = BufferOut{Time: time.Now(), Text: strings.ToUpper(ext[1:]) + " changed " + file}
p.print("log", out, msg, "")
p.cmd("change")
p.fmt(file)
p.test(path)
p.generate(path)
go p.routines(channel, &wr)
}
return nil
}
for {
for _, dir := range p.Watcher.Paths {
base := filepath.Join(p.base, dir)
if _, err := os.Stat(base); err == nil {
if err := filepath.Walk(base, walk); err != nil {
msg = fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Regular(err.Error()))
out = BufferOut{Time: time.Now(), Text: err.Error()}
p.print("error", out, msg, "")
}
} else {
msg = fmt.Sprintln(p.pname(p.Name, 2), ":", base, "path doesn't exist")
out = BufferOut{Time: time.Now(), Text: base + " path doesn't exist"}
p.print("error", out, msg, "")
}
select {
case <-exit:
return
case <-time.After(p.parent.Legacy.Interval / time.Duration(len(p.Watcher.Paths))):
}
}
}
}
// Watch the project by fsnotify
func (p *Project) watchByNotify() {
var wr sync.WaitGroup
var watcher *fsnotify.Watcher
channel, exit := make(chan bool, 1), make(chan os.Signal, 2)
signal.Notify(exit, os.Interrupt, syscall.SIGTERM)
watcher, err := fsnotify.NewWatcher()
p.Fatal(err)
defer func() {
p.cmd("after")
wg.Done()
}()
p.cmd("before")
p.Fatal(p.watch(watcher))
go p.routines(channel, &wr) go p.routines(channel, &wr)
p.LastChangedOn = time.Now().Truncate(time.Second) p.LastChangedOn = time.Now().Truncate(time.Second)
// waiting for an event
for { for {
select { select {
case event := <-watcher.Events: case event := <-watcher.Events:
@ -56,176 +141,36 @@ func (p *Project) watching() {
file := event.Name[:i] + ext file := event.Name[:i] + ext
path := filepath.Dir(event.Name[:i]) path := filepath.Dir(event.Name[:i])
if event.Name[:i] != "" && inArray(ext, p.Watcher.Exts) { if event.Name[:i] != "" && inArray(ext, p.Watcher.Exts) {
msg := fmt.Sprintln(p.pname(p.Name, 4), ":", p.Magenta.Bold(strings.ToUpper(ext[1:])+" changed"), p.Magenta.Bold(file))
out := BufferOut{Time: time.Now(), Text: strings.ToUpper(ext[1:]) + " changed " + file}
p.print("log", out, msg, "")
// stop and run again
if p.Run { if p.Run {
close(channel) close(channel)
channel = make(chan bool) channel = make(chan bool)
} }
// handle multiple errors, need a better way p.LastChangedOn = time.Now().Truncate(time.Second)
// repeat the initial cycle
msg = fmt.Sprintln(p.pname(p.Name, 4), ":", p.Magenta.Bold(strings.ToUpper(ext[1:])+" changed"), p.Magenta.Bold(file))
out = BufferOut{Time: time.Now(), Text: strings.ToUpper(ext[1:]) + " changed " + file}
p.print("log", out, msg, "")
p.cmd("change")
p.fmt(file) p.fmt(file)
p.test(path) p.test(path)
p.generate(path) p.generate(path)
go p.routines(channel, &wr) go p.routines(channel, &wr)
p.LastChangedOn = time.Now().Truncate(time.Second)
} }
} }
} }
case err := <-watcher.Errors: case err := <-watcher.Errors:
log.Println(p.Red.Bold(err.Error())) msg = fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Regular(err.Error()))
out = BufferOut{Time: time.Now(), Text: err.Error()}
p.print("error", out, msg, "")
case <-exit: case <-exit:
return return
} }
} }
} }
// Install calls an implementation of "go install" // Watch the files tree of a project
func (p *Project) install() error { func (p *Project) watch(watcher watcher) error {
if p.Bin {
start := time.Now()
log.Println(p.pname(p.Name, 1), ":", "Installing..")
stream, err := p.goInstall()
if err != nil {
msg := fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Install"), p.Red.Regular(err.Error()))
out := BufferOut{Time: time.Now(), Text: err.Error(), Type: "Go Install", Stream: stream}
p.print("error", out, msg, stream)
} else {
msg := fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Regular("Installed")+" after", p.Magenta.Regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
out := BufferOut{Time: time.Now(), Text: "Installed after " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"}
p.print("log", out, msg, stream)
}
return err
}
return nil
}
// Install calls an implementation of "go run"
func (p *Project) run(channel chan bool, wr *sync.WaitGroup) {
if p.Run {
start := time.Now()
runner := make(chan bool, 1)
log.Println(p.pname(p.Name, 1), ":", "Running..")
go p.goRun(channel, runner, wr)
for {
select {
case <-runner:
msg := fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Regular("Has been run")+" after", p.Magenta.Regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
out := BufferOut{Time: time.Now(), Text: "Has been run after " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"}
p.print("log", out, msg, "")
return
}
}
}
}
// Build calls an implementation of the "go build"
func (p *Project) build() error {
if p.Build {
start := time.Now()
log.Println(p.pname(p.Name, 1), ":", "Building..")
stream, err := p.goBuild()
if err != nil {
msg := fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Build"), p.Red.Regular(err.Error()))
out := BufferOut{Time: time.Now(), Text: err.Error(), Type: "Go Build", Stream: stream}
p.print("error", out, msg, stream)
} else {
msg := fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Regular("Builded")+" after", p.Magenta.Regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
out := BufferOut{Time: time.Now(), Text: "Builded after " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"}
p.print("log", out, msg, stream)
}
return err
}
return nil
}
// Fmt calls an implementation of the "go fmt"
func (p *Project) fmt(path string) error {
if p.Fmt && strings.HasSuffix(path, ".go") {
if stream, err := p.goTools(p.base, "gofmt", "-s", "-w", "-e", path); err != nil {
msg := fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Fmt"), p.Red.Regular("there are some errors in"), ":", p.Magenta.Bold(path))
out := BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: "Go Fmt", Stream: stream}
p.print("error", out, msg, stream)
return err
}
}
return nil
}
// Generate calls an implementation of the "go generate"
func (p *Project) generate(path string) error {
if p.Generate {
if stream, err := p.goTools(path, "go", "generate"); err != nil {
msg := fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Generate"), p.Red.Regular("there are some errors in"), ":", p.Magenta.Bold(path))
out := BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: "Go Generate", Stream: stream}
p.print("error", out, msg, stream)
return err
}
}
return nil
}
// Test calls an implementation of the "go test"
func (p *Project) test(path string) error {
if p.Test {
if stream, err := p.goTools(path, "go", "test"); err != nil {
msg := fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Test"), p.Red.Regular("there are some errors in "), ":", p.Magenta.Bold(path))
out := BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: "Go Test", Stream: stream}
p.print("error", out, msg, stream)
return err
}
}
return nil
}
// Cmd calls an wrapper for execute the commands after/before
func (p *Project) cmd(exit chan bool) {
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
cast := func(commands []string) {
for _, command := range commands {
errors, logs := p.afterBefore(command)
msg := fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Bold("Command"), p.Green.Bold("\"")+command+p.Green.Bold("\""))
out := BufferOut{Time: time.Now(), Text: command, Type: "After/Before"}
if logs != "" {
p.print("log", out, msg, "")
}
if errors != "" {
p.print("error", out, msg, "")
}
if logs != "" {
msg = fmt.Sprintln(logs)
out = BufferOut{Time: time.Now(), Text: logs, Type: "After/Before"}
p.print("log", out, "", msg)
}
if errors != "" {
msg = fmt.Sprintln(p.Red.Regular(errors))
out = BufferOut{Time: time.Now(), Text: errors, Type: "After/Before"}
p.print("error", out, "", msg)
}
}
}
if len(p.Watcher.Before) > 0 {
cast(p.Watcher.Before)
}
go func() {
for {
select {
case <-c:
if len(p.Watcher.After) > 0 {
cast(p.Watcher.After)
}
close(exit)
}
}
}()
}
// Walks the file tree of a project
func (p *Project) walks(watcher *fsnotify.Watcher) error {
var files, folders int64 var files, folders int64
wd, _ := os.Getwd() wd, _ := os.Getwd()
walk := func(path string, info os.FileInfo, err error) error { walk := func(path string, info os.FileInfo, err error) error {
@ -249,7 +194,6 @@ func (p *Project) walks(watcher *fsnotify.Watcher) error {
} }
return nil return nil
} }
if p.path == "." || p.path == "/" { if p.path == "." || p.path == "/" {
p.base = wd p.base = wd
p.path = p.Wdir() p.path = p.Wdir()
@ -268,12 +212,137 @@ func (p *Project) walks(watcher *fsnotify.Watcher) error {
return errors.New(base + " path doesn't exist") return errors.New(base + " path doesn't exist")
} }
} }
msg := fmt.Sprintln(p.pname(p.Name, 1), ":", p.Blue.Bold("Watching"), p.Magenta.Bold(files), "file/s", p.Magenta.Bold(folders), "folder/s") msg = fmt.Sprintln(p.pname(p.Name, 1), ":", p.Blue.Bold("Watching"), p.Magenta.Bold(files), "file/s", p.Magenta.Bold(folders), "folder/s")
out := BufferOut{Time: time.Now(), Text: "Watching " + strconv.FormatInt(files, 10) + " files/s " + strconv.FormatInt(folders, 10) + " folder/s"} out = BufferOut{Time: time.Now(), Text: "Watching " + strconv.FormatInt(files, 10) + " files/s " + strconv.FormatInt(folders, 10) + " folder/s"}
p.print("log", out, msg, "") p.print("log", out, msg, "")
return nil return nil
} }
// Install calls an implementation of "go install"
func (p *Project) install() error {
if p.Bin {
start := time.Now()
log.Println(p.pname(p.Name, 1), ":", "Installing..")
stream, err := p.goInstall()
if err != nil {
msg = fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Install"), p.Red.Regular(err.Error()))
out = BufferOut{Time: time.Now(), Text: err.Error(), Type: "Go Install", Stream: stream}
p.print("error", out, msg, stream)
} else {
msg = fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Regular("Installed")+" after", p.Magenta.Regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
out = BufferOut{Time: time.Now(), Text: "Installed after " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"}
p.print("log", out, msg, stream)
}
return err
}
return nil
}
// Install calls an implementation of "go run"
func (p *Project) run(channel chan bool, wr *sync.WaitGroup) {
if p.Run {
start := time.Now()
runner := make(chan bool, 1)
log.Println(p.pname(p.Name, 1), ":", "Running..")
go p.goRun(channel, runner, wr)
for {
select {
case <-runner:
msg = fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Regular("Has been run")+" after", p.Magenta.Regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
out = BufferOut{Time: time.Now(), Text: "Has been run after " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"}
p.print("log", out, msg, "")
return
}
}
}
}
// Build calls an implementation of the "go build"
func (p *Project) build() error {
if p.Build {
start := time.Now()
log.Println(p.pname(p.Name, 1), ":", "Building..")
stream, err := p.goBuild()
if err != nil {
msg = fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Build"), p.Red.Regular(err.Error()))
out = BufferOut{Time: time.Now(), Text: err.Error(), Type: "Go Build", Stream: stream}
p.print("error", out, msg, stream)
} else {
msg = fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Regular("Builded")+" after", p.Magenta.Regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
out = BufferOut{Time: time.Now(), Text: "Builded after " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"}
p.print("log", out, msg, stream)
}
return err
}
return nil
}
// Fmt calls an implementation of the "go fmt"
func (p *Project) fmt(path string) error {
if p.Fmt && strings.HasSuffix(path, ".go") {
if stream, err := p.goTools(p.base, "gofmt", "-s", "-w", "-e", path); err != nil {
msg = fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Fmt"), p.Red.Regular("there are some errors in"), ":", p.Magenta.Bold(path))
out = BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: "Go Fmt", Stream: stream}
p.print("error", out, msg, stream)
return err
}
}
return nil
}
// Generate calls an implementation of the "go generate"
func (p *Project) generate(path string) error {
if p.Generate {
if stream, err := p.goTools(path, "go", "generate"); err != nil {
msg = fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Generate"), p.Red.Regular("there are some errors in"), ":", p.Magenta.Bold(path))
out = BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: "Go Generate", Stream: stream}
p.print("error", out, msg, stream)
return err
}
}
return nil
}
// Test calls an implementation of the "go test"
func (p *Project) test(path string) error {
if p.Test {
if stream, err := p.goTools(path, "go", "test"); err != nil {
msg = fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold("Go Test"), p.Red.Regular("there are some errors in "), ":", p.Magenta.Bold(path))
out = BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: "Go Test", Stream: stream}
p.print("error", out, msg, stream)
return err
}
}
return nil
}
// Cmd calls an wrapper for execute the commands after/before
func (p *Project) cmd(flag string) {
for _, cmd := range p.Watcher.Scripts {
if strings.ToLower(cmd.Type) == flag {
errors, logs := p.command(cmd)
msg = fmt.Sprintln(p.pname(p.Name, 5), ":", p.Green.Bold("Command"), p.Green.Bold("\"")+cmd.Command+p.Green.Bold("\""))
out = BufferOut{Time: time.Now(), Text: cmd.Command, Type: flag}
if logs != "" {
p.print("log", out, msg, "")
}
if errors != "" {
p.print("error", out, msg, "")
}
if logs != "" {
msg = fmt.Sprintln(logs)
out = BufferOut{Time: time.Now(), Text: logs, Type: flag}
p.print("log", out, "", msg)
}
if errors != "" {
msg = fmt.Sprintln(p.Red.Regular(errors))
out = BufferOut{Time: time.Now(), Text: errors, Type: flag}
p.print("error", out, "", msg)
}
}
}
}
// Ignore and validate a path // Ignore and validate a path
func (p *Project) ignore(str string) bool { func (p *Project) ignore(str string) bool {
for _, v := range p.Watcher.Ignore { for _, v := range p.Watcher.Ignore {
@ -322,20 +391,20 @@ func (p *Project) print(t string, o BufferOut, msg string, stream string) {
switch t { switch t {
case "out": case "out":
p.Buffer.StdOut = append(p.Buffer.StdOut, o) p.Buffer.StdOut = append(p.Buffer.StdOut, o)
if p.File.Streams { if p.Streams.FileOut {
f := p.Create(p.base, p.parent.Resources.Streams) f := p.Create(p.base, p.parent.Resources.Outputs)
t := time.Now() t := time.Now()
s := []string{t.Format("2006-01-02 15:04:05"), strings.ToUpper(p.Name), ":", o.Text, "\r\n"} 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 { if _, err := f.WriteString(strings.Join(s, " ")); err != nil {
p.Fatal(err, "") p.Fatal(err, "")
} }
} }
if msg != "" && p.Cli.Streams { if msg != "" && p.Streams.CliOut {
log.Print(msg) log.Print(msg)
} }
case "log": case "log":
p.Buffer.StdLog = append(p.Buffer.StdLog, o) p.Buffer.StdLog = append(p.Buffer.StdLog, o)
if p.File.Logs { if p.Streams.FileLog {
f := p.Create(p.base, p.parent.Resources.Logs) f := p.Create(p.base, p.parent.Resources.Logs)
t := time.Now() t := time.Now()
s := []string{t.Format("2006-01-02 15:04:05"), strings.ToUpper(p.Name), ":", o.Text, "\r\n"} s := []string{t.Format("2006-01-02 15:04:05"), strings.ToUpper(p.Name), ":", o.Text, "\r\n"}
@ -351,7 +420,7 @@ func (p *Project) print(t string, o BufferOut, msg string, stream string) {
} }
case "error": case "error":
p.Buffer.StdErr = append(p.Buffer.StdErr, o) p.Buffer.StdErr = append(p.Buffer.StdErr, o)
if p.File.Errors { if p.Streams.FileErr {
f := p.Create(p.base, p.parent.Resources.Errors) f := p.Create(p.base, p.parent.Resources.Errors)
t := time.Now() 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"} s := []string{t.Format("2006-01-02 15:04:05"), strings.ToUpper(p.Name), ":", o.Type, o.Text, o.Path, "\r\n"}