From 54b9acef69d2165d7692a85d6b5684240b915c85 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Fri, 8 Nov 2024 04:07:53 -0600 Subject: [PATCH] switch to using go-cmd/cmd Signed-off-by: Jeff Carr --- Makefile | 34 +-------- buildPackage.go | 30 ++++---- cmd.go | 184 ++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 6 ++ 4 files changed, 207 insertions(+), 47 deletions(-) create mode 100644 cmd.go diff --git a/Makefile b/Makefile index 979763e..1e7f3b0 100644 --- a/Makefile +++ b/Makefile @@ -54,38 +54,8 @@ build-test-failure: build build-test-keep-files: build ./go-deb --no-gui --keep-files --repo go.wit.com/apps/go-deb -build-all: - go-deb --no-gui --repo go.wit.com/apps/go-deb - go-deb --no-gui --repo go.wit.com/apps/go-clone - go-deb --no-gui --repo go.wit.com/apps/autotypist - go-deb --no-gui --repo go.wit.com/apps/virtigoctl - go-deb --no-gui --repo go.wit.com/apps/virtigo - go-deb --no-gui --repo go.wit.com/apps/autotypist - go-deb --no-gui --repo go.wit.com/apps/guireleaser - go-deb --no-gui --repo go.wit.com/apps/gowebd - go-deb --no-gui --repo go.wit.com/apps/networkQuality - go-deb --no-gui --repo go.wit.com/apps/helloworld - go-deb --no-gui --repo go.wit.com/apps/basicwindow - go-deb --no-gui --repo go.wit.com/apps/gadgetwindow - go-deb --no-gui --repo go.wit.com/apps/powerpaneld - go-deb --no-gui --repo go.wit.com/lib/daemons/virtigod - go-deb --no-gui --repo go.wit.com/apps/control-panel-cloudflare - go-deb --no-gui --repo go.wit.com/apps/control-panel-dns - go-deb --no-gui --repo go.wit.com/apps/control-panel-droplet - go-deb --no-gui --repo go.wit.com/apps/control-panel-vpn - # go-deb --no-gui --repo golang.org/x/tools/cmd/goimports - -build-releases: - -go-deb --release --no-gui --repo go.wit.com/apps/autotypist - -go-deb --release --no-gui --repo go.wit.com/apps/control-panel-dns - -go-deb --release --no-gui --repo go.wit.com/apps/control-panel-digitalocean - -go-deb --release --no-gui --repo go.wit.com/apps/control-panel-cloudflare - -go-deb --release --no-gui --repo go.wit.com/apps/control-panel-vpn - -go-deb --release --no-gui --repo go.wit.com/apps/go-gui-toolkits - -go-deb --release --no-gui --repo go.wit.com/apps/guireleaser - -go-deb --release --no-gui --repo go.wit.com/apps/go.wit.com - -go-deb --release --no-gui --repo go.wit.com/apps/helloworld - -go-deb --release --no-gui --repo go.wit.com/apps/go-deb +build-release: + go-deb --release --no-gui --repo go.wit.com/apps/go-deb debian: build ./go-deb --no-gui --keep-files --repo go.wit.com/apps/go-deb diff --git a/buildPackage.go b/buildPackage.go index bf0657f..b7c1b27 100644 --- a/buildPackage.go +++ b/buildPackage.go @@ -62,7 +62,7 @@ func (c *controlBox) buildPackage() (bool, error) { os.Unsetenv("GO111MODULE") path := c.pathL.String() + "@latest" cmd := []string{"go", "install", "-v", "-x", path} - if shell.Run(cmd) { + if r := Run(cmd); r.Error == nil { log.Warn("go install worked") } else { return false, errors.New("go install") @@ -84,7 +84,7 @@ func (c *controlBox) buildPackage() (bool, error) { cmd = append(cmd, "-ldflags", "-X "+flag) } - if shell.Run(cmd) { + if r := Run(cmd); r.Error == nil { log.Warn("go build worked") } else { return false, errors.New("go build") @@ -118,13 +118,13 @@ func (c *controlBox) buildPackage() (bool, error) { log.Warn("mkdir failed") return false, errors.New("mkdir files/usr/bin") } - if !shell.Run([]string{"cp", fullfilename, "files/usr/bin"}) { + if r := Run([]string{"cp", fullfilename, "files/usr/bin"}); r.Error != nil { log.Warn("cp failed") - return false, errors.New("cp " + fullfilename) + return false, r.Error } - if !shell.Run([]string{"strip", "files/usr/bin/" + filename}) { + if r := Run([]string{"strip", "files/usr/bin/" + filename}); r.Error != nil { log.Warn("strip failed") - return false, errors.New("strip " + filename) + return false, r.Error } // put the README in there (if missing, generate it?) @@ -142,8 +142,8 @@ func (c *controlBox) buildPackage() (bool, error) { if !shell.Mkdir(path) { return false, errors.New("no files/usr/lib") } - if !shell.Run([]string{"cp", readme, path}) { - return false, errors.New("cp readme") + if r := Run([]string{"cp", readme, path}); r.Error != nil { + return false, r.Error } } @@ -151,7 +151,7 @@ func (c *controlBox) buildPackage() (bool, error) { return false, errors.New("write control file") } if shell.Exists("postinst") { - shell.Run([]string{"cp", "postinst", "files/DEBIAN/"}) + Run([]string{"cp", "postinst", "files/DEBIAN/"}) } if c.status == nil { @@ -167,26 +167,26 @@ func (c *controlBox) buildPackage() (bool, error) { } else { os.Setenv("GO111MODULE", "off") } - shell.Run([]string{"./build"}) + Run([]string{"./build"}) } - shell.Run([]string{"dpkg-deb", "--build", "files", fulldebname}) + Run([]string{"dpkg-deb", "--build", "files", fulldebname}) if shell.Exists(fulldebname) { } else { log.Warn("build failed") return false, errors.New("dpkg-deb --build failed") } - shell.Run([]string{"dpkg-deb", "-I", fulldebname}) - shell.Run([]string{"dpkg-deb", "-c", fulldebname}) + Run([]string{"dpkg-deb", "-I", fulldebname}) + Run([]string{"dpkg-deb", "-c", fulldebname}) // cleanup files if shell.Exists("files") { if argv.KeepFiles { log.Info("keeping the build files/") } else { - shell.Run([]string{"rm", "-rf", "files"}) + Run([]string{"rm", "-rf", "files"}) log.Info("running sync") - shell.Run([]string{"sync"}) + Run([]string{"sync"}) if shell.Exists("files") { log.Warn("rm -rf files/ failed. Run() returned false") return false, errors.New("rm files/") diff --git a/cmd.go b/cmd.go new file mode 100644 index 0000000..faaa3c6 --- /dev/null +++ b/cmd.go @@ -0,0 +1,184 @@ +package main + +// this is a simplified interaction with the excellent +// go-cmd/cmd package to work 'shell' like. + +// in all cases here, STDERR -> STDOUT +// If you want the output from whatever you run +// to be captured like it appears when you see it +// on the command line, this is what this tries to do + +/* + if r := shell.Run([]{"ping", "-c", "3", "localhost"}); r.Error == nil { + if r.Exit == 0 { + log.Println("ran ok") + } else { + log.Println("ran") + } + // all stdout/stderr captured in r.Stdout + } +*/ + +import ( + "errors" + "fmt" + "time" + + "github.com/go-cmd/cmd" + "go.wit.com/log" +) + +func Run(args []string) cmd.Status { + return PwdRun("", args) +} + +// absolutely doesn't echo anything +func PwdRunQuiet(pwd string, args []string) cmd.Status { + var arg0 string + var argx []string + // Check if the slice has at least one element (the command name) + if len(args) == 0 { + var s cmd.Status + s.Error = errors.New("Error: Command slice is empty.") + return s + } + if len(args) == 1 { + // Pass the first element as the command, and the rest as variadic arguments + arg0 = args[0] + } else { + arg0 = args[0] + argx = args[1:] + } + + // Start a long-running process, capture stdout and stderr + findCmd := cmd.NewCmd(arg0, argx...) + if pwd != "" { + findCmd.Dir = pwd + } + statusChan := findCmd.Start() // non-blocking + + ticker := time.NewTicker(2 * time.Second) + + // this is interesting, maybe useful, but wierd, but neat. interesting even + // Print last line of stdout every 2s + go func() { + for range ticker.C { + status := findCmd.Status() + n := len(status.Stdout) + if n != 0 { + fmt.Println(status.Stdout[n-1]) + } + } + }() + + // Stop command after 1 hour + go func() { + <-time.After(1 * time.Hour) + findCmd.Stop() + }() + + // Check if command is done + select { + case finalStatus := <-statusChan: + log.Info("finalStatus =", finalStatus.Exit, finalStatus.Error) + return finalStatus + // done + default: + // no, still running + } + + // Block waiting for command to exit, be stopped, or be killed + finalStatus := <-statusChan + return finalStatus +} + +func blah(cmd []string) { + r := Run(cmd) + log.Info("cmd =", r.Cmd) + log.Info("complete =", r.Complete) + log.Info("exit =", r.Exit) + log.Info("err =", r.Error) + log.Info("len(stdout+stderr) =", len(r.Stdout)) +} + +// run these to see confirm the sytem behaves as expected +func RunTest() { + blah([]string{"ping", "-c", "3", "localhost"}) + blah([]string{"exit", "0"}) + blah([]string{"exit", "-1"}) + blah([]string{"true"}) + blah([]string{"false"}) + blah([]string{"grep", "root", "/etc/", "/proc/cmdline", "/usr/bin/chmod"}) + blah([]string{"grep", "root", "/proc/cmdline"}) + fmt.Sprint("blahdone") +} + +// sets the exec dir if it's sent +// combines stdout and stderr +// echo's output +func PwdRun(pwd string, args []string) cmd.Status { + var save []string // combined stdout & stderr + var arg0 string + var argx []string + // Check if the slice has at least one element (the command name) + if len(args) == 0 { + var s cmd.Status + s.Error = errors.New("Error: Command slice is empty.") + return s + } + if len(args) == 1 { + // Pass the first element as the command, and the rest as variadic arguments + arg0 = args[0] + } else { + arg0 = args[0] + argx = args[1:] + } + + // Disable output buffering, enable streaming + cmdOptions := cmd.Options{ + Buffered: false, + Streaming: true, + } + + // Create Cmd with options + envCmd := cmd.NewCmdOptions(cmdOptions, arg0, argx...) + if pwd != "" { + envCmd.Dir = pwd + } + + // Print STDOUT and STDERR lines streaming from Cmd + doneChan := make(chan struct{}) + go func() { + defer close(doneChan) + // Done when both channels have been closed + // https://dave.cheney.net/2013/04/30/curious-channels + for envCmd.Stdout != nil || envCmd.Stderr != nil { + select { + case line, open := <-envCmd.Stdout: + if !open { + envCmd.Stdout = nil + continue + } + save = append(save, line) + fmt.Println(line) + case line, open := <-envCmd.Stderr: + if !open { + envCmd.Stderr = nil + continue + } + save = append(save, line) + fmt.Println(line) + } + } + }() + + // Run and wait for Cmd to return, discard Status + <-envCmd.Start() + + // Wait for goroutine to print everything + <-doneChan + + s := envCmd.Status() + s.Stdout = save + return s +} diff --git a/main.go b/main.go index edc3fda..4f30d19 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,8 @@ var basicWindow *gadgets.BasicWindow var resources embed.FS func main() { + // RunTest() + // os.Exit(0) if argv.Repo == "" { log.Info("You need to tell me what repo you want to work on") println("") @@ -67,6 +69,10 @@ func main() { os.Exit(-1) } + // set the working directory to argv.Repo + log.Info("cd", cBox.status.Path()) + os.Chdir(cBox.status.Path()) + if argv.NoGui { shell.TestTerminalColor() // basicWindow.Show() // broken gui package. convert to protobuf