Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: set command name explicitly and use it instead of the sequence number in the output #94

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Config struct {
onlyFiles bool
onlyDirs bool
allFiles bool
name string
}

func (c *Config) registerFlags(f *flag.FlagSet) {
Expand Down Expand Up @@ -53,6 +54,8 @@ func (c *Config) registerFlags(f *flag.FlagSet) {
Only match directories (not files).`)
f.BoolVar(&c.allFiles, "all", false, `
Include normally ignored files (VCS and editor special files).`)
f.StringVar(&c.name, "name", "", `
Use fixed name for the command instead of the sequence number.`)
}

// ReadConfigs reads configurations from either a file or, as a special case,
Expand Down
17 changes: 10 additions & 7 deletions print.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,27 @@ const (
)

type OutMsg struct {
reflexID int
msg string
reflexID int
reflexName string
msg string
}

func infoPrintln(id int, args ...interface{}) {
stdout <- OutMsg{id, strings.TrimSpace(fmt.Sprintln(args...))}
func infoPrintln(id int, name string, args ...interface{}) {
stdout <- OutMsg{id, name, strings.TrimSpace(fmt.Sprintln(args...))}
}
func infoPrintf(id int, format string, args ...interface{}) {
stdout <- OutMsg{id, fmt.Sprintf(format, args...)}
func infoPrintf(id int, name string, format string, args ...interface{}) {
stdout <- OutMsg{id, name, fmt.Sprintf(format, args...)}
}

func printMsg(msg OutMsg, writer io.Writer) {
tag := ""
if decoration == DecorationFancy || decoration == DecorationPlain {
if msg.reflexID < 0 {
tag = "[info]"
} else {
} else if msg.reflexName == "" {
tag = fmt.Sprintf("[%02d]", msg.reflexID)
} else {
tag = fmt.Sprintf("[%s]", msg.reflexName)
}
}

Expand Down
32 changes: 17 additions & 15 deletions reflex.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
// A Reflex is a single watch + command to execute.
type Reflex struct {
id int
name string
source string // Describes what config/line defines this Reflex
startService bool
backlog Backlog
Expand Down Expand Up @@ -84,6 +85,7 @@ func NewReflex(c *Config) (*Reflex, error) {

reflex := &Reflex{
id: reflexID,
name: c.name,
source: c.source,
startService: c.startService,
backlog: backlog,
Expand Down Expand Up @@ -148,11 +150,11 @@ func (r *Reflex) filterMatching(out chan<- string, in <-chan string) {

// batch receives file notification events and batches them up. It's a bit
// tricky, but here's what it accomplishes:
// * When we initially get a message, wait a bit and batch messages before
// trying to send anything. This is because the file events come in bursts.
// * Once it's time to send, don't do it until the out channel is unblocked.
// In the meantime, keep batching. When we've sent off all the batched
// messages, go back to the beginning.
// - When we initially get a message, wait a bit and batch messages before
// trying to send anything. This is because the file events come in bursts.
// - Once it's time to send, don't do it until the out channel is unblocked.
// In the meantime, keep batching. When we've sent off all the batched
// messages, go back to the beginning.
func (r *Reflex) batch(out chan<- string, in <-chan string) {

const silenceInterval = 300 * time.Millisecond
Expand Down Expand Up @@ -192,10 +194,10 @@ func (r *Reflex) runEach(names <-chan string) {
for name := range names {
if r.startService {
if r.Running() {
infoPrintln(r.id, "Killing service")
infoPrintln(r.id, r.name, "Killing service")
r.terminate()
}
infoPrintln(r.id, "Starting service")
infoPrintln(r.id, r.name, "Starting service")
r.runCommand(name, stdout)
} else {
r.runCommand(name, stdout)
Expand Down Expand Up @@ -224,16 +226,16 @@ func (r *Reflex) terminate() {
return
case <-timer.C:
if sig == syscall.SIGINT {
infoPrintln(r.id, "Sending SIGINT signal...")
infoPrintln(r.id, r.name, "Sending SIGINT signal...")
} else {
infoPrintln(r.id, "Sending SIGKILL signal...")
infoPrintln(r.id, r.name, "Sending SIGKILL signal...")
}

// Instead of killing the process, we want to kill its
// whole pgroup in order to clean up any children the
// process may have created.
if err := syscall.Kill(-1*r.cmd.Process.Pid, sig); err != nil {
infoPrintln(r.id, "Error killing:", err)
infoPrintln(r.id, r.name, "Error killing:", err)
if err.(syscall.Errno) == syscall.ESRCH { // no such process
return
}
Expand Down Expand Up @@ -269,7 +271,7 @@ func (r *Reflex) runCommand(name string, stdout chan<- OutMsg) {

tty, err := pty.Start(cmd)
if err != nil {
infoPrintln(r.id, err)
infoPrintln(r.id, r.name, err)
return
}
r.tty = tty
Expand All @@ -290,10 +292,10 @@ func (r *Reflex) runCommand(name string, stdout chan<- OutMsg) {
// Allow for lines up to 100 MB.
scanner.Buffer(nil, 100e6)
for scanner.Scan() {
stdout <- OutMsg{r.id, scanner.Text()}
stdout <- OutMsg{r.id, r.name, scanner.Text()}
}
if err := scanner.Err(); errors.Is(err, bufio.ErrTooLong) {
infoPrintln(r.id, "Error: subprocess emitted a line longer than 100 MB")
infoPrintln(r.id, r.name, "Error: subprocess emitted a line longer than 100 MB")
}
// Intentionally ignore other scanner errors. Unfortunately,
// the pty returns a read error when the child dies naturally,
Expand All @@ -307,7 +309,7 @@ func (r *Reflex) runCommand(name string, stdout chan<- OutMsg) {
go func() {
err := cmd.Wait()
if !r.Killed() && err != nil {
stdout <- OutMsg{r.id, fmt.Sprintf("(error exit: %s)", err)}
stdout <- OutMsg{r.id, r.name, fmt.Sprintf("(error exit: %s)", err)}
}
r.done <- struct{}{}

Expand All @@ -328,7 +330,7 @@ func (r *Reflex) Start(changes <-chan string) {
go r.runEach(batched)
if r.startService {
// Easy hack to kick off the initial start.
infoPrintln(r.id, "Starting service")
infoPrintln(r.id, r.name, "Starting service")
r.runCommand("", stdout)
}
}
Expand Down
8 changes: 4 additions & 4 deletions watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ const chmodMask fsnotify.Op = ^fsnotify.Op(0) ^ fsnotify.Chmod
// criteria of all reflexes can be ignored.
func watch(root string, watcher *fsnotify.Watcher, names chan<- string, done chan<- error, reflexes []*Reflex) {
if err := filepath.Walk(root, walker(watcher, reflexes)); err != nil {
infoPrintf(-1, "Error while walking path %s: %s", root, err)
infoPrintf(-1, "", "Error while walking path %s: %s", root, err)
}

for {
select {
case e := <-watcher.Events:
if verbose {
infoPrintln(-1, "fsnotify event:", e)
infoPrintln(-1, "", "fsnotify event:", e)
}
stat, err := os.Stat(e.Name)
if err != nil {
Expand All @@ -36,7 +36,7 @@ func watch(root string, watcher *fsnotify.Watcher, names chan<- string, done cha
names <- path
if e.Op&fsnotify.Create > 0 && stat.IsDir() {
if err := filepath.Walk(path, walker(watcher, reflexes)); err != nil {
infoPrintf(-1, "Error while walking path %s: %s", path, err)
infoPrintf(-1, "", "Error while walking path %s: %s", path, err)
}
}
// TODO: Cannot currently remove fsnotify watches
Expand Down Expand Up @@ -68,7 +68,7 @@ func walker(watcher *fsnotify.Watcher, reflexes []*Reflex) filepath.WalkFunc {
return filepath.SkipDir
}
if err := watcher.Add(path); err != nil {
infoPrintf(-1, "Error while watching new path %s: %s", path, err)
infoPrintf(-1, "", "Error while watching new path %s: %s", path, err)
}
return nil
}
Expand Down