feat(dispatch): launch interactive menu on no-arg TTY invocation
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/app"
|
||||
"git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/buildinfo"
|
||||
"git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/menu"
|
||||
)
|
||||
|
||||
// Exit codes follow the sysexits(3) convention so CI can distinguish
|
||||
@@ -103,8 +104,52 @@ func find(name string) (Builder, bool) {
|
||||
return Builder{}, false
|
||||
}
|
||||
|
||||
// Main is the `crucible` dispatcher entrypoint.
|
||||
func Main(args []string) int { return run(args, os.Stdout, os.Stderr) }
|
||||
// Main is the `crucible` dispatcher entrypoint. With no arguments on an
|
||||
// interactive terminal it launches the menu; otherwise (pipes, CI) it prints
|
||||
// usage, preserving scriptable behavior.
|
||||
func Main(args []string) int {
|
||||
if len(args) == 0 && interactive(os.Stdout) {
|
||||
return runMenu(os.Stdout, os.Stderr, os.Stdin)
|
||||
}
|
||||
return run(args, os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
func interactive(f *os.File) bool {
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fi.Mode()&os.ModeCharDevice != 0
|
||||
}
|
||||
|
||||
func menuItems() []menu.Item {
|
||||
var items []menu.Item
|
||||
for _, b := range Registry {
|
||||
if !b.Wired {
|
||||
continue
|
||||
}
|
||||
for _, sub := range b.subcommands() {
|
||||
items = append(items, menu.Item{
|
||||
Label: b.Name + " " + sub,
|
||||
Desc: b.Summary,
|
||||
Args: []string{b.Name, sub},
|
||||
})
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func runMenu(out, errw io.Writer, in io.Reader) int {
|
||||
chosen, err := menu.Select(out, in, menuItems())
|
||||
if err != nil {
|
||||
fmt.Fprintln(errw, "crucible:", err)
|
||||
return exitUsage
|
||||
}
|
||||
if chosen == nil {
|
||||
return exitOK
|
||||
}
|
||||
return run(chosen, out, errw)
|
||||
}
|
||||
|
||||
// RunBuilder is the standalone `crucible-<name>` entrypoint.
|
||||
func RunBuilder(name string, args []string) int {
|
||||
|
||||
Reference in New Issue
Block a user