commit
21eee314cf
108
README.md
108
README.md
|
@ -1,6 +1,6 @@
|
||||||
## Realize
|
## Realize
|
||||||
|
|
||||||
[![GoDoc](https://img.shields.io/badge/documentation-godoc-blue.svg)](https://godoc.org/github.com/tockins/realize/realize)
|
[![GoDoc](https://img.shields.io/badge/documentation-godoc-blue.svg)](https://godoc.org/github.com/tockins/realize)
|
||||||
[![TeamCity CodeBetter](https://travis-ci.org/tockins/realize.svg?branch=v1)](https://travis-ci.org/tockins/realize)
|
[![TeamCity CodeBetter](https://travis-ci.org/tockins/realize.svg?branch=v1)](https://travis-ci.org/tockins/realize)
|
||||||
[![AUR](https://img.shields.io/aur/license/yaourt.svg?maxAge=2592000?style=flat-square)](https://raw.githubusercontent.com/tockins/realize/v1/LICENSE)
|
[![AUR](https://img.shields.io/aur/license/yaourt.svg?maxAge=2592000?style=flat-square)](https://raw.githubusercontent.com/tockins/realize/v1/LICENSE)
|
||||||
[![](https://img.shields.io/badge/realize-examples-yellow.svg)](https://github.com/tockins/realize-examples)
|
[![](https://img.shields.io/badge/realize-examples-yellow.svg)](https://github.com/tockins/realize-examples)
|
||||||
|
@ -16,13 +16,13 @@ A Go build system with file watchers, output streams and live reload. Run, build
|
||||||
|
|
||||||
#### What's new
|
#### What's new
|
||||||
|
|
||||||
##### v1.1
|
##### v1.2
|
||||||
- [ ] Windows support - **Moved to 1.2**
|
- [x] Windows support
|
||||||
- [x] Custom paths for the commands fast/add
|
- [x] Go generate support
|
||||||
- [x] Save output on a file
|
- [x] Bugs fix
|
||||||
- [x] Before/After fields enabled
|
- [x] Web panel errors log improved
|
||||||
- [x] Web panel in material design (localhost:5000)
|
- [x] Refactoring
|
||||||
|
- [x] Web panel edit settings, partial
|
||||||
|
|
||||||
#### Features
|
#### Features
|
||||||
|
|
||||||
|
@ -50,9 +50,9 @@ A Go build system with file watchers, output streams and live reload. Run, build
|
||||||
$ realize add
|
$ realize add
|
||||||
```
|
```
|
||||||
|
|
||||||
It will create a realize.config.yaml file if it doesn't exist already and adds the working directory as the project.
|
It will create a realize.yaml file if it doesn't exist already and adds the working directory as the project.
|
||||||
|
|
||||||
Otherwise if a config file already exists it adds another project to the existing config file.
|
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:
|
The add command supports the following custom parameters:
|
||||||
|
|
||||||
|
@ -109,13 +109,8 @@ A Go build system with file watchers, output streams and live reload. Run, build
|
||||||
$ realize run
|
$ realize run
|
||||||
```
|
```
|
||||||
|
|
||||||
Fast run launches a project from its working directory without a config file
|
Run can also launch a project from its working directory with or without make a config file (--no-config option).
|
||||||
|
It supports the following custom parameters:
|
||||||
```
|
|
||||||
$ realize fast
|
|
||||||
```
|
|
||||||
|
|
||||||
The fast command supports the following custom parameters:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
--path="server" -> Custom Path, if not specified takes the working directory name
|
--path="server" -> Custom Path, if not specified takes the working directory name
|
||||||
|
@ -126,19 +121,19 @@ A Go build system with file watchers, output streams and live reload. Run, build
|
||||||
--no-run -> Disables the run
|
--no-run -> Disables the run
|
||||||
--no-fmt -> Disables the fmt (go fmt)
|
--no-fmt -> Disables the fmt (go fmt)
|
||||||
--no-server -> Disables the web panel (port :5000)
|
--no-server -> Disables the web panel (port :5000)
|
||||||
|
--no-config -> Doesn't create any configuration files
|
||||||
--open -> Open the web panel in a new browser window
|
--open -> Open the web panel in a new browser window
|
||||||
```
|
```
|
||||||
The "fast" command supports addittional arguments as the "add" command.
|
And additional arguments as the "add" command.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ realize fast --no-run yourParams --yourFlags // correct
|
$ realize run --no-run yourParams --yourFlags // correct
|
||||||
|
|
||||||
$ realize fast yourParams --yourFlags --no-run // wrong
|
$ realize run yourParams --yourFlags --no-run // wrong
|
||||||
|
|
||||||
$ realize fast --path="/Users/alessio/go/src/github.com/tockins/realize-examples/coin/"
|
$ realize run --path="/Users/alessio/go/src/github.com/tockins/realize-examples/coin/"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### Color reference
|
#### Color reference
|
||||||
|
|
||||||
- Blue: outputs of the project
|
- Blue: outputs of the project
|
||||||
|
@ -152,39 +147,50 @@ A Go build system with file watchers, output streams and live reload. Run, build
|
||||||
- For more examples check [Realize Examples](https://github.com/tockins/realize-examples)
|
- For more examples check [Realize Examples](https://github.com/tockins/realize-examples)
|
||||||
|
|
||||||
```
|
```
|
||||||
projects:
|
settings:
|
||||||
- app_name: App One -> name
|
resources:
|
||||||
app_path: one -> root path
|
output: outputs.log // name of the output file
|
||||||
app_run: true -> enable/disable go run (require app_bin)
|
log: logs.log // name of the log file (errors included)
|
||||||
app_bin: true -> enable/disable go install
|
server:
|
||||||
app_build: false -> enable/disable go build
|
enable: true // enables the web server
|
||||||
app_fmt: true -> enable/disable go fmt
|
open: false // opens the web server in a new tab
|
||||||
app_test: true -> enable/disable go test
|
host: localhost // web server host
|
||||||
app_params: -> the project will be launched with these parameters
|
port: 5000 // wev server port
|
||||||
- --flag1
|
config:
|
||||||
- param1
|
flimit: 0 // increases the maximum number of open files - supported only on linux or os x, sudo required
|
||||||
app_watcher:
|
projects:
|
||||||
preview: true -> prints the observed files on startup
|
- name: printer // project name
|
||||||
paths: -> paths to observe for live reload
|
path: / // project path
|
||||||
- /
|
run: true // enables go run (require bin)
|
||||||
ignore_paths: -> paths to ignore
|
bin: true // enables go install
|
||||||
- vendor
|
generate: false // enables go generate
|
||||||
- bin
|
build: false // enables go build
|
||||||
exts: -> file extensions to observe for live reload
|
fmt: true // enables go fmt
|
||||||
- .go
|
test: false // enables go test
|
||||||
output: -> enable/disable the output destinations
|
params: [] // array of additionals params. the project will be launched with these parameters
|
||||||
cli: true -> cli output
|
watcher:
|
||||||
file: true -> creates an output file inside the project
|
before: [] // custom commands launched before the execution of the project
|
||||||
|
after: [] // custom commands launched after the execution of the project
|
||||||
|
paths: // paths to observe for live reload
|
||||||
|
- /
|
||||||
|
ignore_paths: // paths to ignore
|
||||||
|
- vendor
|
||||||
|
exts: // file extensions to observe for live reload
|
||||||
|
- .go
|
||||||
|
preview: true // prints the observed files on startup
|
||||||
|
cli:
|
||||||
|
streams: true // prints the output streams of the project in the cli
|
||||||
|
file:
|
||||||
|
streams: false // saves the output stream of the project in a file
|
||||||
|
logs: false // saves the logs of the project in a file
|
||||||
|
errors: false // saves the errors of the project in a file
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Next release
|
#### Next release
|
||||||
|
|
||||||
##### v1.2
|
##### v1.3
|
||||||
- [ ] Windows support
|
- [ ] Web panel edit settings, full support
|
||||||
- [ ] Go generate support
|
- [ ] Tests
|
||||||
- [ ] Web panel - watched file
|
|
||||||
- [ ] Web panel - edit settings
|
|
||||||
|
|
||||||
#### Contacts
|
#### Contacts
|
||||||
|
|
||||||
|
|
155
app/main.go
155
app/main.go
|
@ -1,155 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
c "github.com/tockins/realize/cli"
|
|
||||||
s "github.com/tockins/realize/server"
|
|
||||||
"gopkg.in/urfave/cli.v2"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
Name = "Realize"
|
|
||||||
Version = "1.1"
|
|
||||||
Description = "A Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths"
|
|
||||||
Limit = 10000
|
|
||||||
Config = "r.config.yaml"
|
|
||||||
Output = "r.output.log"
|
|
||||||
Host = "Web server listening on localhost:5000"
|
|
||||||
)
|
|
||||||
|
|
||||||
var r realize
|
|
||||||
var R Realizer
|
|
||||||
|
|
||||||
// Realizer interface for wrap the cli, app and server functions
|
|
||||||
type Realizer interface {
|
|
||||||
Wdir() string
|
|
||||||
Red(string) string
|
|
||||||
Blue(string) string
|
|
||||||
BlueS(string) string
|
|
||||||
Handle(error) error
|
|
||||||
Serve(*cli.Context)
|
|
||||||
Before(*cli.Context) error
|
|
||||||
Fast(*cli.Context) error
|
|
||||||
Run(*cli.Context) error
|
|
||||||
Add(*cli.Context) error
|
|
||||||
Remove(*cli.Context) error
|
|
||||||
List(*cli.Context) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Realize struct contains the general app informations
|
|
||||||
type realize struct {
|
|
||||||
Name, Description, Author, Email, Host string
|
|
||||||
Version string
|
|
||||||
Limit uint64
|
|
||||||
Blueprint c.Blueprint
|
|
||||||
Server s.Server
|
|
||||||
Files map[string]string
|
|
||||||
Sync chan string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Application initialization
|
|
||||||
func init() {
|
|
||||||
r = realize{
|
|
||||||
Name: Name,
|
|
||||||
Version: Version,
|
|
||||||
Description: Description,
|
|
||||||
Host: Host,
|
|
||||||
Limit: Limit,
|
|
||||||
Files: map[string]string{
|
|
||||||
"config": Config,
|
|
||||||
"output": Output,
|
|
||||||
},
|
|
||||||
Sync: make(chan string),
|
|
||||||
}
|
|
||||||
r.Blueprint = c.Blueprint{
|
|
||||||
Files: r.Files,
|
|
||||||
Sync: r.Sync,
|
|
||||||
}
|
|
||||||
r.Server = s.Server{
|
|
||||||
Blueprint: &r.Blueprint,
|
|
||||||
Files: r.Files,
|
|
||||||
Sync: r.Sync,
|
|
||||||
}
|
|
||||||
r.Increase()
|
|
||||||
R = &r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flimit defines the max number of watched files
|
|
||||||
func (r *realize) Increase() {
|
|
||||||
// increases the files limit
|
|
||||||
var rLimit syscall.Rlimit
|
|
||||||
rLimit.Max = r.Limit
|
|
||||||
rLimit.Cur = r.Limit
|
|
||||||
err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(c.Red("Error Setting Rlimit "), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) Red(s string) string {
|
|
||||||
return c.Red(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) Blue(s string) string {
|
|
||||||
return c.Blue(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) BlueS(s string) string {
|
|
||||||
return c.BlueS(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) Wdir() string {
|
|
||||||
return c.WorkingDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) Serve(p *cli.Context) {
|
|
||||||
if !p.Bool("no-server") {
|
|
||||||
fmt.Println(r.Red(r.Host) + "\n")
|
|
||||||
r.Server.Open = p.Bool("open")
|
|
||||||
r.Server.Start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) Run(p *cli.Context) error {
|
|
||||||
r.Serve(p)
|
|
||||||
return r.Blueprint.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) Fast(p *cli.Context) error {
|
|
||||||
r.Blueprint.Add(p)
|
|
||||||
r.Serve(p)
|
|
||||||
return r.Blueprint.Fast(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) Add(p *cli.Context) error {
|
|
||||||
return r.Blueprint.Insert(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) Remove(p *cli.Context) error {
|
|
||||||
return r.Blueprint.Insert(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) List(p *cli.Context) error {
|
|
||||||
return r.Blueprint.List()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) Before(p *cli.Context) error {
|
|
||||||
fmt.Println(r.Blue(r.Name) + " - " + r.Blue(r.Version))
|
|
||||||
fmt.Println(r.BlueS(r.Description) + "\n")
|
|
||||||
gopath := os.Getenv("GOPATH")
|
|
||||||
if gopath == "" {
|
|
||||||
log.Fatal(r.Red("$GOPATH isn't set up properly"))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *realize) Handle(err error) error {
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(r.Red(err.Error()))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
180
cli/cmd.go
180
cli/cmd.go
|
@ -1,180 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"gopkg.in/urfave/cli.v2"
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watch method adds the given paths on the Watcher
|
|
||||||
func (h *Blueprint) Run() error {
|
|
||||||
err := h.Read()
|
|
||||||
if err == nil {
|
|
||||||
// loop projects
|
|
||||||
wg.Add(len(h.Projects))
|
|
||||||
for k := range h.Projects {
|
|
||||||
h.Projects[k].parent = h
|
|
||||||
go h.Projects[k].watching()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fast method run a project from his working directory without makes a config file
|
|
||||||
func (h *Blueprint) Fast(params *cli.Context) error {
|
|
||||||
// Takes the values from config if wd path match with someone else
|
|
||||||
wg.Add(1)
|
|
||||||
for i := 0; i < len(h.Projects); i++ {
|
|
||||||
v := &h.Projects[i]
|
|
||||||
v.parent = h
|
|
||||||
if params.Bool("config") {
|
|
||||||
if err := h.Read(); err == nil {
|
|
||||||
for l := 0; l < len(h.Projects); l++ {
|
|
||||||
l := &h.Projects[l]
|
|
||||||
if l.Path == "/" {
|
|
||||||
l.Path = "."
|
|
||||||
l.parent = v.parent
|
|
||||||
}
|
|
||||||
if v.Path == l.Path {
|
|
||||||
v = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
go v.watching()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a new project
|
|
||||||
func (h *Blueprint) Add(params *cli.Context) error {
|
|
||||||
p := Project{
|
|
||||||
Name: nameFlag(params),
|
|
||||||
Path: filepath.Clean(params.String("path")),
|
|
||||||
Build: params.Bool("build"),
|
|
||||||
Bin: boolFlag(params.Bool("no-bin")),
|
|
||||||
Run: boolFlag(params.Bool("no-run")),
|
|
||||||
Fmt: boolFlag(params.Bool("no-fmt")),
|
|
||||||
Test: params.Bool("test"),
|
|
||||||
Params: argsParam(params),
|
|
||||||
Watcher: Watcher{
|
|
||||||
Paths: []string{"/"},
|
|
||||||
Ignore: []string{"vendor"},
|
|
||||||
Exts: []string{".go"},
|
|
||||||
Output: map[string]bool{
|
|
||||||
"cli": true,
|
|
||||||
"file": false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if _, err := duplicates(p, h.Projects); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
h.Projects = append(h.Projects, p)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean duplicate projects
|
|
||||||
func (h *Blueprint) Clean() {
|
|
||||||
arr := h.Projects
|
|
||||||
for key, val := range arr {
|
|
||||||
if _, err := duplicates(val, arr[key+1:]); err != nil {
|
|
||||||
h.Projects = append(arr[:key], arr[key+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read, Check and remove duplicates from the config file
|
|
||||||
func (h *Blueprint) Read() error {
|
|
||||||
content, err := read(h.Files["config"])
|
|
||||||
if err == nil {
|
|
||||||
err = yaml.Unmarshal(content, h)
|
|
||||||
if err == nil {
|
|
||||||
if len(h.Projects) > 0 {
|
|
||||||
h.Clean()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errors.New("There are no projects!")
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and unmarshal yaml config file
|
|
||||||
func (h *Blueprint) Create() error {
|
|
||||||
y, err := yaml.Marshal(h)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return write(h.Files["config"], y)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserts a new project in the list
|
|
||||||
func (h *Blueprint) Insert(params *cli.Context) error {
|
|
||||||
check := h.Read()
|
|
||||||
err := h.Add(params)
|
|
||||||
if err == nil {
|
|
||||||
err = h.Create()
|
|
||||||
if check == nil && err == nil {
|
|
||||||
fmt.Println(Green("Your project was successfully added"))
|
|
||||||
} else {
|
|
||||||
fmt.Println(Green("The config file was successfully created"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a project
|
|
||||||
func (h *Blueprint) Remove(params *cli.Context) error {
|
|
||||||
err := h.Read()
|
|
||||||
if err == nil {
|
|
||||||
for key, val := range h.Projects {
|
|
||||||
if params.String("name") == val.Name {
|
|
||||||
h.Projects = append(h.Projects[:key], h.Projects[key+1:]...)
|
|
||||||
err = h.Create()
|
|
||||||
if err == nil {
|
|
||||||
fmt.Println(Green("Your project was successfully removed"))
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errors.New("No project found")
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// List of all the projects
|
|
||||||
func (h *Blueprint) List() error {
|
|
||||||
err := h.Read()
|
|
||||||
if err == nil {
|
|
||||||
for _, val := range h.Projects {
|
|
||||||
fmt.Println(Blue("|"), Blue(strings.ToUpper(val.Name)))
|
|
||||||
fmt.Println(MagentaS("|"), "\t", Yellow("Base Path"), ":", MagentaS(val.Path))
|
|
||||||
fmt.Println(MagentaS("|"), "\t", Yellow("Run"), ":", MagentaS(val.Run))
|
|
||||||
fmt.Println(MagentaS("|"), "\t", Yellow("Build"), ":", MagentaS(val.Build))
|
|
||||||
fmt.Println(MagentaS("|"), "\t", Yellow("Install"), ":", MagentaS(val.Bin))
|
|
||||||
fmt.Println(MagentaS("|"), "\t", Yellow("Fmt"), ":", MagentaS(val.Fmt))
|
|
||||||
fmt.Println(MagentaS("|"), "\t", Yellow("Test"), ":", MagentaS(val.Test))
|
|
||||||
fmt.Println(MagentaS("|"), "\t", Yellow("Params"), ":", MagentaS(val.Params))
|
|
||||||
fmt.Println(MagentaS("|"), "\t", Yellow("Watcher"), ":")
|
|
||||||
fmt.Println(MagentaS("|"), "\t\t", Yellow("After"), ":", MagentaS(val.Watcher.After))
|
|
||||||
fmt.Println(MagentaS("|"), "\t\t", Yellow("Before"), ":", MagentaS(val.Watcher.Before))
|
|
||||||
fmt.Println(MagentaS("|"), "\t\t", Yellow("Extensions"), ":", MagentaS(val.Watcher.Exts))
|
|
||||||
fmt.Println(MagentaS("|"), "\t\t", Yellow("Paths"), ":", MagentaS(val.Watcher.Paths))
|
|
||||||
fmt.Println(MagentaS("|"), "\t\t", Yellow("Paths ignored"), ":", MagentaS(val.Watcher.Ignore))
|
|
||||||
fmt.Println(MagentaS("|"), "\t\t", Yellow("Watch preview"), ":", MagentaS(val.Watcher.Preview))
|
|
||||||
fmt.Println(MagentaS("|"), "\t\t", Yellow("Output"), ":")
|
|
||||||
fmt.Println(MagentaS("|"), "\t\t\t", Yellow("Cli"), ":", MagentaS(val.Watcher.Output["cli"]))
|
|
||||||
fmt.Println(MagentaS("|"), "\t\t\t", Yellow("File"), ":", MagentaS(val.Watcher.Output["file"]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
77
cli/main.go
77
cli/main.go
|
@ -1,77 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"log"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
var Green, Red, RedS, Blue, BlueS, Yellow, YellowS, Magenta, MagentaS = color.New(color.FgGreen, color.Bold).SprintFunc(),
|
|
||||||
color.New(color.FgRed, color.Bold).SprintFunc(),
|
|
||||||
color.New(color.FgRed).SprintFunc(),
|
|
||||||
color.New(color.FgBlue, color.Bold).SprintFunc(),
|
|
||||||
color.New(color.FgBlue).SprintFunc(),
|
|
||||||
color.New(color.FgYellow, color.Bold).SprintFunc(),
|
|
||||||
color.New(color.FgYellow).SprintFunc(),
|
|
||||||
color.New(color.FgMagenta, color.Bold).SprintFunc(),
|
|
||||||
color.New(color.FgMagenta).SprintFunc()
|
|
||||||
|
|
||||||
// Log struct
|
|
||||||
type logWriter struct{}
|
|
||||||
|
|
||||||
// Projects struct contains a projects list
|
|
||||||
type Blueprint struct {
|
|
||||||
Projects []Project `yaml:"projects,omitempty"`
|
|
||||||
Files map[string]string `yaml:"-"`
|
|
||||||
Sync chan string `yaml:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Project defines the informations of a single project
|
|
||||||
type Project struct {
|
|
||||||
LastChangedOn time.Time `yaml:"-"`
|
|
||||||
base string
|
|
||||||
Name string `yaml:"app_name,omitempty"`
|
|
||||||
Path string `yaml:"app_path,omitempty"`
|
|
||||||
Run bool `yaml:"app_run,omitempty"`
|
|
||||||
Bin bool `yaml:"app_bin,omitempty"`
|
|
||||||
Build bool `yaml:"app_build,omitempty"`
|
|
||||||
Fmt bool `yaml:"app_fmt,omitempty"`
|
|
||||||
Test bool `yaml:"app_test,omitempty"`
|
|
||||||
Params []string `yaml:"app_params,omitempty"`
|
|
||||||
Watcher Watcher `yaml:"app_watcher,omitempty"`
|
|
||||||
Buffer Buffer `yaml:"-"`
|
|
||||||
parent *Blueprint
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watcher struct defines the livereload's logic
|
|
||||||
type Watcher struct {
|
|
||||||
// different before and after on re-run?
|
|
||||||
Before []string `yaml:"before,omitempty"`
|
|
||||||
After []string `yaml:"after,omitempty"`
|
|
||||||
Paths []string `yaml:"paths,omitempty"`
|
|
||||||
Ignore []string `yaml:"ignore_paths,omitempty"`
|
|
||||||
Exts []string `yaml:"exts,omitempty"`
|
|
||||||
Preview bool `yaml:"preview,omitempty"`
|
|
||||||
Output map[string]bool `yaml:"output,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buffer struct for buffering outputs
|
|
||||||
type Buffer struct {
|
|
||||||
StdOut []BufferOut
|
|
||||||
StdLog []BufferOut
|
|
||||||
StdErr []BufferOut
|
|
||||||
}
|
|
||||||
|
|
||||||
type BufferOut struct {
|
|
||||||
Time time.Time
|
|
||||||
Text string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the application
|
|
||||||
func init() {
|
|
||||||
log.SetFlags(0)
|
|
||||||
log.SetOutput(new(logWriter))
|
|
||||||
}
|
|
135
cli/utils.go
135
cli/utils.go
|
@ -1,135 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"gopkg.in/urfave/cli.v2"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WorkingDir returns the name last element of the working directory path
|
|
||||||
func WorkingDir() string {
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(Red(err))
|
|
||||||
}
|
|
||||||
return filepath.Base(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a file given a name and return its byte stream
|
|
||||||
func read(file string) ([]byte, error) {
|
|
||||||
_, err := os.Stat(file)
|
|
||||||
if err == nil {
|
|
||||||
content, err := ioutil.ReadFile(file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return content, err
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a file given a name and a byte stream
|
|
||||||
func write(name string, data []byte) error {
|
|
||||||
err := ioutil.WriteFile(name, data, 0655)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(Red(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new file and return its pointer
|
|
||||||
func create(file string) *os.File {
|
|
||||||
out, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0655)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// argsParam parse one by one the given argumentes
|
|
||||||
func argsParam(params *cli.Context) []string {
|
|
||||||
argsN := params.NArg()
|
|
||||||
if argsN > 0 {
|
|
||||||
var args []string
|
|
||||||
for i := 0; i <= argsN-1; i++ {
|
|
||||||
args = append(args, params.Args().Get(i))
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NameParam check the project name presence. If empty takes the working directory name
|
|
||||||
func nameFlag(params *cli.Context) string {
|
|
||||||
var name string
|
|
||||||
if params.String("name") == "" && params.String("path") == "" {
|
|
||||||
return WorkingDir()
|
|
||||||
} else if params.String("path") != "/" {
|
|
||||||
name = filepath.Base(params.String("path"))
|
|
||||||
} else {
|
|
||||||
name = params.String("name")
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolParam is used to check the presence of a bool flag
|
|
||||||
func boolFlag(b bool) bool {
|
|
||||||
if b {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duplicates check projects with same name or same combinations of main/path
|
|
||||||
func duplicates(value Project, arr []Project) (Project, error) {
|
|
||||||
for _, val := range arr {
|
|
||||||
if value.Path == val.Path || value.Name == val.Name {
|
|
||||||
return val, errors.New("There is a duplicate of '" + val.Name + "'. Check your config file!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Project{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if a string is inArray
|
|
||||||
func inArray(str string, list []string) bool {
|
|
||||||
for _, v := range list {
|
|
||||||
if v == str {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defines the colors scheme for the project name
|
|
||||||
func pname(name string, color int) string {
|
|
||||||
switch color {
|
|
||||||
case 1:
|
|
||||||
name = Yellow("[") + strings.ToUpper(name) + Yellow("]")
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
name = Yellow("[") + Red(strings.ToUpper(name)) + Yellow("]")
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
name = Yellow("[") + Blue(strings.ToUpper(name)) + Yellow("]")
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
name = Yellow("[") + Magenta(strings.ToUpper(name)) + Yellow("]")
|
|
||||||
break
|
|
||||||
case 5:
|
|
||||||
name = Yellow("[") + Green(strings.ToUpper(name)) + Yellow("]")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cewrites the log timestamp
|
|
||||||
func (writer logWriter) Write(bytes []byte) (int, error) {
|
|
||||||
return fmt.Print(YellowS("[") + time.Now().Format("15:04:05") + YellowS("]") + string(bytes))
|
|
||||||
}
|
|
263
cli/watcher.go
263
cli/watcher.go
|
@ -1,263 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
"log"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watching method is the main core. It manages the livereload and the watching
|
|
||||||
func (p *Project) watching() {
|
|
||||||
var wr sync.WaitGroup
|
|
||||||
var watcher *fsnotify.Watcher
|
|
||||||
|
|
||||||
sync := func() {
|
|
||||||
p.parent.Sync <- "sync"
|
|
||||||
}
|
|
||||||
|
|
||||||
watcher, err := fsnotify.NewWatcher()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(strings.ToUpper(pname(p.Name, 1)), ":", Red(err.Error()))
|
|
||||||
}
|
|
||||||
channel, exit := make(chan bool, 1), make(chan bool, 1)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(pname(p.Name, 1), ":", Red(err.Error()))
|
|
||||||
}
|
|
||||||
end := func() {
|
|
||||||
watcher.Close()
|
|
||||||
wg.Done()
|
|
||||||
}
|
|
||||||
defer end()
|
|
||||||
|
|
||||||
p.cmd(exit)
|
|
||||||
err = p.walks(watcher)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(pname(p.Name, 1), ":", Red(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go p.routines(channel, &wr)
|
|
||||||
p.LastChangedOn = time.Now().Truncate(time.Second)
|
|
||||||
|
|
||||||
// waiting for an event
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event := <-watcher.Events:
|
|
||||||
if time.Now().Truncate(time.Second).After(p.LastChangedOn) {
|
|
||||||
if event.Op&fsnotify.Chmod == fsnotify.Chmod {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(event.Name); err == nil {
|
|
||||||
var ext string
|
|
||||||
if index := strings.Index(filepath.Ext(event.Name), "_"); index == -1 {
|
|
||||||
ext = filepath.Ext(event.Name)
|
|
||||||
} else {
|
|
||||||
ext = filepath.Ext(event.Name)
|
|
||||||
ext = ext[0:index]
|
|
||||||
}
|
|
||||||
|
|
||||||
i := strings.Index(event.Name, filepath.Ext(event.Name))
|
|
||||||
if event.Name[:i] != "" && inArray(ext, p.Watcher.Exts) {
|
|
||||||
p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: strings.ToUpper(ext[1:]) + " changed " + event.Name[:i] + ext})
|
|
||||||
go sync()
|
|
||||||
fmt.Println(pname(p.Name, 4), Magenta(strings.ToUpper(ext[1:])+" changed"), Magenta(event.Name[:i]+ext))
|
|
||||||
// stop and run again
|
|
||||||
if p.Run {
|
|
||||||
close(channel)
|
|
||||||
channel = make(chan bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := p.fmt(event.Name[:i] + ext)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(Red(err))
|
|
||||||
} else {
|
|
||||||
go p.routines(channel, &wr)
|
|
||||||
p.LastChangedOn = time.Now().Truncate(time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case err := <-watcher.Errors:
|
|
||||||
log.Println(Red(err.Error()))
|
|
||||||
case <-exit:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Install calls an implementation of the "go install"
|
|
||||||
func (p *Project) install(channel chan bool, wr *sync.WaitGroup) {
|
|
||||||
if p.Bin {
|
|
||||||
log.Println(pname(p.Name, 1), ":", "Installing..")
|
|
||||||
start := time.Now()
|
|
||||||
if std, err := p.GoInstall(); err != nil {
|
|
||||||
log.Println(pname(p.Name, 1), ":", fmt.Sprint(Red(err)), std)
|
|
||||||
wr.Done()
|
|
||||||
} else {
|
|
||||||
log.Println(pname(p.Name, 5), ":", Green("Installed")+" after", MagentaS(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
|
|
||||||
if p.Run {
|
|
||||||
runner := make(chan bool, 1)
|
|
||||||
log.Println(pname(p.Name, 1), ":", "Running..")
|
|
||||||
start = time.Now()
|
|
||||||
go p.GoRun(channel, runner, wr)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-runner:
|
|
||||||
log.Println(pname(p.Name, 5), ":", Green("Has been run")+" after", MagentaS(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build calls an implementation of the "go build"
|
|
||||||
func (p *Project) build() {
|
|
||||||
if p.Build {
|
|
||||||
log.Println(pname(p.Name, 1), ":", "Building..")
|
|
||||||
start := time.Now()
|
|
||||||
if std, err := p.GoBuild(); err != nil {
|
|
||||||
log.Println(pname(p.Name, 1), ":", fmt.Sprint(Red(err)), std)
|
|
||||||
} else {
|
|
||||||
log.Println(pname(p.Name, 5), ":", Green("Builded")+" after", MagentaS(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fmt calls an implementation of the "gofmt"
|
|
||||||
func (p *Project) fmt(path string) error {
|
|
||||||
if p.Fmt {
|
|
||||||
if _, err := p.GoFmt(path); err != nil {
|
|
||||||
log.Println(pname(p.Name, 1), Red("There are some GoFmt errors in "), ":", Magenta(path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
if errs := p.Cmd(commands); errs != nil {
|
|
||||||
for _, err := range errs {
|
|
||||||
log.Println(pname(p.Name, 2), Red(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test calls an implementation of the "go test"
|
|
||||||
func (p *Project) test(path string) error {
|
|
||||||
if p.Test {
|
|
||||||
if _, err := p.GoTest(path); err != nil {
|
|
||||||
log.Println(pname(p.Name, 1), Red("Go Test fails in "), ":", Magenta(path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walks the file tree of a project
|
|
||||||
func (p *Project) walks(watcher *fsnotify.Watcher) error {
|
|
||||||
var files, folders int64
|
|
||||||
wd, _ := os.Getwd()
|
|
||||||
|
|
||||||
walk := func(path string, info os.FileInfo, err error) error {
|
|
||||||
if !p.ignore(path) {
|
|
||||||
if (info.IsDir() && len(filepath.Ext(path)) == 0 && !strings.HasPrefix(path, ".")) && !strings.Contains(path, "/.") || (inArray(filepath.Ext(path), p.Watcher.Exts)) {
|
|
||||||
if p.Watcher.Preview {
|
|
||||||
fmt.Println(pname(p.Name, 1), ":", path)
|
|
||||||
}
|
|
||||||
if err = watcher.Add(path); err != nil {
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
|
||||||
if inArray(filepath.Ext(path), p.Watcher.Exts) {
|
|
||||||
files++
|
|
||||||
go func() {
|
|
||||||
if err := p.fmt(path); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
folders++
|
|
||||||
go func() {
|
|
||||||
if err := p.test(path); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Path == "." || p.Path == "/" {
|
|
||||||
p.base = wd
|
|
||||||
p.Path = WorkingDir()
|
|
||||||
} else if filepath.IsAbs(p.Path) {
|
|
||||||
p.base = p.Path
|
|
||||||
} else {
|
|
||||||
p.base = filepath.Join(wd, p.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
log.Println(Red(err.Error()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return errors.New(base + " path doesn't exist")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println(pname(p.Name, 1), Red("Watching"), Magenta(files), "file/s", Magenta(folders), "folder/s")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore validates a path
|
|
||||||
func (p *Project) ignore(str string) bool {
|
|
||||||
for _, v := range p.Watcher.Ignore {
|
|
||||||
if strings.Contains(str, filepath.Join(p.base, v)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routines launches the following methods: run, build, fmt, install
|
|
||||||
func (p *Project) routines(channel chan bool, wr *sync.WaitGroup) {
|
|
||||||
wr.Add(1)
|
|
||||||
go p.build()
|
|
||||||
go p.install(channel, wr)
|
|
||||||
wr.Wait()
|
|
||||||
}
|
|
175
realize.go
175
realize.go
|
@ -1,47 +1,125 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
a "github.com/tockins/realize/app"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
s "github.com/tockins/realize/server"
|
||||||
|
c "github.com/tockins/realize/settings"
|
||||||
|
w "github.com/tockins/realize/watcher"
|
||||||
"gopkg.in/urfave/cli.v2"
|
"gopkg.in/urfave/cli.v2"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var app a.Realizer
|
const (
|
||||||
|
name = "Realize"
|
||||||
|
version = "1.2"
|
||||||
|
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"
|
||||||
|
output = "outputs.log"
|
||||||
|
log = "logs.log"
|
||||||
|
err = "errors.log"
|
||||||
|
host = "localhost"
|
||||||
|
port = 5000
|
||||||
|
server = true
|
||||||
|
open = false
|
||||||
|
)
|
||||||
|
|
||||||
|
var r realize
|
||||||
|
|
||||||
|
// Realize struct contains the general app informations
|
||||||
|
type realize struct {
|
||||||
|
c.Settings `yaml:"settings,omitempty"`
|
||||||
|
Name, Description, Author, Email, Host, Version string `yaml:"-"`
|
||||||
|
Sync chan string `yaml:"-"`
|
||||||
|
Blueprint w.Blueprint `yaml:"-"`
|
||||||
|
Server s.Server `yaml:"-"`
|
||||||
|
Projects *[]w.Project `yaml:"projects" json:"projects"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Realize struct initialization
|
||||||
|
func init() {
|
||||||
|
r = realize{
|
||||||
|
Name: name,
|
||||||
|
Version: version,
|
||||||
|
Description: description,
|
||||||
|
Sync: make(chan string),
|
||||||
|
Settings: c.Settings{
|
||||||
|
Config: c.Config{
|
||||||
|
Flimit: 0,
|
||||||
|
},
|
||||||
|
Resources: c.Resources{
|
||||||
|
Config: config,
|
||||||
|
Output: output,
|
||||||
|
Log: log,
|
||||||
|
},
|
||||||
|
Server: c.Server{
|
||||||
|
Enabled: server,
|
||||||
|
Open: open,
|
||||||
|
Host: host,
|
||||||
|
Port: port,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
r.Blueprint = w.Blueprint{
|
||||||
|
Settings: &r.Settings,
|
||||||
|
Sync: r.Sync,
|
||||||
|
}
|
||||||
|
r.Server = s.Server{
|
||||||
|
Blueprint: &r.Blueprint,
|
||||||
|
Settings: &r.Settings,
|
||||||
|
Sync: r.Sync,
|
||||||
|
}
|
||||||
|
r.Projects = &r.Blueprint.Projects
|
||||||
|
|
||||||
|
// read if exist
|
||||||
|
r.Read(&r)
|
||||||
|
|
||||||
|
// increase the file limit
|
||||||
|
if r.Config.Flimit != 0 {
|
||||||
|
r.Flimit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before of every exec of a cli method
|
||||||
|
func before() error {
|
||||||
|
fmt.Println(r.Blue.Bold(name) + " - " + r.Blue.Bold(version))
|
||||||
|
fmt.Println(r.Blue.Regular(description) + "\n")
|
||||||
|
gopath := os.Getenv("GOPATH")
|
||||||
|
if gopath == "" {
|
||||||
|
return handle(errors.New("$GOPATH isn't set up properly"))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle errors
|
||||||
|
func handle(err error) error {
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(r.Red.Bold(err.Error()))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cli commands
|
||||||
func main() {
|
func main() {
|
||||||
app = a.R
|
|
||||||
c := &cli.App{
|
c := &cli.App{
|
||||||
Name: a.Name,
|
Name: r.Name,
|
||||||
Version: a.Version,
|
Version: r.Version,
|
||||||
Authors: []*cli.Author{
|
Authors: []*cli.Author{
|
||||||
{
|
{
|
||||||
Name: "Alessio Pracchia",
|
Name: "Alessio Pracchia",
|
||||||
Email: "pracchia@hastega.it",
|
Email: "pracchia@hastegit",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Daniele Conventi",
|
Name: "Daniele Conventi",
|
||||||
Email: "conventi@hastega.it",
|
Email: "conventi@hastegit",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Usage: a.Description,
|
Usage: r.Description,
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "run",
|
Name: "run",
|
||||||
Usage: "Build and watch file changes",
|
Usage: "Build and watch file changes. Can be used even with a single project or without the config file",
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.BoolFlag{Name: "no-server", Usage: "Enable the web panel"},
|
|
||||||
&cli.BoolFlag{Name: "open", Usage: "Automatically opens the web panel"},
|
|
||||||
},
|
|
||||||
Action: func(p *cli.Context) error {
|
|
||||||
return app.Handle(app.Run(p))
|
|
||||||
},
|
|
||||||
Before: func(c *cli.Context) error {
|
|
||||||
return app.Before(c)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "fast",
|
|
||||||
Usage: "Build and watch file changes for a single project without any Configuration file",
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{Name: "path", Aliases: []string{"b"}, Value: "", Usage: "Project base path"},
|
&cli.StringFlag{Name: "path", Aliases: []string{"b"}, Value: "", Usage: "Project base path"},
|
||||||
&cli.BoolFlag{Name: "build", Value: false, Usage: "Enables the build"},
|
&cli.BoolFlag{Name: "build", Value: false, Usage: "Enables the build"},
|
||||||
|
@ -49,15 +127,38 @@ func main() {
|
||||||
&cli.BoolFlag{Name: "no-bin", Usage: "Disables the installation"},
|
&cli.BoolFlag{Name: "no-bin", Usage: "Disables the installation"},
|
||||||
&cli.BoolFlag{Name: "no-fmt", Usage: "Disables the fmt (go fmt)"},
|
&cli.BoolFlag{Name: "no-fmt", Usage: "Disables the fmt (go fmt)"},
|
||||||
&cli.BoolFlag{Name: "no-server", Usage: "Disables the web panel"},
|
&cli.BoolFlag{Name: "no-server", Usage: "Disables the web panel"},
|
||||||
|
&cli.BoolFlag{Name: "no-config", Value: false, Usage: "Uses the config settings"},
|
||||||
&cli.BoolFlag{Name: "open", Usage: "Automatically opens the web panel"},
|
&cli.BoolFlag{Name: "open", Usage: "Automatically opens the web panel"},
|
||||||
&cli.BoolFlag{Name: "test", Value: false, Usage: "Enables the tests"},
|
&cli.BoolFlag{Name: "test", Value: false, Usage: "Enables the tests"},
|
||||||
&cli.BoolFlag{Name: "config", Value: false, Usage: "Take the defined settings if exist a Configuration file."},
|
|
||||||
},
|
},
|
||||||
Action: func(p *cli.Context) error {
|
Action: func(p *cli.Context) error {
|
||||||
return app.Handle(app.Fast(p))
|
if p.Bool("no-config") {
|
||||||
|
r.Settings = c.Settings{
|
||||||
|
Config: c.Config{
|
||||||
|
Flimit: 0,
|
||||||
|
},
|
||||||
|
Resources: c.Resources{
|
||||||
|
Config: config,
|
||||||
|
Output: output,
|
||||||
|
Log: log,
|
||||||
|
},
|
||||||
|
Server: c.Server{
|
||||||
|
Enabled: server,
|
||||||
|
Open: open,
|
||||||
|
Host: host,
|
||||||
|
Port: port,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
r.Blueprint.Projects = r.Blueprint.Projects[:0]
|
||||||
|
}
|
||||||
|
r.Blueprint.Add(p)
|
||||||
|
handle(r.Server.Start(p))
|
||||||
|
handle(r.Blueprint.Run())
|
||||||
|
handle(r.Record(r))
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
Before: func(c *cli.Context) error {
|
Before: func(c *cli.Context) error {
|
||||||
return app.Before(c)
|
return before()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -66,7 +167,7 @@ func main() {
|
||||||
Aliases: []string{"a"},
|
Aliases: []string{"a"},
|
||||||
Usage: "Add another project",
|
Usage: "Add another project",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: app.Wdir(), Usage: "Project name"},
|
&cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: r.Wdir(), Usage: "Project name"},
|
||||||
&cli.StringFlag{Name: "path", Aliases: []string{"b"}, Value: "/", Usage: "Project base path"},
|
&cli.StringFlag{Name: "path", Aliases: []string{"b"}, Value: "/", Usage: "Project base path"},
|
||||||
&cli.BoolFlag{Name: "build", Value: false, Usage: "Enable the build"},
|
&cli.BoolFlag{Name: "build", Value: false, Usage: "Enable the build"},
|
||||||
&cli.BoolFlag{Name: "no-run", Usage: "Disables the run"},
|
&cli.BoolFlag{Name: "no-run", Usage: "Disables the run"},
|
||||||
|
@ -74,11 +175,14 @@ func main() {
|
||||||
&cli.BoolFlag{Name: "no-fmt", Usage: "Disables the fmt (go fmt)"},
|
&cli.BoolFlag{Name: "no-fmt", Usage: "Disables the fmt (go fmt)"},
|
||||||
&cli.BoolFlag{Name: "test", Value: false, Usage: "Enables the tests"},
|
&cli.BoolFlag{Name: "test", Value: false, Usage: "Enables the tests"},
|
||||||
},
|
},
|
||||||
Action: func(p *cli.Context) error {
|
Action: func(p *cli.Context) (err error) {
|
||||||
return app.Handle(app.Add(p))
|
handle(r.Blueprint.Insert(p))
|
||||||
|
handle(r.Record(r))
|
||||||
|
fmt.Println(r.Green.Bold("Your project was successfully added"))
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
Before: func(c *cli.Context) error {
|
Before: func(c *cli.Context) error {
|
||||||
return app.Before(c)
|
return before()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -90,10 +194,13 @@ func main() {
|
||||||
&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 {
|
||||||
return app.Handle(app.Remove(p))
|
handle(r.Blueprint.Remove(p))
|
||||||
|
handle(r.Record(r))
|
||||||
|
fmt.Println(r.Green.Bold("Your project was successfully removed."))
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
Before: func(c *cli.Context) error {
|
Before: func(c *cli.Context) error {
|
||||||
return app.Before(c)
|
return before()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -102,10 +209,10 @@ func main() {
|
||||||
Aliases: []string{"l"},
|
Aliases: []string{"l"},
|
||||||
Usage: "Projects list",
|
Usage: "Projects list",
|
||||||
Action: func(p *cli.Context) error {
|
Action: func(p *cli.Context) error {
|
||||||
return app.Handle(app.List(p))
|
return handle(r.Blueprint.List())
|
||||||
},
|
},
|
||||||
Before: func(c *cli.Context) error {
|
Before: func(c *cli.Context) error {
|
||||||
return app.Before(c)
|
return before()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because one or more lines are too long
135
server/main.go
135
server/main.go
|
@ -3,20 +3,20 @@ package server
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"github.com/labstack/echo/engine/standard"
|
|
||||||
"github.com/labstack/echo/middleware"
|
"github.com/labstack/echo/middleware"
|
||||||
c "github.com/tockins/realize/cli"
|
c "github.com/tockins/realize/settings"
|
||||||
|
w "github.com/tockins/realize/watcher"
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
"log"
|
"gopkg.in/urfave/cli.v2"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server struct contains server informations
|
// Server struct contains server informations
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Blueprint *c.Blueprint
|
*c.Settings `yaml:"-"`
|
||||||
Files map[string]string
|
*w.Blueprint `yaml:"-"`
|
||||||
Sync chan string
|
Sync chan string `yaml:"-"`
|
||||||
Open bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func render(c echo.Context, path string, mime int) error {
|
func render(c echo.Context, path string, mime int) error {
|
||||||
|
@ -46,65 +46,86 @@ func render(c echo.Context, path string, mime int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server starting
|
// Server starting
|
||||||
func (s *Server) Start() {
|
func (s *Server) Start(p *cli.Context) (err error) {
|
||||||
e := echo.New()
|
if !p.Bool("no-server") && s.Enabled {
|
||||||
e.Use(middleware.Gzip())
|
e := echo.New()
|
||||||
|
e.Use(middleware.Gzip())
|
||||||
|
e.Use(middleware.Recover())
|
||||||
|
|
||||||
// web panel
|
// web panel
|
||||||
e.GET("/", func(c echo.Context) error {
|
e.GET("/", func(c echo.Context) error {
|
||||||
return render(c, "assets/index.html", 1)
|
return render(c, "assets/index.html", 1)
|
||||||
})
|
})
|
||||||
e.GET("/assets/js/all.min.js", func(c echo.Context) error {
|
e.GET("/assets/js/all.min.js", func(c echo.Context) error {
|
||||||
return render(c, "assets/assets/js/all.min.js", 2)
|
return render(c, "assets/assets/js/all.min.js", 2)
|
||||||
})
|
})
|
||||||
e.GET("/assets/css/app.css", func(c echo.Context) error {
|
e.GET("/assets/css/app.css", func(c echo.Context) error {
|
||||||
return render(c, "assets/assets/css/app.css", 3)
|
return render(c, "assets/assets/css/app.css", 3)
|
||||||
})
|
})
|
||||||
e.GET("/app/components/projects/index.html", func(c echo.Context) error {
|
e.GET("/app/components/projects/index.html", func(c echo.Context) error {
|
||||||
return render(c, "assets/app/components/projects/index.html", 1)
|
return render(c, "assets/app/components/projects/index.html", 1)
|
||||||
})
|
})
|
||||||
e.GET("/app/components/project/index.html", func(c echo.Context) error {
|
e.GET("/app/components/project/index.html", func(c echo.Context) error {
|
||||||
return render(c, "assets/app/components/project/index.html", 1)
|
return render(c, "assets/app/components/project/index.html", 1)
|
||||||
})
|
})
|
||||||
e.GET("/app/components/index.html", func(c echo.Context) error {
|
e.GET("/app/components/index.html", func(c echo.Context) error {
|
||||||
return render(c, "assets/app/components/index.html", 1)
|
return render(c, "assets/app/components/index.html", 1)
|
||||||
})
|
})
|
||||||
e.GET("/assets/img/svg/github-logo.svg", func(c echo.Context) error {
|
e.GET("/assets/img/svg/github-logo.svg", func(c echo.Context) error {
|
||||||
return render(c, "assets/assets/img/svg/github-logo.svg", 4)
|
return render(c, "assets/assets/img/svg/github-logo.svg", 4)
|
||||||
})
|
})
|
||||||
e.GET("/assets/img/svg/ic_error_black_48px.svg", func(c echo.Context) error {
|
e.GET("/assets/img/svg/ic_error_black_48px.svg", func(c echo.Context) error {
|
||||||
return render(c, "assets/assets/img/svg/ic_error_black_48px.svg", 4)
|
return render(c, "assets/assets/img/svg/ic_error_black_48px.svg", 4)
|
||||||
})
|
})
|
||||||
e.GET("/assets/img/svg/ic_swap_vertical_circle_black_48px.svg", func(c echo.Context) error {
|
e.GET("/assets/img/svg/ic_swap_vertical_circle_black_48px.svg", func(c echo.Context) error {
|
||||||
return render(c, "assets/assets/img/svg/ic_swap_vertical_circle_black_48px.svg", 4)
|
return render(c, "assets/assets/img/svg/ic_swap_vertical_circle_black_48px.svg", 4)
|
||||||
})
|
})
|
||||||
|
|
||||||
//websocket
|
//websocket
|
||||||
//e.GET("/ws", standard.WrapHandler(s.projects()))
|
//e.GET("/ws", echo.WrapHandler(s.projects()))
|
||||||
e.GET("/ws", standard.WrapHandler(s.projects()))
|
e.GET("/ws", s.hello)
|
||||||
go e.Run(standard.New(":5000"))
|
|
||||||
if s.Open {
|
|
||||||
Open("http://localhost:5000")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The WebSocket for projects list
|
go e.Start(string(s.Settings.Server.Host) + ":" + strconv.Itoa(s.Settings.Server.Port))
|
||||||
func (s *Server) projects() websocket.Handler {
|
if s.Open || p.Bool("open") {
|
||||||
return websocket.Handler(func(ws *websocket.Conn) {
|
_, err = Open("http://" + string(s.Settings.Server.Host) + ":" + strconv.Itoa(s.Settings.Server.Port))
|
||||||
msg := func() {
|
|
||||||
|
|
||||||
message, _ := json.Marshal(s.Blueprint.Projects)
|
|
||||||
err := websocket.Message.Send(ws, string(message))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msg()
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) hello(c echo.Context) error {
|
||||||
|
websocket.Handler(func(ws *websocket.Conn) {
|
||||||
|
defer ws.Close()
|
||||||
|
msg, _ := json.Marshal(s.Blueprint.Projects)
|
||||||
|
err := websocket.Message.Send(ws, string(msg))
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-s.Sync:
|
case <-s.Sync:
|
||||||
msg()
|
msg, _ := json.Marshal(s.Blueprint.Projects)
|
||||||
|
err = websocket.Message.Send(ws, string(msg))
|
||||||
|
if err != nil {
|
||||||
|
//log.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read
|
||||||
|
text := ""
|
||||||
|
err := websocket.Message.Receive(ws, &text)
|
||||||
|
if err != nil {
|
||||||
|
//log.Println(err)
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
err := json.Unmarshal([]byte(text), &s.Blueprint.Projects)
|
||||||
|
if err != nil {
|
||||||
|
//log.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}).ServeHTTP(c.Response(), c.Request())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,16 @@ package server
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cli map[string]string
|
var cmd map[string]string
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cli = map[string]string{
|
cmd = map[string]string{
|
||||||
"windows": "start",
|
"windows": "start",
|
||||||
"darwin": "open",
|
"darwin": "open",
|
||||||
"linux": "xdg-open",
|
"linux": "xdg-open",
|
||||||
|
@ -21,13 +20,12 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(url string) (io.Writer, error) {
|
func Open(url string) (io.Writer, error) {
|
||||||
if open, err := cli[runtime.GOOS]; !err {
|
if open, err := cmd[runtime.GOOS]; !err {
|
||||||
return nil, errors.New("This operating system is not supported.")
|
return nil, errors.New("This operating system is not supported.")
|
||||||
} else {
|
} else {
|
||||||
cmd := exec.Command(open, url)
|
cmd := exec.Command(open, url)
|
||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
fmt.Println(cmd.Stderr, err)
|
|
||||||
return cmd.Stderr, err
|
return cmd.Stderr, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fatih/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Colors struct {
|
||||||
|
Red
|
||||||
|
Blue
|
||||||
|
Yellow
|
||||||
|
Magenta
|
||||||
|
Green
|
||||||
|
}
|
||||||
|
type Red struct{}
|
||||||
|
type Blue struct{}
|
||||||
|
type Yellow struct{}
|
||||||
|
type Magenta struct{}
|
||||||
|
type Green struct{}
|
||||||
|
|
||||||
|
func (c Red) Regular(t ...interface{}) string {
|
||||||
|
r := color.New(color.FgRed).SprintFunc()
|
||||||
|
return r(t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Red) Bold(t ...interface{}) string {
|
||||||
|
r := color.New(color.FgRed, color.Bold).SprintFunc()
|
||||||
|
return r(t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Blue) Regular(t ...interface{}) string {
|
||||||
|
r := color.New(color.FgBlue).SprintFunc()
|
||||||
|
return r(t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Blue) Bold(t ...interface{}) string {
|
||||||
|
r := color.New(color.FgBlue, color.Bold).SprintFunc()
|
||||||
|
return r(t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Yellow) Regular(t ...interface{}) string {
|
||||||
|
r := color.New(color.FgYellow).SprintFunc()
|
||||||
|
return r(t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Yellow) Bold(t ...interface{}) string {
|
||||||
|
r := color.New(color.FgYellow, color.Bold).SprintFunc()
|
||||||
|
return r(t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Magenta) Regular(t ...interface{}) string {
|
||||||
|
r := color.New(color.FgMagenta).SprintFunc()
|
||||||
|
return r(t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Magenta) Bold(t ...interface{}) string {
|
||||||
|
r := color.New(color.FgMagenta, color.Bold).SprintFunc()
|
||||||
|
return r(t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Green) Regular(t ...interface{}) string {
|
||||||
|
r := color.New(color.FgGreen).SprintFunc()
|
||||||
|
return r(t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Green) Bold(t ...interface{}) string {
|
||||||
|
r := color.New(color.FgGreen, color.Bold).SprintFunc()
|
||||||
|
return r(t...)
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Scan return a byte stream of a given file
|
||||||
|
func (s Settings) Stream(file string) ([]byte, error) {
|
||||||
|
_, err := os.Stat(file)
|
||||||
|
if err == nil {
|
||||||
|
content, err := ioutil.ReadFile(file)
|
||||||
|
s.Validate(err)
|
||||||
|
return content, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a file given a name and a byte stream
|
||||||
|
func (s Settings) Write(name string, data []byte) error {
|
||||||
|
err := ioutil.WriteFile(name, data, 0655)
|
||||||
|
return s.Validate(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new file and return its pointer
|
||||||
|
func (s Settings) Create(file string) *os.File {
|
||||||
|
out, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0655)
|
||||||
|
s.Validate(err)
|
||||||
|
return out
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
Colors `yaml:"-"`
|
||||||
|
Resources `yaml:"resources" json:"resources"`
|
||||||
|
Server `yaml:"server" json:"server"`
|
||||||
|
Config `yaml:"config" json:"config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Flimit uint64 `yaml:"flimit" json:"flimit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Enabled bool `yaml:"enable" json:"enable"`
|
||||||
|
Open bool `yaml:"open" json:"open"`
|
||||||
|
Host string `yaml:"host" json:"host"`
|
||||||
|
Port int `yaml:"port" json:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resources struct {
|
||||||
|
Config string `yaml:"-" json:"-"`
|
||||||
|
Output string `yaml:"output" json:"output"`
|
||||||
|
Log string `yaml:"log" json:"log"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flimit defines the max number of watched files
|
||||||
|
func (s *Settings) Flimit() {
|
||||||
|
var rLimit syscall.Rlimit
|
||||||
|
rLimit.Max = s.Config.Flimit
|
||||||
|
rLimit.Cur = s.Config.Flimit
|
||||||
|
err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||||
|
if err != nil {
|
||||||
|
s.Fatal("Error Setting Rlimit", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read from the configuration file
|
||||||
|
func (s *Settings) Read(out interface{}) error {
|
||||||
|
content, err := s.Stream(s.Resources.Config)
|
||||||
|
if err == nil {
|
||||||
|
err = yaml.Unmarshal(content, out)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record create and unmarshal the yaml config file
|
||||||
|
func (h *Settings) Record(out interface{}) error {
|
||||||
|
y, err := yaml.Marshal(out)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return h.Write(h.Resources.Config, y)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Settings) Wdir() string {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
s.Validate(err)
|
||||||
|
return filepath.Base(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Settings) Validate(err error) error {
|
||||||
|
if err != nil {
|
||||||
|
s.Fatal("", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Settings) Fatal(msg string, err error) {
|
||||||
|
if msg != "" {
|
||||||
|
log.Fatal(s.Red.Regular(msg), err.Error())
|
||||||
|
}
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/urfave/cli.v2"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Watch method adds the given paths on the Watcher
|
||||||
|
func (h *Blueprint) Run() error {
|
||||||
|
err := h.check()
|
||||||
|
if err == nil {
|
||||||
|
// loop projects
|
||||||
|
wg.Add(len(h.Projects))
|
||||||
|
for k := range h.Projects {
|
||||||
|
h.Projects[k].parent = h
|
||||||
|
go h.Projects[k].watching()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new project
|
||||||
|
func (h *Blueprint) Add(p *cli.Context) error {
|
||||||
|
project := Project{
|
||||||
|
Name: h.name(p),
|
||||||
|
Path: filepath.Clean(p.String("path")),
|
||||||
|
Build: p.Bool("build"),
|
||||||
|
Bin: !p.Bool("no-bin"),
|
||||||
|
Run: !p.Bool("no-run"),
|
||||||
|
Fmt: !p.Bool("no-fmt"),
|
||||||
|
Test: p.Bool("test"),
|
||||||
|
Params: argsParam(p),
|
||||||
|
Watcher: Watcher{
|
||||||
|
Paths: []string{"/"},
|
||||||
|
Ignore: []string{"vendor"},
|
||||||
|
Exts: []string{".go"},
|
||||||
|
Preview: false,
|
||||||
|
},
|
||||||
|
Cli: Cli{
|
||||||
|
Streams: true,
|
||||||
|
},
|
||||||
|
File: File{
|
||||||
|
Streams: false,
|
||||||
|
Logs: false,
|
||||||
|
Errors: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if _, err := duplicates(project, h.Projects); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h.Projects = append(h.Projects, project)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean duplicate projects
|
||||||
|
func (h *Blueprint) Clean() {
|
||||||
|
arr := h.Projects
|
||||||
|
for key, val := range arr {
|
||||||
|
if _, err := duplicates(val, arr[key+1:]); err != nil {
|
||||||
|
h.Projects = append(arr[:key], arr[key+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inserts a new project in the list
|
||||||
|
func (h *Blueprint) Insert(p *cli.Context) error {
|
||||||
|
err := h.Add(p)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a project
|
||||||
|
func (h *Blueprint) Remove(p *cli.Context) error {
|
||||||
|
for key, val := range h.Projects {
|
||||||
|
if p.String("name") == val.Name {
|
||||||
|
h.Projects = append(h.Projects[:key], h.Projects[key+1:]...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.New("No project found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of all the projects
|
||||||
|
func (h *Blueprint) List() error {
|
||||||
|
err := h.check()
|
||||||
|
if err == nil {
|
||||||
|
for _, val := range h.Projects {
|
||||||
|
fmt.Println(h.Blue.Bold("|"), h.Blue.Bold(strings.ToUpper(val.Name)))
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Base Path"), ":", h.Magenta.Regular(val.Path))
|
||||||
|
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(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Install"), ":", h.Magenta.Regular(val.Bin))
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Fmt"), ":", h.Magenta.Regular(val.Fmt))
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Test"), ":", h.Magenta.Regular(val.Test))
|
||||||
|
if len(val.Params) > 0 {
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t", 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))
|
||||||
|
}
|
||||||
|
if len(val.Watcher.Exts) > 0 {
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Extensions"), ":", h.Magenta.Regular(val.Watcher.Exts))
|
||||||
|
}
|
||||||
|
if len(val.Watcher.Paths) > 0 {
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Paths"), ":", h.Magenta.Regular(val.Watcher.Paths))
|
||||||
|
}
|
||||||
|
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(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Files preview"), ":", h.Magenta.Regular(val.Watcher.Preview))
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("Cli"), ":")
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Streams"), ":", h.Magenta.Regular(val.Cli.Streams))
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t", h.Yellow.Regular("File"), ":")
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Streams"), ":", h.Magenta.Regular(val.File.Streams))
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Logs"), ":", h.Magenta.Regular(val.File.Logs))
|
||||||
|
fmt.Println(h.Magenta.Regular("|"), "\t\t", h.Yellow.Regular("Errors"), ":", h.Magenta.Regular(val.File.Errors))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether there is a project
|
||||||
|
func (h *Blueprint) check() error {
|
||||||
|
if len(h.Projects) > 0 {
|
||||||
|
h.Clean()
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ package cli
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -14,27 +13,23 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GoRun is an implementation of the bin execution
|
// GoRun is an implementation of the bin execution
|
||||||
func (p *Project) GoRun(channel chan bool, runner chan bool, wr *sync.WaitGroup) error {
|
func (p *Project) goRun(channel chan bool, runner chan bool, wr *sync.WaitGroup) error {
|
||||||
|
|
||||||
sync := func() {
|
|
||||||
p.parent.Sync <- "sync"
|
|
||||||
}
|
|
||||||
|
|
||||||
var build *exec.Cmd
|
var build *exec.Cmd
|
||||||
if len(p.Params) != 0 {
|
if len(p.Params) != 0 {
|
||||||
build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.Path)), p.Params...)
|
build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path)), p.Params...)
|
||||||
} else {
|
} else {
|
||||||
build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.Path)))
|
build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.path)))
|
||||||
}
|
}
|
||||||
build.Dir = p.base
|
build.Dir = p.base
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := build.Process.Kill(); err != nil {
|
if err := build.Process.Kill(); err != nil {
|
||||||
p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Failed to stop: " + err.Error()})
|
p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Failed to stop: " + err.Error()})
|
||||||
log.Fatal(Red("Failed to stop: "), Red(err))
|
p.Fatal("Failed to stop:", err)
|
||||||
}
|
}
|
||||||
p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Ended"})
|
p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Ended"})
|
||||||
log.Println(pname(p.Name, 2), ":", RedS("Ended"))
|
log.Println(p.pname(p.Name, 2), ":", p.Red.Regular("Ended"))
|
||||||
go sync()
|
p.sync()
|
||||||
wr.Done()
|
wr.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -42,11 +37,11 @@ func (p *Project) GoRun(channel chan bool, runner chan bool, wr *sync.WaitGroup)
|
||||||
stderr, err := build.StderrPipe()
|
stderr, err := build.StderrPipe()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(Red(err.Error()))
|
log.Println(p.Red.Bold(err.Error()))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := build.Start(); err != nil {
|
if err := build.Start(); err != nil {
|
||||||
log.Println(Red(err.Error()))
|
log.Println(p.Red.Bold(err.Error()))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
close(runner)
|
close(runner)
|
||||||
|
@ -59,21 +54,20 @@ func (p *Project) GoRun(channel chan bool, runner chan bool, wr *sync.WaitGroup)
|
||||||
select {
|
select {
|
||||||
default:
|
default:
|
||||||
if isError {
|
if isError {
|
||||||
p.Buffer.StdErr = append(p.Buffer.StdErr, BufferOut{Time: time.Now(), Text: output.Text()})
|
p.Buffer.StdErr = append(p.Buffer.StdErr, BufferOut{Time: time.Now(), Text: output.Text(), Type: "Go Run"})
|
||||||
} else {
|
} else {
|
||||||
p.Buffer.StdOut = append(p.Buffer.StdOut, BufferOut{Time: time.Now(), Text: output.Text()})
|
p.Buffer.StdOut = append(p.Buffer.StdOut, BufferOut{Time: time.Now(), Text: output.Text()})
|
||||||
}
|
}
|
||||||
go sync()
|
p.sync()
|
||||||
|
if p.Cli.Streams {
|
||||||
if p.Watcher.Output["cli"] {
|
log.Println(p.pname(p.Name, 3), ":", p.Blue.Regular(output.Text()))
|
||||||
log.Println(pname(p.Name, 3), ":", BlueS(output.Text()))
|
|
||||||
}
|
}
|
||||||
if p.Watcher.Output["file"] {
|
if p.File.Streams {
|
||||||
path := filepath.Join(p.base, p.parent.Files["output"])
|
path := filepath.Join(p.base, p.Resources.Output)
|
||||||
f := create(path)
|
f := p.Create(path)
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
if _, err := f.WriteString(t.Format("2006-01-02 15:04:05") + " : " + output.Text() + "\r\n"); err != nil {
|
if _, err := f.WriteString(t.Format("2006-01-02 15:04:05") + " : " + output.Text() + "\r\n"); err != nil {
|
||||||
log.Fatal(err)
|
p.Fatal("", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +91,10 @@ func (p *Project) GoRun(channel chan bool, runner chan bool, wr *sync.WaitGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoBuild is an implementation of the "go build"
|
// GoBuild is an implementation of the "go build"
|
||||||
func (p *Project) GoBuild() (string, error) {
|
func (p *Project) goBuild() (string, error) {
|
||||||
|
defer func() {
|
||||||
|
p.sync()
|
||||||
|
}()
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
build := exec.Command("go", "build")
|
build := exec.Command("go", "build")
|
||||||
|
@ -111,12 +108,15 @@ func (p *Project) GoBuild() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoInstall is an implementation of the "go install"
|
// GoInstall is an implementation of the "go install"
|
||||||
func (p *Project) GoInstall() (string, error) {
|
func (p *Project) goInstall() (string, error) {
|
||||||
|
defer func() {
|
||||||
|
p.sync()
|
||||||
|
}()
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
err := os.Setenv("GOBIN", filepath.Join(os.Getenv("GOPATH"), "bin"))
|
err := os.Setenv("GOBIN", filepath.Join(os.Getenv("GOPATH"), "bin"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil
|
return "", err
|
||||||
}
|
}
|
||||||
build := exec.Command("go", "install")
|
build := exec.Command("go", "install")
|
||||||
build.Dir = p.base
|
build.Dir = p.base
|
||||||
|
@ -129,41 +129,69 @@ func (p *Project) GoInstall() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoFmt is an implementation of the gofmt
|
// GoFmt is an implementation of the gofmt
|
||||||
func (p *Project) GoFmt(path string) (io.Writer, error) {
|
func (p *Project) goFmt(path string) (string, error) {
|
||||||
var out bytes.Buffer
|
var out, stderr bytes.Buffer
|
||||||
build := exec.Command("gofmt", "-s", "-w", "-e", path)
|
build := exec.Command("gofmt", "-s", "-w", "-e", path)
|
||||||
build.Dir = p.base
|
build.Dir = p.base
|
||||||
build.Stdout = &out
|
build.Stdout = &out
|
||||||
build.Stderr = &out
|
build.Stderr = &stderr
|
||||||
if err := build.Run(); err != nil {
|
if err := build.Run(); err != nil {
|
||||||
return build.Stderr, err
|
return stderr.String(), err
|
||||||
}
|
}
|
||||||
return nil, nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoTest is an implementation of the go test
|
// GoTest is an implementation of the go test
|
||||||
func (p *Project) GoTest(path string) (io.Writer, error) {
|
func (p *Project) goTest(path string) (string, error) {
|
||||||
var out bytes.Buffer
|
var out, stderr bytes.Buffer
|
||||||
build := exec.Command("go", "test")
|
build := exec.Command("go", "test")
|
||||||
build.Dir = path
|
build.Dir = path
|
||||||
build.Stdout = &out
|
build.Stdout = &out
|
||||||
build.Stderr = &out
|
build.Stderr = &stderr
|
||||||
if err := build.Run(); err != nil {
|
if err := build.Run(); err != nil {
|
||||||
return build.Stdout, err
|
return stderr.String(), err
|
||||||
}
|
}
|
||||||
return nil, nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cmd exec a list of defined commands
|
// GoGenerate is an implementation of the go test
|
||||||
func (p *Project) Cmd(cmds []string) (errors []error) {
|
func (p *Project) goGenerate(path string) (string, error) {
|
||||||
|
var out, stderr bytes.Buffer
|
||||||
|
build := exec.Command("go", "generate")
|
||||||
|
build.Dir = path
|
||||||
|
build.Stdout = &out
|
||||||
|
build.Stderr = &stderr
|
||||||
|
if err := build.Run(); err != nil {
|
||||||
|
return stderr.String(), err
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cmds exec a list of defined commands
|
||||||
|
func (p *Project) cmds(cmds []string) (errors []string) {
|
||||||
|
defer func() {
|
||||||
|
p.sync()
|
||||||
|
}()
|
||||||
for _, cmd := range cmds {
|
for _, cmd := range cmds {
|
||||||
|
var out bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
cmd := strings.Replace(strings.Replace(cmd, "'", "", -1), "\"", "", -1)
|
cmd := strings.Replace(strings.Replace(cmd, "'", "", -1), "\"", "", -1)
|
||||||
c := strings.Split(cmd, " ")
|
c := strings.Split(cmd, " ")
|
||||||
build := exec.Command(c[0], c[1:]...)
|
build := exec.Command(c[0], c[1:]...)
|
||||||
build.Dir = p.base
|
build.Dir = p.base
|
||||||
|
build.Stdout = &out
|
||||||
|
build.Stderr = &stderr
|
||||||
if err := build.Run(); err != nil {
|
if err := build.Run(); err != nil {
|
||||||
errors = append(errors, err)
|
errors = append(errors, stderr.String())
|
||||||
|
return errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errors
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync datas with the web server
|
||||||
|
func (p *Project) sync() {
|
||||||
|
go func() {
|
||||||
|
p.parent.Sync <- "sync"
|
||||||
|
}()
|
||||||
}
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
c "github.com/tockins/realize/settings"
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
// Log struct
|
||||||
|
type logWriter struct {
|
||||||
|
c.Colors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Projects struct contains a projects list
|
||||||
|
type Blueprint struct {
|
||||||
|
*c.Settings `yaml:"-"`
|
||||||
|
Projects []Project `yaml:"projects,omitempty" json:"projects,omitempty"`
|
||||||
|
Sync chan string `yaml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project defines the informations of a single project
|
||||||
|
type Project struct {
|
||||||
|
c.Settings `yaml:"-"`
|
||||||
|
LastChangedOn time.Time `yaml:"-" json:"-"`
|
||||||
|
base string
|
||||||
|
Name string `yaml:"name" json:"name"`
|
||||||
|
Path string `yaml:"path" json:"path"`
|
||||||
|
Run bool `yaml:"run" json:"run"`
|
||||||
|
Bin bool `yaml:"bin" json:"bin"`
|
||||||
|
Generate bool `yaml:"generate" json:"generate"`
|
||||||
|
Build bool `yaml:"build" json:"build"`
|
||||||
|
Fmt bool `yaml:"fmt" json:"fmt"`
|
||||||
|
Test bool `yaml:"test" json:"test"`
|
||||||
|
Params []string `yaml:"params" json:"params"`
|
||||||
|
Watcher Watcher `yaml:"watcher" json:"watcher"`
|
||||||
|
Cli Cli `yaml:"cli" json:"cli"`
|
||||||
|
File File `yaml:"file" json:"file"`
|
||||||
|
Buffer Buffer `yaml:"-" json:"buffer"`
|
||||||
|
parent *Blueprint
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watcher struct defines the livereload's logic
|
||||||
|
type Watcher struct {
|
||||||
|
// different before and after on re-run?
|
||||||
|
Before []string `yaml:"before" json:"before"`
|
||||||
|
After []string `yaml:"after" json:"after"`
|
||||||
|
Paths []string `yaml:"paths" json:"paths"`
|
||||||
|
Ignore []string `yaml:"ignore_paths" json:"ignore"`
|
||||||
|
Exts []string `yaml:"exts" json:"exts"`
|
||||||
|
Preview bool `yaml:"preview" json:"preview"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cli struct {
|
||||||
|
Streams bool `yaml:"streams" json:"streams"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
Streams bool `yaml:"streams" json:"streams"`
|
||||||
|
Logs bool `yaml:"logs" json:"logs"`
|
||||||
|
Errors bool `yaml:"errors" json:"errors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer struct for buffering outputs
|
||||||
|
type Buffer struct {
|
||||||
|
StdOut []BufferOut `json:"stdOut"`
|
||||||
|
StdLog []BufferOut `json:"stdLog"`
|
||||||
|
StdErr []BufferOut `json:"stdErr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BufferOut struct {
|
||||||
|
Time time.Time `json:"time"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Stream string `json:"stream"`
|
||||||
|
Errors []string `json:"errors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the application
|
||||||
|
func init() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
log.SetOutput(new(logWriter))
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/urfave/cli.v2"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Argsparam parse one by one the given argumentes
|
||||||
|
func argsParam(params *cli.Context) []string {
|
||||||
|
argsN := params.NArg()
|
||||||
|
if argsN > 0 {
|
||||||
|
var args []string
|
||||||
|
for i := 0; i <= argsN-1; i++ {
|
||||||
|
args = append(args, params.Args().Get(i))
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicates check projects with same name or same combinations of main/path
|
||||||
|
func duplicates(value Project, arr []Project) (Project, error) {
|
||||||
|
for _, val := range arr {
|
||||||
|
if value.Path == val.Path || value.Name == val.Name {
|
||||||
|
return val, errors.New("There is a duplicate of '" + val.Name + "'. Check your config file!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Project{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a string is inArray
|
||||||
|
func inArray(str string, list []string) bool {
|
||||||
|
for _, v := range list {
|
||||||
|
if v == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewrite the layout of the log timestamp
|
||||||
|
func (w logWriter) Write(bytes []byte) (int, error) {
|
||||||
|
return fmt.Print(w.Yellow.Regular("[") + time.Now().Format("15:04:05") + w.Yellow.Regular("]") + string(bytes))
|
||||||
|
}
|
|
@ -0,0 +1,351 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"log"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Watching method is the main core. It manages the livereload and the watching
|
||||||
|
func (p *Project) watching() {
|
||||||
|
var wr sync.WaitGroup
|
||||||
|
var watcher *fsnotify.Watcher
|
||||||
|
channel, exit := make(chan bool, 1), make(chan bool, 1)
|
||||||
|
p.path = p.Path
|
||||||
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(strings.ToUpper(p.pname(p.Name, 1)), ":", p.Red.Bold(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
watcher.Close()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
p.cmd(exit)
|
||||||
|
if p.walks(watcher) != nil {
|
||||||
|
log.Println(strings.ToUpper(p.pname(p.Name, 1)), ":", p.Red.Bold(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go p.routines(channel, &wr)
|
||||||
|
p.LastChangedOn = time.Now().Truncate(time.Second)
|
||||||
|
// waiting for an event
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-watcher.Events:
|
||||||
|
if time.Now().Truncate(time.Second).After(p.LastChangedOn) {
|
||||||
|
if event.Op&fsnotify.Chmod == fsnotify.Chmod {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(event.Name); err == nil {
|
||||||
|
var ext string
|
||||||
|
if index := strings.Index(filepath.Ext(event.Name), "_"); index == -1 {
|
||||||
|
ext = filepath.Ext(event.Name)
|
||||||
|
} else {
|
||||||
|
ext = filepath.Ext(event.Name)[0:index]
|
||||||
|
}
|
||||||
|
i := strings.Index(event.Name, filepath.Ext(event.Name))
|
||||||
|
file := event.Name[:i] + ext
|
||||||
|
path := filepath.Dir(event.Name[:i])
|
||||||
|
if event.Name[:i] != "" && inArray(ext, p.Watcher.Exts) {
|
||||||
|
p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: strings.ToUpper(ext[1:]) + " changed " + file})
|
||||||
|
go func() {
|
||||||
|
p.parent.Sync <- "sync"
|
||||||
|
}()
|
||||||
|
log.Println(p.pname(p.Name, 4), ":", p.Magenta.Bold(strings.ToUpper(ext[1:])+" changed"), p.Magenta.Bold(file))
|
||||||
|
// stop and run again
|
||||||
|
if p.Run {
|
||||||
|
close(channel)
|
||||||
|
channel = make(chan bool)
|
||||||
|
}
|
||||||
|
// handle multiple errors, need a better way
|
||||||
|
p.fmt(file)
|
||||||
|
p.test(path)
|
||||||
|
p.generate(path)
|
||||||
|
go p.routines(channel, &wr)
|
||||||
|
p.LastChangedOn = time.Now().Truncate(time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case err := <-watcher.Errors:
|
||||||
|
log.Println(p.Red.Bold(err.Error()))
|
||||||
|
case <-exit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install calls an implementation of the "go install"
|
||||||
|
func (p *Project) install(channel chan bool, wr *sync.WaitGroup) {
|
||||||
|
defer func() {
|
||||||
|
p.sync()
|
||||||
|
}()
|
||||||
|
if p.Bin {
|
||||||
|
log.Println(p.pname(p.Name, 1), ":", "Installing..")
|
||||||
|
start := time.Now()
|
||||||
|
if stream, err := p.goInstall(); 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)
|
||||||
|
wr.Done()
|
||||||
|
} 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"}
|
||||||
|
p.print("log", out, msg, stream)
|
||||||
|
|
||||||
|
if p.Run {
|
||||||
|
runner := make(chan bool, 1)
|
||||||
|
log.Println(p.pname(p.Name, 1), ":", "Running..")
|
||||||
|
start = time.Now()
|
||||||
|
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"}
|
||||||
|
p.print("log", out, msg, stream)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build calls an implementation of the "go build"
|
||||||
|
func (p *Project) build() {
|
||||||
|
defer func() {
|
||||||
|
p.sync()
|
||||||
|
}()
|
||||||
|
if p.Build {
|
||||||
|
log.Println(p.pname(p.Name, 1), ":", "Building..")
|
||||||
|
start := time.Now()
|
||||||
|
if stream, err := p.goBuild(); 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"}
|
||||||
|
p.print("log", out, msg, stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fmt calls an implementation of the "go fmt"
|
||||||
|
func (p *Project) fmt(path string) error {
|
||||||
|
defer func() {
|
||||||
|
p.sync()
|
||||||
|
}()
|
||||||
|
if p.Fmt {
|
||||||
|
if stream, err := p.goFmt(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 {
|
||||||
|
defer func() {
|
||||||
|
p.sync()
|
||||||
|
}()
|
||||||
|
if p.Generate {
|
||||||
|
if stream, err := p.goGenerate(path); 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if errs := p.cmds(commands); errs != nil {
|
||||||
|
for _, err := range errs {
|
||||||
|
msg := fmt.Sprintln(p.pname(p.Name, 2), ":", p.Red.Bold(err))
|
||||||
|
out := BufferOut{Time: time.Now(), Text: err, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test calls an implementation of the "go test"
|
||||||
|
func (p *Project) test(path string) error {
|
||||||
|
defer func() {
|
||||||
|
p.sync()
|
||||||
|
}()
|
||||||
|
if p.Test {
|
||||||
|
if stream, err := p.goTest(path); 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walks the file tree of a project
|
||||||
|
func (p *Project) walks(watcher *fsnotify.Watcher) error {
|
||||||
|
var files, folders int64
|
||||||
|
wd, _ := os.Getwd()
|
||||||
|
walk := func(path string, info os.FileInfo, err error) error {
|
||||||
|
if !p.ignore(path) {
|
||||||
|
if (info.IsDir() && len(filepath.Ext(path)) == 0 && !strings.HasPrefix(path, ".")) && !strings.Contains(path, "/.") || (inArray(filepath.Ext(path), p.Watcher.Exts)) {
|
||||||
|
if p.Watcher.Preview {
|
||||||
|
log.Println(p.pname(p.Name, 1), ":", path)
|
||||||
|
}
|
||||||
|
if err = watcher.Add(path); err != nil {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
if inArray(filepath.Ext(path), p.Watcher.Exts) {
|
||||||
|
files++
|
||||||
|
p.fmt(path)
|
||||||
|
} else {
|
||||||
|
folders++
|
||||||
|
p.generate(path)
|
||||||
|
p.test(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.path == "." || p.path == "/" {
|
||||||
|
p.base = wd
|
||||||
|
p.path = p.Wdir()
|
||||||
|
} else if filepath.IsAbs(p.path) {
|
||||||
|
p.base = p.path
|
||||||
|
} else {
|
||||||
|
p.base = filepath.Join(wd, p.path)
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
log.Println(p.Red.Bold(err.Error()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return errors.New(base + " path doesn't exist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Println(p.pname(p.Name, 1), ":", p.Blue.Bold("Watching"), p.Magenta.Bold(files), "file/s", p.Magenta.Bold(folders), "folder/s")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore validates a path
|
||||||
|
func (p *Project) ignore(str string) bool {
|
||||||
|
for _, v := range p.Watcher.Ignore {
|
||||||
|
if strings.Contains(str, filepath.Join(p.base, v)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routines launches the following methods: run, build, install
|
||||||
|
func (p *Project) routines(channel chan bool, wr *sync.WaitGroup) {
|
||||||
|
wr.Add(1)
|
||||||
|
go p.build()
|
||||||
|
go p.install(channel, wr)
|
||||||
|
wr.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defines the colors scheme for the project name
|
||||||
|
func (p *Project) pname(name string, color int) string {
|
||||||
|
switch color {
|
||||||
|
case 1:
|
||||||
|
name = p.Yellow.Regular("[") + strings.ToUpper(name) + p.Yellow.Regular("]")
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
name = p.Yellow.Regular("[") + p.Red.Bold(strings.ToUpper(name)) + p.Yellow.Regular("]")
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
name = p.Yellow.Regular("[") + p.Blue.Bold(strings.ToUpper(name)) + p.Yellow.Regular("]")
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
name = p.Yellow.Regular("[") + p.Magenta.Bold(strings.ToUpper(name)) + p.Yellow.Regular("]")
|
||||||
|
break
|
||||||
|
case 5:
|
||||||
|
name = p.Yellow.Regular("[") + p.Green.Bold(strings.ToUpper(name)) + p.Yellow.Regular("]")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Project) print(t string, o BufferOut, msg string, stream string) {
|
||||||
|
switch t {
|
||||||
|
case "out":
|
||||||
|
p.Buffer.StdOut = append(p.Buffer.StdOut, o)
|
||||||
|
if p.File.Streams {
|
||||||
|
path := filepath.Join(p.base, p.Resources.Output)
|
||||||
|
f := p.Create(path)
|
||||||
|
t := time.Now()
|
||||||
|
if _, err := f.WriteString(t.Format("2006-01-02 15:04:05") + " : " + o.Text + "\r\n"); err != nil {
|
||||||
|
p.Fatal("", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "log":
|
||||||
|
p.Buffer.StdLog = append(p.Buffer.StdLog, o)
|
||||||
|
if p.File.Logs {
|
||||||
|
path := filepath.Join(p.base, p.Resources.Log)
|
||||||
|
f := p.Create(path)
|
||||||
|
t := time.Now()
|
||||||
|
if _, err := f.WriteString(t.Format("2006-01-02 15:04:05") + " : " + o.Text + "\r\n"); err != nil {
|
||||||
|
p.Fatal("", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "error":
|
||||||
|
p.Buffer.StdErr = append(p.Buffer.StdErr, o)
|
||||||
|
if p.File.Errors {
|
||||||
|
path := filepath.Join(p.base, p.Resources.Log)
|
||||||
|
f := p.Create(path)
|
||||||
|
t := time.Now()
|
||||||
|
if _, err := f.WriteString(t.Format("2006-01-02 15:04:05") + " : " + o.Text + "\r\n"); err != nil {
|
||||||
|
p.Fatal("", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
log.Print(msg)
|
||||||
|
if stream != "" {
|
||||||
|
fmt.Println(stream)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue