v1.1
This commit is contained in:
commit
c9cde5a974
1
.gitignore
vendored
1
.gitignore
vendored
@ -27,6 +27,7 @@ _testmain.go
|
||||
.idea
|
||||
.DS_Store
|
||||
|
||||
server/assets
|
||||
docker
|
||||
vendor
|
||||
bin
|
||||
|
68
README.md
68
README.md
@ -12,7 +12,17 @@
|
||||
|
||||
A Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths
|
||||
|
||||
![Preview](http://i.imgur.com/GV2Yus5.png)
|
||||
![Preview](http://i.imgur.com/dJbNZjt.gif)
|
||||
|
||||
#### What's new
|
||||
|
||||
##### v1.1
|
||||
- [ ] Windows support - **Moved to 1.2**
|
||||
- [x] Custom paths for the commands fast/add
|
||||
- [x] Save output on a file
|
||||
- [x] Enable the fields Before/After
|
||||
- [x] Web panel (localhost:5000)
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
@ -48,29 +58,28 @@ A Go build system with file watchers, output streams and live reload. Run, build
|
||||
|
||||
```
|
||||
--name="Project Name" -> Name, if not specified takes the working directory name
|
||||
--path="server" -> Base Path, if not specified takes the working directory name
|
||||
--path="server" -> Custom Path, if not specified takes the working directory name
|
||||
--build -> Enables the build
|
||||
--test -> Enables the tests
|
||||
--test -> Enables the tests
|
||||
--no-bin -> Disables the installation
|
||||
--no-run -> Disables the run
|
||||
--no-fmt -> Disables the fmt (go fmt)
|
||||
--no-server -> Disables the web panel (port :5000)
|
||||
```
|
||||
Examples:
|
||||
|
||||
```
|
||||
$ realize add
|
||||
```
|
||||
```
|
||||
|
||||
$ realize add --path="mypath"
|
||||
```
|
||||
```
|
||||
|
||||
$ realize add --name="My Project" --build
|
||||
```
|
||||
```
|
||||
|
||||
$ realize add --name="My Project" --path="/projects/package" --build
|
||||
```
|
||||
```
|
||||
|
||||
$ realize add --name="My Project" --path="projects/package" --build --no-run
|
||||
|
||||
$ realize add --path="/Users/alessio/go/src/github.com/tockins/realize-examples/coin/"
|
||||
```
|
||||
|
||||
If you want, you can specify additional arguments for your project.
|
||||
@ -108,20 +117,23 @@ A Go build system with file watchers, output streams and live reload. Run, build
|
||||
The fast command supports the following custom parameters:
|
||||
|
||||
```
|
||||
--path="server" -> Custom Path, if not specified takes the working directory name
|
||||
--build -> Enables the build
|
||||
--test -> Enables the tests
|
||||
--config -> Take the defined settings if exist a config file
|
||||
--no-bin -> Disables the installation
|
||||
--no-run -> Disables the run
|
||||
--no-fmt -> Disables the fmt (go fmt)
|
||||
--config -> Take the defined settings if exist a config file
|
||||
--no-server -> Disables the web panel (port :5000)
|
||||
```
|
||||
|
||||
The "fast" command supporst addittional arguments as the "add" command.
|
||||
The "fast" command supports addittional arguments as the "add" command.
|
||||
|
||||
```
|
||||
$ realize fast --no-run yourParams --yourFlags // correct
|
||||
|
||||
$ realize fast yourParams --yourFlags --no-run // wrong
|
||||
|
||||
$ realize fast --path="/Users/alessio/go/src/github.com/tockins/realize-examples/coin/"
|
||||
```
|
||||
|
||||
|
||||
@ -138,7 +150,6 @@ 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)
|
||||
|
||||
```
|
||||
version: "1.0"
|
||||
projects:
|
||||
- app_name: App One -> name
|
||||
app_path: one -> root path
|
||||
@ -159,30 +170,17 @@ A Go build system with file watchers, output streams and live reload. Run, build
|
||||
- bin
|
||||
exts: -> file extensions to observe for live reload
|
||||
- .go
|
||||
- app_name: App Two -> another project
|
||||
app_path: two
|
||||
app_run: true
|
||||
app_build: true
|
||||
app_bin: true
|
||||
app_watcher:
|
||||
paths:
|
||||
- /
|
||||
ignore_paths:
|
||||
- vendor
|
||||
- bin
|
||||
exts:
|
||||
- .go
|
||||
output: -> enable/disable the output destinations
|
||||
cli: true -> cli output
|
||||
file: true -> creates an output file inside the project
|
||||
|
||||
```
|
||||
|
||||
#### Next release
|
||||
|
||||
##### Milestone 1.1
|
||||
- [ ] Testing on windows
|
||||
- [ ] Custom paths for the commands fast/add
|
||||
- [ ] Save output on a file
|
||||
- [ ] Enable the fields Before/After
|
||||
- [ ] Web panel - **Maybe**
|
||||
|
||||
##### v1.2
|
||||
- [ ] Windows support
|
||||
- [ ] Go generate support
|
||||
|
||||
#### Contacts
|
||||
|
||||
|
151
app/main.go
Normal file
151
app/main.go
Normal file
@ -0,0 +1,151 @@
|
||||
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"
|
||||
)
|
||||
|
||||
var r realize
|
||||
var R Realizer
|
||||
|
||||
// Realizer interface for wrap the cli, app and server functions
|
||||
type Realizer interface {
|
||||
Wdir() string
|
||||
Before() error
|
||||
Red(string) string
|
||||
Blue(string) string
|
||||
BlueS(string) string
|
||||
Handle(error) error
|
||||
Serve(*cli.Context)
|
||||
Fast(*cli.Context) error
|
||||
Run(p *cli.Context) error
|
||||
Add(p *cli.Context) error
|
||||
Remove(p *cli.Context) error
|
||||
List(p *cli.Context) error
|
||||
}
|
||||
|
||||
// Realize struct contains the general app informations
|
||||
type realize struct {
|
||||
Name, Description, Author, Email 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,
|
||||
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 {
|
||||
fmt.Println(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") {
|
||||
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() 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
Normal file
180
cli/cmd.go
Normal file
@ -0,0 +1,180 @@
|
||||
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
|
||||
}
|
169
cli/exec.go
Normal file
169
cli/exec.go
Normal file
@ -0,0 +1,169 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GoRun is an implementation of the bin execution
|
||||
func (p *Project) GoRun(channel chan bool, runner chan bool, wr *sync.WaitGroup) error {
|
||||
|
||||
sync := func() {
|
||||
p.parent.Sync <- "sync"
|
||||
}
|
||||
|
||||
var build *exec.Cmd
|
||||
if len(p.Params) != 0 {
|
||||
build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.Path)), p.Params...)
|
||||
} else {
|
||||
build = exec.Command(filepath.Join(os.Getenv("GOBIN"), filepath.Base(p.Path)))
|
||||
}
|
||||
build.Dir = p.base
|
||||
defer func() {
|
||||
if err := build.Process.Kill(); err != nil {
|
||||
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.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Ended"})
|
||||
log.Println(pname(p.Name, 2), ":", RedS("Ended"))
|
||||
go sync()
|
||||
wr.Done()
|
||||
}()
|
||||
|
||||
stdout, err := build.StdoutPipe()
|
||||
stderr, err := build.StderrPipe()
|
||||
|
||||
if err != nil {
|
||||
log.Println(Red(err.Error()))
|
||||
return err
|
||||
}
|
||||
if err := build.Start(); err != nil {
|
||||
log.Println(Red(err.Error()))
|
||||
return err
|
||||
}
|
||||
close(runner)
|
||||
|
||||
execOutput, execError := bufio.NewScanner(stdout), bufio.NewScanner(stderr)
|
||||
stopOutput, stopError := make(chan bool, 1), make(chan bool, 1)
|
||||
|
||||
scanner := func(stop chan bool, output *bufio.Scanner, isError bool) {
|
||||
for output.Scan() {
|
||||
select {
|
||||
default:
|
||||
if isError {
|
||||
p.Buffer.StdErr = append(p.Buffer.StdErr, BufferOut{Time: time.Now(), Text: output.Text()})
|
||||
} else {
|
||||
p.Buffer.StdOut = append(p.Buffer.StdOut, BufferOut{Time: time.Now(), Text: output.Text()})
|
||||
}
|
||||
go sync()
|
||||
|
||||
if p.Watcher.Output["cli"] {
|
||||
log.Println(pname(p.Name, 3), ":", BlueS(output.Text()))
|
||||
}
|
||||
if p.Watcher.Output["file"] {
|
||||
path := filepath.Join(p.base, p.parent.Files["output"])
|
||||
f := create(path)
|
||||
t := time.Now()
|
||||
if _, err := f.WriteString(t.Format("2006-01-02 15:04:05") + " : " + output.Text() + "\r\n"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
close(stop)
|
||||
}
|
||||
p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Started"})
|
||||
go scanner(stopOutput, execOutput, false)
|
||||
go scanner(stopError, execError, true)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-channel:
|
||||
return nil
|
||||
case <-stopOutput:
|
||||
return nil
|
||||
case <-stopError:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GoBuild is an implementation of the "go build"
|
||||
func (p *Project) GoBuild() (string, error) {
|
||||
var out bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
build := exec.Command("go", "build")
|
||||
build.Dir = p.base
|
||||
build.Stdout = &out
|
||||
build.Stderr = &stderr
|
||||
if err := build.Run(); err != nil {
|
||||
return stderr.String(), err
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// GoInstall is an implementation of the "go install"
|
||||
func (p *Project) GoInstall() (string, error) {
|
||||
var out bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
err := os.Setenv("GOBIN", filepath.Join(os.Getenv("GOPATH"), "bin"))
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
build := exec.Command("go", "install")
|
||||
build.Dir = p.base
|
||||
build.Stdout = &out
|
||||
build.Stderr = &stderr
|
||||
if err := build.Run(); err != nil {
|
||||
return stderr.String(), err
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// GoFmt is an implementation of the gofmt
|
||||
func (p *Project) GoFmt(path string) (io.Writer, error) {
|
||||
var out bytes.Buffer
|
||||
build := exec.Command("gofmt", "-s", "-w", "-e", path)
|
||||
build.Dir = p.base
|
||||
build.Stdout = &out
|
||||
build.Stderr = &out
|
||||
if err := build.Run(); err != nil {
|
||||
return build.Stderr, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GoTest is an implementation of the go test
|
||||
func (p *Project) GoTest(path string) (io.Writer, error) {
|
||||
var out bytes.Buffer
|
||||
build := exec.Command("go", "test")
|
||||
build.Dir = path
|
||||
build.Stdout = &out
|
||||
build.Stderr = &out
|
||||
if err := build.Run(); err != nil {
|
||||
return build.Stdout, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Cmd exec a list of defined commands
|
||||
func (p *Project) Cmd(cmds []string) (errors []error) {
|
||||
for _, cmd := range cmds {
|
||||
cmd := strings.Replace(strings.Replace(cmd, "'", "", -1), "\"", "", -1)
|
||||
c := strings.Split(cmd, " ")
|
||||
build := exec.Command(c[0], c[1:]...)
|
||||
build.Dir = p.base
|
||||
if err := build.Run(); err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
77
cli/main.go
Normal file
77
cli/main.go
Normal file
@ -0,0 +1,77 @@
|
||||
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
Normal file
135
cli/utils.go
Normal file
@ -0,0 +1,135 @@
|
||||
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))
|
||||
}
|
@ -1,66 +1,22 @@
|
||||
package realize
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The 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"`
|
||||
}
|
||||
|
||||
// Watch method adds the given paths on the Watcher
|
||||
func (h *Config) Watch() error {
|
||||
err := h.Read()
|
||||
if err == nil {
|
||||
// loop projects
|
||||
wg.Add(len(h.Projects))
|
||||
for k := range h.Projects {
|
||||
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 *Config) Fast(params *cli.Context) error {
|
||||
fast := h.Projects[0]
|
||||
// Takes the values from config if wd path match with someone else
|
||||
if params.Bool("config") {
|
||||
if err := h.Read(); err == nil {
|
||||
for _, val := range h.Projects {
|
||||
if fast.Path == val.Path {
|
||||
fast = val
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
wg.Add(1)
|
||||
go fast.watching()
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@ -68,7 +24,7 @@ func (p *Project) watching() {
|
||||
if err != nil {
|
||||
log.Println(strings.ToUpper(pname(p.Name, 1)), ":", Red(err.Error()))
|
||||
}
|
||||
channel := make(chan bool, 1)
|
||||
channel, exit := make(chan bool, 1), make(chan bool, 1)
|
||||
if err != nil {
|
||||
log.Println(pname(p.Name, 1), ":", Red(err.Error()))
|
||||
}
|
||||
@ -78,15 +34,21 @@ func (p *Project) watching() {
|
||||
}
|
||||
defer end()
|
||||
|
||||
p.walks(watcher)
|
||||
go routines(p, channel, &wr)
|
||||
p.reload = time.Now().Truncate(time.Second)
|
||||
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.reload) {
|
||||
if time.Now().Truncate(time.Second).After(p.LastChangedOn) {
|
||||
if event.Op&fsnotify.Chmod == fsnotify.Chmod {
|
||||
continue
|
||||
}
|
||||
@ -101,7 +63,7 @@ func (p *Project) watching() {
|
||||
|
||||
i := strings.Index(event.Name, filepath.Ext(event.Name))
|
||||
if event.Name[:i] != "" && inArray(ext, p.Watcher.Exts) {
|
||||
log.Println(pname(p.Name, 4), ":", Magenta(event.Name[:i]+ext))
|
||||
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)
|
||||
@ -112,14 +74,16 @@ func (p *Project) watching() {
|
||||
if err != nil {
|
||||
log.Fatal(Red(err))
|
||||
} else {
|
||||
go routines(p, channel, &wr)
|
||||
p.reload = time.Now().Truncate(time.Second)
|
||||
go p.routines(channel, &wr)
|
||||
p.LastChangedOn = time.Now().Truncate(time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
log.Println(Red(err.Error()))
|
||||
case <-exit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -133,7 +97,7 @@ func (p *Project) install(channel chan bool, wr *sync.WaitGroup) {
|
||||
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"))
|
||||
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..")
|
||||
@ -142,7 +106,7 @@ func (p *Project) install(channel chan bool, wr *sync.WaitGroup) {
|
||||
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"))
|
||||
log.Println(pname(p.Name, 5), ":", Green("Has been run")+" after", MagentaS(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -160,25 +124,53 @@ func (p *Project) build() {
|
||||
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"))
|
||||
log.Println(pname(p.Name, 5), ":", Green("Builded")+" after", MagentaS(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Build calls an implementation of the "gofmt"
|
||||
// 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))
|
||||
//fmt.Println(msg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build calls an implementation of the "go test"
|
||||
// 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 {
|
||||
@ -189,7 +181,7 @@ func (p *Project) test(path string) error {
|
||||
}
|
||||
|
||||
// Walks the file tree of a project
|
||||
func (p *Project) walks(watcher *fsnotify.Watcher) {
|
||||
func (p *Project) walks(watcher *fsnotify.Watcher) error {
|
||||
var files, folders int64
|
||||
wd, _ := os.Getwd()
|
||||
|
||||
@ -226,9 +218,12 @@ func (p *Project) walks(watcher *fsnotify.Watcher) {
|
||||
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 {
|
||||
@ -236,10 +231,11 @@ func (p *Project) walks(watcher *fsnotify.Watcher) {
|
||||
log.Println(Red(err.Error()))
|
||||
}
|
||||
} else {
|
||||
fmt.Println(pname(p.Name, 1), ":\t", Red(base+" path doesn't exist"))
|
||||
return errors.New(base + " path doesn't exist")
|
||||
}
|
||||
}
|
||||
fmt.Println(Red("Watching: "), pname(p.Name, 1), Magenta(files), "file/s", Magenta(folders), "folder/s")
|
||||
fmt.Println(pname(p.Name, 1), Red("Watching"), Magenta(files), "file/s", Magenta(folders), "folder/s")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ignore validates a path
|
||||
@ -253,41 +249,9 @@ func (p *Project) ignore(str string) bool {
|
||||
}
|
||||
|
||||
// Routines launches the following methods: run, build, fmt, install
|
||||
func routines(p *Project, channel chan bool, wr *sync.WaitGroup) {
|
||||
func (p *Project) routines(channel chan bool, wr *sync.WaitGroup) {
|
||||
wr.Add(1)
|
||||
go p.build()
|
||||
go p.install(channel, wr)
|
||||
wr.Wait()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
@ -1,38 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
r "github.com/tockins/realize/realize"
|
||||
a "github.com/tockins/realize/app"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var app a.Realizer
|
||||
|
||||
func main() {
|
||||
|
||||
app := r.Info()
|
||||
|
||||
handle := func(err error) error {
|
||||
if err != nil {
|
||||
fmt.Println(r.Red(err.Error()))
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
header := func() error {
|
||||
fmt.Println(r.Blue(app.Name) + " - " + r.Blue(app.Version))
|
||||
fmt.Println(r.BlueS(app.Description) + "\n")
|
||||
gopath := os.Getenv("GOPATH")
|
||||
if gopath == "" {
|
||||
log.Fatal(r.Red("$GOPATH isn't set up properly"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
cli := &cli.App{
|
||||
Name: app.Name,
|
||||
Version: app.Version,
|
||||
app = a.R
|
||||
c := &cli.App{
|
||||
Name: a.Name,
|
||||
Version: a.Version,
|
||||
Authors: []*cli.Author{
|
||||
{
|
||||
Name: "Alessio Pracchia",
|
||||
@ -43,96 +23,90 @@ func main() {
|
||||
Email: "conventi@hastega.it",
|
||||
},
|
||||
},
|
||||
Usage: app.Description,
|
||||
Usage: a.Description,
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "run",
|
||||
Usage: "Build and watch file changes",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{Name: "no-server", Usage: "Enable the web panel"},
|
||||
},
|
||||
Action: func(p *cli.Context) error {
|
||||
y := r.New(p)
|
||||
return handle(y.Watch())
|
||||
return app.Handle(app.Run(p))
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
header()
|
||||
return nil
|
||||
return app.Before()
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "fast",
|
||||
Usage: "Build and watch file changes for a single project without any config file",
|
||||
Usage: "Build and watch file changes for a single project without any Configuration file",
|
||||
Flags: []cli.Flag{
|
||||
&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: "no-run", Usage: "Disables the run"},
|
||||
&cli.BoolFlag{Name: "no-bin", Usage: "Disables the installation"},
|
||||
&cli.BoolFlag{Name: "no-fmt", Usage: "Disables the fmt (go fmt)"},
|
||||
&cli.BoolFlag{Name: "test", Value: false, Usage: "Enable the tests"},
|
||||
&cli.BoolFlag{Name: "config", Value: false, Usage: "Take the defined settings if exist a config file."},
|
||||
&cli.BoolFlag{Name: "no-server", Usage: "Disables the web panel"},
|
||||
&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 {
|
||||
y := r.New(p)
|
||||
return handle(y.Fast(p))
|
||||
return app.Handle(app.Fast(p))
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
header()
|
||||
return nil
|
||||
return app.Before()
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "add",
|
||||
Category: "config",
|
||||
Category: "Configuration",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Add another project",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: r.WorkingDir(), Usage: "Project name"},
|
||||
&cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: app.Wdir(), Usage: "Project name"},
|
||||
&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: "no-run", Usage: "Disables the run"},
|
||||
&cli.BoolFlag{Name: "no-bin", Usage: "Disables the installation"},
|
||||
&cli.BoolFlag{Name: "no-fmt", Usage: "Disables the fmt (go fmt)"},
|
||||
&cli.BoolFlag{Name: "test", Value: false, Usage: "Enable the tests"},
|
||||
&cli.BoolFlag{Name: "test", Value: false, Usage: "Enables the tests"},
|
||||
},
|
||||
Action: func(p *cli.Context) error {
|
||||
y := r.New(p)
|
||||
return handle(y.Add(p))
|
||||
return app.Handle(app.Add(p))
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
header()
|
||||
return nil
|
||||
return app.Before()
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
Category: "config",
|
||||
Category: "Configuration",
|
||||
Aliases: []string{"r"},
|
||||
Usage: "Remove a project",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: ""},
|
||||
},
|
||||
Action: func(p *cli.Context) error {
|
||||
y := r.New(p)
|
||||
return handle(y.Remove(p))
|
||||
return app.Handle(app.Remove(p))
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
header()
|
||||
return nil
|
||||
return app.Before()
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "list",
|
||||
Category: "config",
|
||||
Category: "Configuration",
|
||||
Aliases: []string{"l"},
|
||||
Usage: "Projects list",
|
||||
Action: func(p *cli.Context) error {
|
||||
y := r.New(p)
|
||||
return handle(y.List())
|
||||
return app.Handle(app.List(p))
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
header()
|
||||
return nil
|
||||
return app.Before()
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cli.Run(os.Args)
|
||||
c.Run(os.Args)
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
package realize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fatih/color"
|
||||
"log"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Default values and info
|
||||
const (
|
||||
AppName = "Realize"
|
||||
AppVersion = "v1.0"
|
||||
AppDescription = "A Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths"
|
||||
AppFile = "realize.config.yaml"
|
||||
)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Green color bold
|
||||
var Green = color.New(color.FgGreen, color.Bold).SprintFunc()
|
||||
|
||||
// Red color bold
|
||||
var Red = color.New(color.FgRed, color.Bold).SprintFunc()
|
||||
|
||||
// RedS color used for errors
|
||||
var RedS = color.New(color.FgRed).SprintFunc()
|
||||
|
||||
// Blue color bold used for project output
|
||||
var Blue = color.New(color.FgBlue, color.Bold).SprintFunc()
|
||||
|
||||
// BlueS color
|
||||
var BlueS = color.New(color.FgBlue).SprintFunc()
|
||||
|
||||
// Yellow color bold
|
||||
var Yellow = color.New(color.FgYellow, color.Bold).SprintFunc()
|
||||
|
||||
// YellowS color
|
||||
var YellowS = color.New(color.FgYellow).SprintFunc()
|
||||
|
||||
// MagentaS color
|
||||
var MagentaS = color.New(color.FgMagenta).SprintFunc()
|
||||
|
||||
// Magenta color bold
|
||||
var Magenta = color.New(color.FgMagenta, color.Bold).SprintFunc()
|
||||
|
||||
// WatcherIgnores is an array of default ignored paths
|
||||
var watcherIgnores = []string{"vendor", "bin"}
|
||||
|
||||
// WatcherExts is an array of default exts
|
||||
var watcherExts = []string{".go"}
|
||||
|
||||
// WatcherPaths is an array of default watched paths
|
||||
var watcherPaths = []string{"/"}
|
||||
|
||||
type logWriter struct{}
|
||||
|
||||
// App struct contains the informations about realize
|
||||
type App struct {
|
||||
Name, Version, Description, Author, Email string
|
||||
}
|
||||
|
||||
// Custom log timestamp
|
||||
func init() {
|
||||
log.SetFlags(0)
|
||||
log.SetOutput(new(logWriter))
|
||||
|
||||
// increases the files limit
|
||||
var rLimit syscall.Rlimit
|
||||
rLimit.Max = 10000
|
||||
rLimit.Cur = 10000
|
||||
err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
fmt.Println(Red("Error Setting Rlimit "), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Info returns the general informations about Realize
|
||||
func Info() *App {
|
||||
return &App{
|
||||
Name: AppName,
|
||||
Version: AppVersion,
|
||||
Description: AppDescription,
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
1
server/assets/index.html
Normal file
1
server/assets/index.html
Normal file
@ -0,0 +1 @@
|
||||
Testing
|
554
server/bindata.go
Normal file
554
server/bindata.go
Normal file
File diff suppressed because one or more lines are too long
108
server/main.go
Normal file
108
server/main.go
Normal file
@ -0,0 +1,108 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/engine/standard"
|
||||
"github.com/labstack/echo/middleware"
|
||||
c "github.com/tockins/realize/cli"
|
||||
"golang.org/x/net/websocket"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Server struct contains server informations
|
||||
type Server struct {
|
||||
Blueprint *c.Blueprint
|
||||
Files map[string]string
|
||||
Sync chan string
|
||||
}
|
||||
|
||||
func render(c echo.Context, path string, mime int) error {
|
||||
data, err := Asset(path)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound)
|
||||
}
|
||||
rs := c.Response()
|
||||
// check content type by extensions
|
||||
switch mime {
|
||||
case 1:
|
||||
rs.Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8)
|
||||
break
|
||||
case 2:
|
||||
rs.Header().Set(echo.HeaderContentType, echo.MIMEApplicationJavaScriptCharsetUTF8)
|
||||
break
|
||||
case 3:
|
||||
rs.Header().Set(echo.HeaderContentType, "text/css")
|
||||
break
|
||||
case 4:
|
||||
rs.Header().Set(echo.HeaderContentType, "image/svg+xml")
|
||||
break
|
||||
}
|
||||
rs.WriteHeader(http.StatusOK)
|
||||
rs.Write(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Server starting
|
||||
func (s *Server) Start() {
|
||||
e := echo.New()
|
||||
e.Use(middleware.Gzip())
|
||||
|
||||
// web panel
|
||||
e.GET("/", func(c echo.Context) error {
|
||||
return render(c, "assets/index.html", 1)
|
||||
})
|
||||
e.GET("/assets/js/all.min.js", func(c echo.Context) error {
|
||||
return render(c, "assets/assets/js/all.min.js", 2)
|
||||
})
|
||||
e.GET("/assets/css/app.css", func(c echo.Context) error {
|
||||
return render(c, "assets/assets/css/app.css", 3)
|
||||
})
|
||||
e.GET("/app/components/projects/index.html", func(c echo.Context) error {
|
||||
return render(c, "assets/app/components/projects/index.html", 1)
|
||||
})
|
||||
e.GET("/app/components/project/index.html", func(c echo.Context) error {
|
||||
return render(c, "assets/app/components/project/index.html", 1)
|
||||
})
|
||||
e.GET("/app/components/index.html", func(c echo.Context) error {
|
||||
return render(c, "assets/app/components/index.html", 1)
|
||||
})
|
||||
e.GET("/assets/img/svg/github-logo.svg", func(c echo.Context) error {
|
||||
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 {
|
||||
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 {
|
||||
return render(c, "assets/assets/img/svg/ic_swap_vertical_circle_black_48px.svg", 4)
|
||||
})
|
||||
|
||||
//websocket
|
||||
e.GET("/ws", standard.WrapHandler(s.projects()))
|
||||
go e.Run(standard.New(":5000"))
|
||||
Open("http://localhost:5000")
|
||||
}
|
||||
|
||||
// The WebSocket for projects list
|
||||
func (s *Server) projects() websocket.Handler {
|
||||
return websocket.Handler(func(ws *websocket.Conn) {
|
||||
msg := func() {
|
||||
|
||||
message, _ := json.Marshal(s.Blueprint.Projects)
|
||||
err := websocket.Message.Send(ws, string(message))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
msg()
|
||||
for {
|
||||
select {
|
||||
default:
|
||||
continue
|
||||
case <-s.Sync:
|
||||
msg()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
35
server/open.go
Normal file
35
server/open.go
Normal file
@ -0,0 +1,35 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var cli map[string]string
|
||||
var stderr bytes.Buffer
|
||||
|
||||
func init() {
|
||||
cli = map[string]string{
|
||||
"windows": "start",
|
||||
"darwin": "open",
|
||||
"linux": "xdg-open",
|
||||
}
|
||||
}
|
||||
|
||||
func Open(url string) (io.Writer, error) {
|
||||
if open, err := cli[runtime.GOOS]; !err {
|
||||
return nil, errors.New("This operating system is not supported.")
|
||||
} else {
|
||||
cmd := exec.Command(open, url)
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Println(cmd.Stderr, err)
|
||||
return cmd.Stderr, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user