WIP: stuff
This commit is contained in:
19
cli/internal/apps/msg.go
Normal file
19
cli/internal/apps/msg.go
Normal file
@ -0,0 +1,19 @@
|
||||
package apps
|
||||
|
||||
import "time"
|
||||
|
||||
type Msg struct {
|
||||
Text string
|
||||
Time time.Time
|
||||
Key *string
|
||||
Loading *bool
|
||||
Success *bool
|
||||
|
||||
App *App
|
||||
}
|
||||
|
||||
type App struct {
|
||||
Name string
|
||||
Color string
|
||||
Loading *bool
|
||||
}
|
118
cli/internal/apps/node.go
Normal file
118
cli/internal/apps/node.go
Normal file
@ -0,0 +1,118 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/spotdemo4/trevstack/cli/internal/utils"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
App App
|
||||
c chan Msg
|
||||
ctx context.Context
|
||||
wg *sync.WaitGroup
|
||||
|
||||
dir string
|
||||
|
||||
Cancel context.CancelFunc
|
||||
Wait func()
|
||||
}
|
||||
|
||||
func NewNode(dir string, c chan Msg) *Node {
|
||||
|
||||
// Create new context
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// Create wait group
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
node := Node{
|
||||
App: App{
|
||||
Name: "node",
|
||||
Color: "#fab387",
|
||||
},
|
||||
c: c,
|
||||
ctx: ctx,
|
||||
wg: &wg,
|
||||
|
||||
dir: dir,
|
||||
|
||||
Cancel: cancel,
|
||||
Wait: wg.Wait,
|
||||
}
|
||||
|
||||
// Start watching
|
||||
go node.dev()
|
||||
|
||||
return &node
|
||||
}
|
||||
|
||||
func (n *Node) msg(m Msg) {
|
||||
m.Time = time.Now()
|
||||
m.App = &n.App
|
||||
n.c <- m
|
||||
}
|
||||
|
||||
func (n *Node) dev() {
|
||||
n.wg.Add(1)
|
||||
defer n.wg.Done()
|
||||
|
||||
// Create cmd
|
||||
cmd := exec.Command("npm", "run", "dev")
|
||||
cmd.Dir = n.dir
|
||||
|
||||
// Stop cmd on exit
|
||||
n.wg.Add(1)
|
||||
go func() {
|
||||
defer n.wg.Done()
|
||||
<-n.ctx.Done()
|
||||
|
||||
if err := cmd.Process.Signal(os.Interrupt); err != nil {
|
||||
cmd.Process.Kill() // If the process is not responding to the interrupt signal, kill it
|
||||
}
|
||||
}()
|
||||
|
||||
// Start cmd
|
||||
out, err := utils.Run(cmd)
|
||||
if err != nil {
|
||||
n.msg(Msg{
|
||||
Text: err.Error(),
|
||||
Success: utils.BoolPointer(false),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Watch for output
|
||||
for line := range out {
|
||||
switch line := line.(type) {
|
||||
case utils.Stdout:
|
||||
n.msg(Msg{
|
||||
Text: string(line),
|
||||
})
|
||||
|
||||
case utils.Stderr:
|
||||
n.msg(Msg{
|
||||
Text: string(line),
|
||||
Success: utils.BoolPointer(false),
|
||||
})
|
||||
|
||||
case utils.ExitCode:
|
||||
if line == 0 {
|
||||
n.msg(Msg{
|
||||
Text: "Node stopped",
|
||||
Success: utils.BoolPointer(true),
|
||||
})
|
||||
} else {
|
||||
n.msg(Msg{
|
||||
Text: fmt.Sprintf("Node failed with exit code %d", out),
|
||||
Success: utils.BoolPointer(false),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
269
cli/internal/apps/proto.go
Normal file
269
cli/internal/apps/proto.go
Normal file
@ -0,0 +1,269 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spotdemo4/trevstack/cli/internal/utils"
|
||||
)
|
||||
|
||||
type Proto struct {
|
||||
App App
|
||||
c chan Msg
|
||||
ctx context.Context
|
||||
wg *sync.WaitGroup
|
||||
watcher *fsnotify.Watcher
|
||||
|
||||
dir string
|
||||
rootDir string
|
||||
|
||||
Cancel context.CancelFunc
|
||||
Wait func()
|
||||
}
|
||||
|
||||
func NewProto(dir string, rootDir string, c chan Msg) (*Proto, error) {
|
||||
|
||||
// Create new watcher
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add directory to watcher
|
||||
err = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
if slices.Contains(watcher.WatchList(), path) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := watcher.Add(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create new context
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// Create wait group
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
proto := Proto{
|
||||
App: App{
|
||||
Name: "proto",
|
||||
Color: "#89dceb",
|
||||
},
|
||||
c: c,
|
||||
ctx: ctx,
|
||||
wg: &wg,
|
||||
watcher: watcher,
|
||||
|
||||
dir: dir,
|
||||
rootDir: rootDir,
|
||||
|
||||
Cancel: cancel,
|
||||
Wait: wg.Wait,
|
||||
}
|
||||
|
||||
// Start watching
|
||||
go proto.watch()
|
||||
|
||||
return &proto, nil
|
||||
}
|
||||
|
||||
func (p *Proto) msg(m Msg) {
|
||||
m.Time = time.Now()
|
||||
m.App = &p.App
|
||||
p.c <- m
|
||||
}
|
||||
|
||||
func (p *Proto) watch() {
|
||||
p.wg.Add(1)
|
||||
defer p.wg.Done()
|
||||
defer p.watcher.Close()
|
||||
|
||||
// Create new rate limit map
|
||||
rateLimit := make(map[string]time.Time)
|
||||
|
||||
p.lint()
|
||||
|
||||
p.msg(Msg{
|
||||
Text: "Watching for proto changes...",
|
||||
})
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-p.ctx.Done():
|
||||
return
|
||||
|
||||
case event, ok := <-p.watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Rate limit
|
||||
rl, ok := rateLimit[event.Name]
|
||||
if ok && time.Since(rl) < 1*time.Second {
|
||||
continue
|
||||
}
|
||||
rateLimit[event.Name] = time.Now()
|
||||
|
||||
p.msg(Msg{
|
||||
Text: "File changed: " + strings.TrimPrefix(event.Name, p.dir),
|
||||
})
|
||||
|
||||
ok, _ = p.lint()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
p.generate()
|
||||
|
||||
case err, ok := <-p.watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
p.msg(Msg{
|
||||
Text: err.Error(),
|
||||
Success: utils.BoolPointer(false),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proto) lint() (bool, error) {
|
||||
p.msg(Msg{
|
||||
Text: "Linting",
|
||||
Loading: utils.BoolPointer(true),
|
||||
Key: utils.StringPointer("lint"),
|
||||
})
|
||||
|
||||
// Run buf lint
|
||||
cmd := exec.Command("buf", "lint")
|
||||
cmd.Dir = p.rootDir
|
||||
out, err := utils.Run(cmd)
|
||||
if err != nil {
|
||||
p.msg(Msg{
|
||||
Text: err.Error(),
|
||||
Success: utils.BoolPointer(false),
|
||||
})
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Watch for output
|
||||
for line := range out {
|
||||
switch line := line.(type) {
|
||||
case utils.Stdout:
|
||||
p.msg(Msg{
|
||||
Text: string(line),
|
||||
})
|
||||
|
||||
case utils.Stderr:
|
||||
p.msg(Msg{
|
||||
Text: string(line),
|
||||
Success: utils.BoolPointer(false),
|
||||
})
|
||||
|
||||
case utils.ExitCode:
|
||||
if line == 0 {
|
||||
p.msg(Msg{
|
||||
Text: "Buf lint successful",
|
||||
Success: utils.BoolPointer(true),
|
||||
Loading: utils.BoolPointer(false),
|
||||
Key: utils.StringPointer("lint"),
|
||||
})
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
p.msg(Msg{
|
||||
Text: fmt.Sprintf("Buf lint failed with exit code %d", out),
|
||||
Success: utils.BoolPointer(false),
|
||||
Loading: utils.BoolPointer(false),
|
||||
Key: utils.StringPointer("lint"),
|
||||
})
|
||||
|
||||
return false, fmt.Errorf("buf lint failed with exit code %d", line)
|
||||
}
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("buf lint failed")
|
||||
}
|
||||
|
||||
func (p *Proto) generate() error {
|
||||
p.msg(Msg{
|
||||
Text: "Generating proto files",
|
||||
Loading: utils.BoolPointer(true),
|
||||
Key: utils.StringPointer("generate"),
|
||||
})
|
||||
|
||||
// Run buf gen
|
||||
cmd := exec.Command("buf", "generate")
|
||||
cmd.Dir = p.rootDir
|
||||
out, err := utils.Run(cmd)
|
||||
if err != nil {
|
||||
p.msg(Msg{
|
||||
Text: err.Error(),
|
||||
Success: utils.BoolPointer(false),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Watch for output
|
||||
for line := range out {
|
||||
switch line := line.(type) {
|
||||
case utils.Stdout:
|
||||
p.msg(Msg{
|
||||
Text: string(line),
|
||||
})
|
||||
|
||||
case utils.Stderr:
|
||||
p.msg(Msg{
|
||||
Text: string(line),
|
||||
Success: utils.BoolPointer(false),
|
||||
})
|
||||
|
||||
case utils.ExitCode:
|
||||
if line == 0 {
|
||||
p.msg(Msg{
|
||||
Text: "Buf generate successful",
|
||||
Success: utils.BoolPointer(true),
|
||||
Loading: utils.BoolPointer(false),
|
||||
Key: utils.StringPointer("generate"),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
p.msg(Msg{
|
||||
Text: fmt.Sprintf("Buf generate failed with exit code %d", out),
|
||||
Success: utils.BoolPointer(false),
|
||||
Loading: utils.BoolPointer(false),
|
||||
Key: utils.StringPointer("generate"),
|
||||
})
|
||||
|
||||
return fmt.Errorf("generate failed with exit code %d", line)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("generate failed")
|
||||
}
|
Reference in New Issue
Block a user