Compare commits
No commits in common. "master" and "v0.22.2" have entirely different histories.
8
Makefile
8
Makefile
|
@ -1,9 +1,5 @@
|
||||||
all: goimports vet
|
all:
|
||||||
|
GO111MODULE=off go build
|
||||||
vet:
|
|
||||||
@GO111MODULE=off go vet
|
|
||||||
@echo this go library package builds okay
|
|
||||||
|
|
||||||
|
|
||||||
goimports:
|
goimports:
|
||||||
goimports -w *.go
|
goimports -w *.go
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
package shell
|
||||||
|
|
||||||
|
/*
|
||||||
|
perl 'chomp'
|
||||||
|
|
||||||
|
send it anything, always get back a string
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// import "github.com/davecgh/go-spew/spew"
|
||||||
|
|
||||||
|
func chompBytesBuffer(buf *bytes.Buffer) string {
|
||||||
|
var bytesSplice []byte
|
||||||
|
bytesSplice = buf.Bytes()
|
||||||
|
|
||||||
|
return Chomp(string(bytesSplice))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: obviously this is stupidly wrong
|
||||||
|
// TODO: fix this to trim fucking everything
|
||||||
|
// really world? 8 fucking years of this language
|
||||||
|
// and I'm fucking writing this? jesus. how the
|
||||||
|
// hell is everyone else doing this? Why isn't
|
||||||
|
// this already in the strings package?
|
||||||
|
func perlChomp(s string) string {
|
||||||
|
// lots of stuff in go moves around the whole block of whatever it is so lots of things are padded with NULL values
|
||||||
|
s = strings.Trim(s, "\x00") // removes NULL (needed!)
|
||||||
|
|
||||||
|
// TODO: christ. make some fucking regex that takes out every NULL, \t, ' ", \n, and \r
|
||||||
|
s = strings.Trim(s, "\n")
|
||||||
|
s = strings.Trim(s, "\n")
|
||||||
|
s = strings.TrimSuffix(s, "\r")
|
||||||
|
s = strings.TrimSuffix(s, "\n")
|
||||||
|
|
||||||
|
s = strings.TrimSpace(s) // this is like 'chomp' in perl
|
||||||
|
s = strings.TrimSuffix(s, "\n") // this is like 'chomp' in perl
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: fix this to chomp \n \r NULL \t and ' '
|
||||||
|
func Chomp(a interface{}) string {
|
||||||
|
// switch reflect.TypeOf(a) {
|
||||||
|
switch t := a.(type) {
|
||||||
|
case string:
|
||||||
|
var s string
|
||||||
|
s = a.(string)
|
||||||
|
return perlChomp(s)
|
||||||
|
case []uint8:
|
||||||
|
// log.Printf("shell.Chomp() FOUND []uint8")
|
||||||
|
var tmp []uint8
|
||||||
|
tmp = a.([]uint8)
|
||||||
|
|
||||||
|
s := string(tmp)
|
||||||
|
return perlChomp(s)
|
||||||
|
case uint64:
|
||||||
|
// log.Printf("shell.Chomp() FOUND []uint64")
|
||||||
|
s := fmt.Sprintf("%d", a.(uint64))
|
||||||
|
return perlChomp(s)
|
||||||
|
case int64:
|
||||||
|
// log.Printf("shell.Chomp() FOUND []int64")
|
||||||
|
s := fmt.Sprintf("%d", a.(int64))
|
||||||
|
return perlChomp(s)
|
||||||
|
case *bytes.Buffer:
|
||||||
|
// log.Printf("shell.Chomp() FOUND *bytes.Buffer")
|
||||||
|
var tmp *bytes.Buffer
|
||||||
|
tmp = a.(*bytes.Buffer)
|
||||||
|
if tmp == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytesSplice []byte
|
||||||
|
bytesSplice = tmp.Bytes()
|
||||||
|
return Chomp(string(bytesSplice))
|
||||||
|
default:
|
||||||
|
tmp := fmt.Sprint("shell.Chomp() NO HANDLER FOR TYPE: %T", a)
|
||||||
|
handleError(fmt.Errorf(tmp), -1)
|
||||||
|
log.Warn("shell.Chomp() NEED TO MAKE CONVERTER FOR type =", reflect.TypeOf(t))
|
||||||
|
}
|
||||||
|
tmp := "shell.Chomp() THIS SHOULD NEVER HAPPEN"
|
||||||
|
handleError(fmt.Errorf(tmp), -1)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is stuff from a long time ago that there must be a replacement for
|
||||||
|
func RemoveFirstElement(slice []string) (string, []string) {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return "", slice // Return the original slice if it's empty
|
||||||
|
}
|
||||||
|
return slice[0], slice[1:] // Return the slice without the first element
|
||||||
|
}
|
290
cmd.go
290
cmd.go
|
@ -1,290 +0,0 @@
|
||||||
package shell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-cmd/cmd"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// shortcut, sends a blank value for pwd
|
|
||||||
// which means the exec Dir is not set
|
|
||||||
// echos output (otherwise use RunQuiet)
|
|
||||||
func Run(argv []string) cmd.Status {
|
|
||||||
return PathRun("", argv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// exec the cmd at a filepath. this does not change the working directory
|
|
||||||
// sets the exec dir if it's not an empty string
|
|
||||||
// combines stdout and stderr
|
|
||||||
// echo's output (otherwise use PathRunQuiet()
|
|
||||||
// this is basically the exact example from the go-cmd/cmd devs
|
|
||||||
// where the have rocked out a proper smart read on both filehandles
|
|
||||||
// https://dave.cheney.net/2013/04/30/curious-channels
|
|
||||||
func PathRun(path string, argv []string) cmd.Status {
|
|
||||||
return PathRunLog(path, argv, INFO)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the actual wrapper around go-cmd/cmd
|
|
||||||
// adds a log Flag so that echo to stdout can be enabled/disabled
|
|
||||||
func PathRunLog(path string, argv []string, logf *log.LogFlag) cmd.Status {
|
|
||||||
var save []string // combined stdout & stderr
|
|
||||||
var arg0 string
|
|
||||||
var args []string
|
|
||||||
if logf == nil {
|
|
||||||
logf = NOW
|
|
||||||
}
|
|
||||||
log.Log(logf, "shell.PathRunLog() Path =", path, "cmd =", argv)
|
|
||||||
// Check if the slice has at least one element (the command name)
|
|
||||||
if len(argv) == 0 {
|
|
||||||
var s cmd.Status
|
|
||||||
s.Error = errors.New("Error: Command slice is empty.")
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
if len(argv) == 1 {
|
|
||||||
// Pass the first element as the command, and the rest as variadic arguments
|
|
||||||
arg0 = argv[0]
|
|
||||||
} else {
|
|
||||||
arg0 = argv[0]
|
|
||||||
args = argv[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable output buffering, enable streaming
|
|
||||||
cmdOptions := cmd.Options{
|
|
||||||
Buffered: false,
|
|
||||||
Streaming: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Cmd with options
|
|
||||||
envCmd := cmd.NewCmdOptions(cmdOptions, arg0, args...)
|
|
||||||
if path != "" {
|
|
||||||
// set the path for exec
|
|
||||||
envCmd.Dir = path
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
log.Log(logf, line)
|
|
||||||
// fmt.Println(line)
|
|
||||||
case line, open := <-envCmd.Stderr:
|
|
||||||
if !open {
|
|
||||||
envCmd.Stderr = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
save = append(save, line)
|
|
||||||
log.Log(logf, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// uses the 'log' package to disable echo to STDOUT
|
|
||||||
// only echos if you enable the shell.INFO log flag
|
|
||||||
func RunQuiet(args []string) cmd.Status {
|
|
||||||
return PathRunLog("", args, INFO)
|
|
||||||
}
|
|
||||||
|
|
||||||
// uses the 'log' package to disable echo to STDOUT
|
|
||||||
// only echos if you enable the shell.INFO log flag
|
|
||||||
func PathRunQuiet(pwd string, args []string) cmd.Status {
|
|
||||||
return PathRunLog(pwd, args, INFO)
|
|
||||||
}
|
|
||||||
|
|
||||||
// send blank path == use current golang working directory
|
|
||||||
func RunRealtime(args []string) cmd.Status {
|
|
||||||
return PathRunRealtime("", args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunEcho(cmd []string) cmd.Status {
|
|
||||||
result := RunQuiet(cmd)
|
|
||||||
pwd, _ := os.Getwd()
|
|
||||||
log.Warn("shell.RunEcho() cmd:", cmd, pwd)
|
|
||||||
log.Warn("shell.RunEcho() Exit:", result.Exit)
|
|
||||||
log.Warn("shell.RunEcho() Error:", result.Error)
|
|
||||||
for _, line := range result.Stdout {
|
|
||||||
log.Warn("STDOUT:", line)
|
|
||||||
}
|
|
||||||
for _, line := range result.Stderr {
|
|
||||||
log.Warn("STDERR:", line)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// echos twice a second if anything sends to STDOUT or STDERR
|
|
||||||
// not great, but it's really just for watching things run in real time anyway
|
|
||||||
// TODO: fix \r handling for things like git-clone so the terminal doesn't
|
|
||||||
// have to do a \n newline each time.
|
|
||||||
// TODO: add timeouts and status of things hanging around forever
|
|
||||||
func PathRunRealtime(pwd string, args []string) cmd.Status {
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a long-running process, capture stdout and stderr
|
|
||||||
a, b := RemoveFirstElement(args)
|
|
||||||
findCmd := cmd.NewCmd(a, b...)
|
|
||||||
if pwd != "" {
|
|
||||||
findCmd.Dir = pwd
|
|
||||||
}
|
|
||||||
statusChan := findCmd.Start() // non-blocking
|
|
||||||
|
|
||||||
ticker := time.NewTicker(100 * time.Microsecond)
|
|
||||||
|
|
||||||
// this is interesting, maybe useful, but wierd, but neat. interesting even
|
|
||||||
// Print last line of stdout every 2s
|
|
||||||
go func() {
|
|
||||||
// loop very quickly, but only print the line if it changes
|
|
||||||
var lastout string
|
|
||||||
var lasterr string
|
|
||||||
for range ticker.C {
|
|
||||||
status := findCmd.Status()
|
|
||||||
n := len(status.Stdout)
|
|
||||||
if n != 0 {
|
|
||||||
newline := status.Stdout[n-1]
|
|
||||||
if lastout != newline {
|
|
||||||
lastout = newline
|
|
||||||
log.Info(lastout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n = len(status.Stderr)
|
|
||||||
if n != 0 {
|
|
||||||
newline := status.Stderr[n-1]
|
|
||||||
if lasterr != newline {
|
|
||||||
lasterr = newline
|
|
||||||
log.Info(lasterr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if status.Complete {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// there are things being left around here. debug this
|
|
||||||
finalStatus := <-statusChan
|
|
||||||
if len(finalStatus.Cmd) != 0 {
|
|
||||||
if string(finalStatus.Cmd) != "go" {
|
|
||||||
log.Info("shell.Run() ok goroutine end?", finalStatus.Cmd, finalStatus.Exit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return findCmd.Status()
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is stuff from a long time ago that there must be a replacement for
|
|
||||||
func RemoveFirstElement(slice []string) (string, []string) {
|
|
||||||
if len(slice) == 0 {
|
|
||||||
return "", slice // Return the original slice if it's empty
|
|
||||||
}
|
|
||||||
return slice[0], slice[1:] // Return the slice without the first element
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunVerbose(cmd []string) (*cmd.Status, error) {
|
|
||||||
pwd, _ := os.Getwd()
|
|
||||||
log.Info("Running:", pwd, cmd)
|
|
||||||
r, err := RunStrict(cmd)
|
|
||||||
if err != nil {
|
|
||||||
log.Info("Error", cmd, err)
|
|
||||||
}
|
|
||||||
for _, line := range r.Stdout {
|
|
||||||
log.Info(line)
|
|
||||||
}
|
|
||||||
for _, line := range r.Stderr {
|
|
||||||
log.Info(line)
|
|
||||||
}
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunVerboseOnError(cmd []string) (*cmd.Status, error) {
|
|
||||||
r, err := RunStrict(cmd)
|
|
||||||
if err == nil {
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
pwd, _ := os.Getwd()
|
|
||||||
log.Info("Run Error:", pwd, cmd, err)
|
|
||||||
for _, line := range r.Stdout {
|
|
||||||
log.Info(line)
|
|
||||||
}
|
|
||||||
for _, line := range r.Stderr {
|
|
||||||
log.Info(line)
|
|
||||||
}
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunStrict(cmd []string) (*cmd.Status, error) {
|
|
||||||
pwd, _ := os.Getwd()
|
|
||||||
result := PathRunQuiet(pwd, cmd)
|
|
||||||
if result.Error != nil {
|
|
||||||
log.Warn(pwd, cmd, "wow. golang is cool. an os.Error:", result.Error)
|
|
||||||
return &result, result.Error
|
|
||||||
}
|
|
||||||
if result.Exit != 0 {
|
|
||||||
// log.Warn(cmd, "failed with", result.Exit, repo.GetGoPath())
|
|
||||||
return &result, errors.New(fmt.Sprint(cmd, "failed with", result.Exit))
|
|
||||||
}
|
|
||||||
return &result, nil
|
|
||||||
}
|
|
71
exec.go
71
exec.go
|
@ -1,71 +0,0 @@
|
||||||
package shell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Exec(args []string) error {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return errors.New("Error: Command slice is empty.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a long-running process, capture stdout and stderr
|
|
||||||
a, b := RemoveFirstElement(args)
|
|
||||||
|
|
||||||
process := exec.Command(a, b...)
|
|
||||||
process.Stderr = os.Stderr
|
|
||||||
process.Stdin = os.Stdin
|
|
||||||
process.Stdout = os.Stdout
|
|
||||||
process.Start()
|
|
||||||
err := process.Wait()
|
|
||||||
log.Log(INFO, "shell.Exec() err =", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExecCheck(args []string) error {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return errors.New("Error: Command slice is empty.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a long-running process, capture stdout and stderr
|
|
||||||
a, b := RemoveFirstElement(args)
|
|
||||||
|
|
||||||
process := exec.Command(a, b...)
|
|
||||||
process.Stderr = os.Stderr
|
|
||||||
process.Stdin = os.Stdin
|
|
||||||
process.Stdout = os.Stdout
|
|
||||||
err := process.Run()
|
|
||||||
if err != nil {
|
|
||||||
log.Info("ExecCheck() err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// log.Info("ExecCheck() nil")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func PathExecVerbose(path string, args []string) error {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return errors.New("Error: Command slice is empty.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a long-running process, capture stdout and stderr
|
|
||||||
a, b := RemoveFirstElement(args)
|
|
||||||
|
|
||||||
process := exec.Command(a, b...)
|
|
||||||
process.Dir = path
|
|
||||||
process.Stderr = os.Stderr
|
|
||||||
process.Stdin = os.Stdin
|
|
||||||
process.Stdout = os.Stdout
|
|
||||||
err := process.Run()
|
|
||||||
log.Info("Exec() cmd:", args)
|
|
||||||
if err != nil {
|
|
||||||
log.Info("ExecCheck() err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// log.Info("ExecCheck() nil")
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package shell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SudoRaw(c []string) {
|
|
||||||
args := []string{"-S"}
|
|
||||||
args = append(args, c...)
|
|
||||||
cmd := exec.Command("sudo", args...)
|
|
||||||
|
|
||||||
// Assign the current process's standard input, output, and error
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
|
|
||||||
// Ensure the process has a terminal session
|
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
Setsid: true, // Start a new session
|
|
||||||
}
|
|
||||||
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Command execution failed:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Sudo(c []string) error {
|
|
||||||
args := []string{"-S"}
|
|
||||||
// args := []string{}
|
|
||||||
args = append(args, c...)
|
|
||||||
cmd := exec.Command("sudo", args...)
|
|
||||||
|
|
||||||
// Open the terminal device directly to preserve input/output control
|
|
||||||
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to open /dev/tty:", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer tty.Close()
|
|
||||||
|
|
||||||
// Assign the TTY explicitly
|
|
||||||
cmd.Stdin = tty
|
|
||||||
cmd.Stdout = tty
|
|
||||||
cmd.Stderr = tty
|
|
||||||
|
|
||||||
// Ensure the new process gets its own session
|
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
Setsid: true, // Start a new session
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the command
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
fmt.Println("Command execution failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("\nProcess finished. TTY restored.")
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package shell
|
||||||
|
|
||||||
|
/*
|
||||||
|
send it anything, always get back an int
|
||||||
|
*/
|
||||||
|
|
||||||
|
// import "log"
|
||||||
|
// import "reflect"
|
||||||
|
// import "strings"
|
||||||
|
// import "bytes"
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func Int(s string) int {
|
||||||
|
s = Chomp(s)
|
||||||
|
i, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
handleError(err, -1)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func Int64(s string) int64 {
|
||||||
|
s = Chomp(s)
|
||||||
|
i, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
handleError(err, -1)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int64(i)
|
||||||
|
}
|
95
posix.go
95
posix.go
|
@ -1,95 +0,0 @@
|
||||||
package shell
|
|
||||||
|
|
||||||
// old code and probably junk
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: look at https://github.com/go-cmd/cmd/issues/20
|
|
||||||
// use go-cmd instead here?
|
|
||||||
|
|
||||||
var callback func(interface{}, int)
|
|
||||||
|
|
||||||
// var shellStdout *os.File
|
|
||||||
// var shellStderr *os.File
|
|
||||||
|
|
||||||
// var spewOn bool = false
|
|
||||||
// var quiet bool = false
|
|
||||||
|
|
||||||
// var msecDelay int = 20 // number of milliseconds to delay between reads with no data
|
|
||||||
|
|
||||||
// var bytesBuffer bytes.Buffer
|
|
||||||
// var bytesSplice []byte
|
|
||||||
|
|
||||||
func handleError(c interface{}, ret int) {
|
|
||||||
log.Log(INFO, "shell.Run() Returned", ret)
|
|
||||||
if callback != nil {
|
|
||||||
callback(c, ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
callback = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitCallback(f func(interface{}, int)) {
|
|
||||||
callback = f
|
|
||||||
}
|
|
||||||
|
|
||||||
func Unlink(filename string) bool {
|
|
||||||
if err := os.Remove(filename); err != nil {
|
|
||||||
return Exists(filename)
|
|
||||||
} else {
|
|
||||||
return Exists(filename)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// run interactively. output from the cmd is in real time
|
|
||||||
// shows all the output. For example, 'ping -n localhost'
|
|
||||||
// shows the output like you would expect to see
|
|
||||||
func RunSimple(cmd []string) error {
|
|
||||||
log.Log(INFO, "NewRun() ", cmd)
|
|
||||||
|
|
||||||
return PathRunSimple("", cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PathRunSimple(workingpath string, cmd []string) error {
|
|
||||||
log.Log(INFO, "NewRun() ", cmd)
|
|
||||||
|
|
||||||
process := exec.Command(cmd[0], cmd[1:len(cmd)]...)
|
|
||||||
// Set the working directory
|
|
||||||
process.Dir = workingpath
|
|
||||||
process.Stderr = os.Stderr
|
|
||||||
process.Stdin = os.Stdin
|
|
||||||
process.Stdout = os.Stdout
|
|
||||||
process.Start()
|
|
||||||
err := process.Wait()
|
|
||||||
if err != nil {
|
|
||||||
log.Log(INFO, "shell.Exec() err =", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// return true if the filename exists (cross-platform)
|
|
||||||
|
|
||||||
// return true if the filename exists (cross-platform)
|
|
||||||
func Exists(filename string) bool {
|
|
||||||
_, err := os.Stat(Path(filename))
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// return true if the filename exists (cross-platform)
|
|
||||||
func IsDir(dirname string) bool {
|
|
||||||
info, err := os.Stat(Path(dirname))
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return info.IsDir()
|
|
||||||
}
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
package shell
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var msecDelay int = 20 // check every 20 milliseconds
|
||||||
|
|
||||||
|
// TODO: look at https://github.com/go-cmd/cmd/issues/20
|
||||||
|
// use go-cmd instead here?
|
||||||
|
// exiterr.Sys().(syscall.WaitStatus)
|
||||||
|
|
||||||
|
// run command and return it's output
|
||||||
|
/*
|
||||||
|
func RunCapture(cmdline string) string {
|
||||||
|
test := New()
|
||||||
|
test.Exec(cmdline)
|
||||||
|
return Chomp(test.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunWait(args []string) *OldShell {
|
||||||
|
test := New()
|
||||||
|
cmdline := strings.Join(args, " ")
|
||||||
|
test.Exec(cmdline)
|
||||||
|
return test
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// var newfile *shell.File
|
||||||
|
func RunString(args string) bool {
|
||||||
|
// return false
|
||||||
|
parts := strings.Split(args, " ")
|
||||||
|
return Run(parts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(args []string) bool {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
r := RunPath(dir, args)
|
||||||
|
if r.ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrorArgvEmpty error = errors.New("command was empty")
|
||||||
|
|
||||||
|
type RunResult struct {
|
||||||
|
ok bool
|
||||||
|
argv []string
|
||||||
|
path string
|
||||||
|
output []byte
|
||||||
|
err error
|
||||||
|
outerr error
|
||||||
|
}
|
||||||
|
|
||||||
|
// run, but set the working path
|
||||||
|
func RunPath(path string, args []string) *RunResult {
|
||||||
|
r := new(RunResult)
|
||||||
|
r.path = path
|
||||||
|
r.argv = args
|
||||||
|
if len(args) == 0 {
|
||||||
|
r.ok = true
|
||||||
|
r.err = ErrorArgvEmpty
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
if args[0] == "" {
|
||||||
|
r.ok = false
|
||||||
|
r.err = ErrorArgvEmpty
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
thing := args[0]
|
||||||
|
parts := args[1:]
|
||||||
|
cmd := exec.Command(thing, parts...)
|
||||||
|
cmd.Dir = path
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
log.Info("path =", path, "cmd =", strings.Join(args, " "))
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
// Handle error if the command execution fails
|
||||||
|
// log.Info("RunPath() failed")
|
||||||
|
// log.Info("cmd.Enviorn =", cmd.Environ())
|
||||||
|
out, outerr := cmd.Output()
|
||||||
|
// log.Info("cmd.output =", out)
|
||||||
|
// log.Info("cmd.output err=", outerr)
|
||||||
|
// log.Info("path =", path)
|
||||||
|
// log.Info("args =", args)
|
||||||
|
// log.Info("err =", err.Error())
|
||||||
|
r.ok = false
|
||||||
|
r.err = err
|
||||||
|
r.output = out
|
||||||
|
r.outerr = outerr
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
out, outerr := cmd.Output()
|
||||||
|
r.output = out
|
||||||
|
r.outerr = outerr
|
||||||
|
r.ok = true
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the path and the command
|
||||||
|
func RunCmd(workingpath string, parts []string) (error, bool, string) {
|
||||||
|
if len(parts) == 0 {
|
||||||
|
log.Warn("command line was empty")
|
||||||
|
return errors.New("empty"), false, ""
|
||||||
|
}
|
||||||
|
if parts[0] == "" {
|
||||||
|
log.Warn("command line was empty")
|
||||||
|
return errors.New("empty"), false, ""
|
||||||
|
}
|
||||||
|
thing := parts[0]
|
||||||
|
parts = parts[1:]
|
||||||
|
log.Log(INFO, "working path =", workingpath, "thing =", thing, "cmdline =", parts)
|
||||||
|
|
||||||
|
// Create the command
|
||||||
|
cmd := exec.Command(thing, parts...)
|
||||||
|
|
||||||
|
// Set the working directory
|
||||||
|
cmd.Dir = workingpath
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
if thing == "git" {
|
||||||
|
log.Log(INFO, "git ERROR. maybe okay", workingpath, "thing =", thing, "cmdline =", parts)
|
||||||
|
log.Log(INFO, "git ERROR. maybe okay err =", err)
|
||||||
|
if err.Error() == "exit status 1" {
|
||||||
|
log.Log(INFO, "git ERROR. normal exit status 1")
|
||||||
|
if parts[0] == "diff-index" {
|
||||||
|
log.Log(INFO, "git normal diff-index when repo dirty")
|
||||||
|
return nil, false, "git diff-index exit status 1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warn("ERROR working path =", workingpath, "thing =", thing, "cmdline =", parts)
|
||||||
|
log.Warn("ERROR working path =", workingpath, "thing =", thing, "cmdline =", parts)
|
||||||
|
log.Warn("ERROR working path =", workingpath, "thing =", thing, "cmdline =", parts)
|
||||||
|
log.Error(err)
|
||||||
|
log.Warn("output was", string(output))
|
||||||
|
log.Warn("cmd exited with error", err)
|
||||||
|
// panic("fucknuts")
|
||||||
|
|
||||||
|
// The command failed (non-zero exit status)
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
// Assert that it is an exec.ExitError and get the exit code
|
||||||
|
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||||
|
log.Warn("Exit Status: %d\n", status.ExitStatus())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Warn("cmd.Run() failed with %s\n", err)
|
||||||
|
}
|
||||||
|
return err, false, string(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := string(output)
|
||||||
|
tmp = strings.TrimSpace(tmp)
|
||||||
|
|
||||||
|
// Print the output
|
||||||
|
return nil, true, tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the path and the command
|
||||||
|
func RunCmdRun(workingpath string, parts []string) error {
|
||||||
|
if len(parts) == 0 {
|
||||||
|
log.Warn("command line was empty")
|
||||||
|
return errors.New("empty")
|
||||||
|
}
|
||||||
|
if parts[0] == "" {
|
||||||
|
log.Warn("command line was empty")
|
||||||
|
return errors.New("empty")
|
||||||
|
}
|
||||||
|
thing := parts[0]
|
||||||
|
parts = parts[1:]
|
||||||
|
log.Log(INFO, "working path =", workingpath, "thing =", thing, "cmdline =", parts)
|
||||||
|
|
||||||
|
// Create the command
|
||||||
|
cmd := exec.Command(thing, parts...)
|
||||||
|
|
||||||
|
// Set the working directory
|
||||||
|
cmd.Dir = workingpath
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("ERROR working path =", workingpath, "thing =", thing, "cmdline =", parts)
|
||||||
|
log.Error(err)
|
||||||
|
log.Warn("cmd exited with error", err)
|
||||||
|
// panic("fucknuts")
|
||||||
|
|
||||||
|
// The command failed (non-zero exit status)
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
// Assert that it is an exec.ExitError and get the exit code
|
||||||
|
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||||
|
log.Warn("Exit Status: %d\n", status.ExitStatus())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Warn("cmd.Run() failed with %s\n", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
package shell
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: look at https://github.com/go-cmd/cmd/issues/20
|
||||||
|
// use go-cmd instead here?
|
||||||
|
|
||||||
|
var callback func(interface{}, int)
|
||||||
|
|
||||||
|
var shellStdout *os.File
|
||||||
|
var shellStderr *os.File
|
||||||
|
|
||||||
|
var spewOn bool = false
|
||||||
|
var quiet bool = false
|
||||||
|
|
||||||
|
// var msecDelay int = 20 // number of milliseconds to delay between reads with no data
|
||||||
|
|
||||||
|
// var bytesBuffer bytes.Buffer
|
||||||
|
// var bytesSplice []byte
|
||||||
|
|
||||||
|
func handleError(c interface{}, ret int) {
|
||||||
|
log.Log(INFO, "shell.Run() Returned", ret)
|
||||||
|
if callback != nil {
|
||||||
|
callback(c, ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
callback = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitCallback(f func(interface{}, int)) {
|
||||||
|
callback = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// this means it won't copy all the output to STDOUT
|
||||||
|
func Quiet(q bool) {
|
||||||
|
quiet = q
|
||||||
|
}
|
||||||
|
|
||||||
|
func Script(cmds string) int {
|
||||||
|
// split on new lines (while we are at it, handle stupid windows text files
|
||||||
|
lines := strings.Split(strings.Replace(cmds, "\r\n", "\n", -1), "\n")
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
line = Chomp(line) // this is like 'chomp' in perl
|
||||||
|
log.Log(INFO, "LINE:", line)
|
||||||
|
time.Sleep(1)
|
||||||
|
RunString(line)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func SpewOn() {
|
||||||
|
spewOn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetDelayInMsec(msecs int) {
|
||||||
|
msecDelay = msecs
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetStdout(newout *os.File) {
|
||||||
|
shellStdout = newout
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetStderr(newerr *os.File) {
|
||||||
|
shellStderr = newerr
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unlink(filename string) {
|
||||||
|
os.Remove(Path(filename))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RM(filename string) {
|
||||||
|
os.Remove(Path(filename))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Daemon(cmdline string, timeout time.Duration) int {
|
||||||
|
for {
|
||||||
|
RunString(cmdline)
|
||||||
|
time.Sleep(timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// run something and never return from it
|
||||||
|
// TODO: pass STDOUT, STDERR, STDIN correctly
|
||||||
|
// TODO: figure out how to nohup the process and exit
|
||||||
|
func Exec(cmdline string) {
|
||||||
|
log.Log(INFO, "shell.Run() START "+cmdline)
|
||||||
|
|
||||||
|
cmd := Chomp(cmdline) // this is like 'chomp' in perl
|
||||||
|
cmdArgs := strings.Fields(cmd)
|
||||||
|
|
||||||
|
process := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
|
||||||
|
process.Stderr = os.Stderr
|
||||||
|
process.Stdin = os.Stdin
|
||||||
|
process.Stdout = os.Stdout
|
||||||
|
process.Start()
|
||||||
|
err := process.Wait()
|
||||||
|
log.Log(INFO, "shell.Exec() err =", err)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRun(workingpath string, cmd []string) error {
|
||||||
|
log.Log(INFO, "NewRun() ", cmd)
|
||||||
|
|
||||||
|
process := exec.Command(cmd[0], cmd[1:len(cmd)]...)
|
||||||
|
// Set the working directory
|
||||||
|
process.Dir = workingpath
|
||||||
|
process.Stderr = os.Stderr
|
||||||
|
process.Stdin = os.Stdin
|
||||||
|
process.Stdout = os.Stdout
|
||||||
|
process.Start()
|
||||||
|
err := process.Wait()
|
||||||
|
log.Log(INFO, "shell.Exec() err =", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// return true if the filename exists (cross-platform)
|
||||||
|
func Exists(filename string) bool {
|
||||||
|
_, err := os.Stat(Path(filename))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// makes the directory
|
||||||
|
func Mkdir(dir string) bool {
|
||||||
|
if Dir(dir) {
|
||||||
|
// already a dir
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if Exists(dir) {
|
||||||
|
// something else is there
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Run([]string{"mkdir", "-p", dir})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsDir(dirname string) bool {
|
||||||
|
return Dir(dirname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return true if the filename exists (cross-platform)
|
||||||
|
func Dir(dirname string) bool {
|
||||||
|
info, err := os.Stat(Path(dirname))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cat a file into a string
|
||||||
|
func Cat(filename string) string {
|
||||||
|
buffer, err := ioutil.ReadFile(Path(filename))
|
||||||
|
// log.Log(INFO, "buffer =", string(buffer))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return Chomp(buffer)
|
||||||
|
}
|
|
@ -1,17 +0,0 @@
|
||||||
package shell
|
|
||||||
|
|
||||||
import "regexp"
|
|
||||||
|
|
||||||
// splits strings. should work all the time
|
|
||||||
// A string with mixed line endings, including old Mac style (\r)
|
|
||||||
func SplitNewLines(input string) []string {
|
|
||||||
// This regex matches a carriage return and optional newline, OR just a newline.
|
|
||||||
// This covers \r\n, \n, and \r.
|
|
||||||
re := regexp.MustCompile(`\r\n?|\n|\r`)
|
|
||||||
|
|
||||||
// The -1 means there is no limit to the number of splits.
|
|
||||||
lines := re.Split(input, -1)
|
|
||||||
|
|
||||||
// Output: ["line one" "line two" "line three" "line four"]
|
|
||||||
return lines
|
|
||||||
}
|
|
|
@ -1,6 +1,12 @@
|
||||||
package shell
|
package shell
|
||||||
|
|
||||||
/*
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
var FileMap map[string]*File
|
var FileMap map[string]*File
|
||||||
|
|
||||||
var readBufferSize int
|
var readBufferSize int
|
||||||
|
@ -48,4 +54,3 @@ func New() *OldShell {
|
||||||
|
|
||||||
return &tmp
|
return &tmp
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
120
time.go
120
time.go
|
@ -1,120 +0,0 @@
|
||||||
package shell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// converts a git for-each-ref date. "Wed Feb 7 10:13:38 2024 -0600"
|
|
||||||
func getGitDateStamp(gitdefault string) (time.Time, string, string) {
|
|
||||||
// now := time.Now().Format("Wed Feb 7 10:13:38 2024 -0600")
|
|
||||||
const gitLayout = "Mon Jan 2 15:04:05 2006 -0700"
|
|
||||||
tagTime, err := time.Parse(gitLayout, gitdefault)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("GOT THIS IN PARSE AAA." + gitdefault + ".AAA")
|
|
||||||
log.Warn(err)
|
|
||||||
return time.Now(), "Feb 1 12:34:56 1978 -0600", ""
|
|
||||||
}
|
|
||||||
return tagTime, gitdefault, GetDurationStamp(tagTime)
|
|
||||||
}
|
|
||||||
func getRawDateStamp(raw string) (time.Time, string, string) {
|
|
||||||
parts := strings.Split(raw, " ")
|
|
||||||
if len(parts) == 0 {
|
|
||||||
// raw was blank here
|
|
||||||
// return "Jan 4 1977", "40y" // eh, why not. it'll be easy to grep for this
|
|
||||||
return time.Now(), "Jan 4 1977", "40y" // eh, why not. it'll be easy to grep for this
|
|
||||||
}
|
|
||||||
i, err := strconv.ParseInt(parts[0], 10, 64) // base 10 string, return int64
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Error converting timestamp:", raw)
|
|
||||||
log.Warn("Error converting timestamp err =", err)
|
|
||||||
return time.Now(), "", ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the Unix timestamp into a time.Time object
|
|
||||||
gitTagDate := time.Unix(i, 0)
|
|
||||||
return gitTagDate, gitTagDate.UTC().Format("2006/01/02 15:04:05 UTC"), GetDurationStamp(gitTagDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDurationStamp(t time.Time) string {
|
|
||||||
|
|
||||||
// Get the current time
|
|
||||||
currentTime := time.Now()
|
|
||||||
|
|
||||||
// Calculate the duration between t current time
|
|
||||||
duration := currentTime.Sub(t)
|
|
||||||
|
|
||||||
return FormatDuration(duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
// allows nil
|
|
||||||
func HumanDuration(d *time.Duration) string {
|
|
||||||
if d == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return FormatDuration(*d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func FormatDuration(d time.Duration) string {
|
|
||||||
result := ""
|
|
||||||
|
|
||||||
// check if it's more than a year
|
|
||||||
years := int(d.Hours()) / (24 * 365)
|
|
||||||
if years > 0 {
|
|
||||||
result += fmt.Sprintf("%dy ", years)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if it's more than a day
|
|
||||||
days := int(d.Hours()) / 24
|
|
||||||
if days > 0 {
|
|
||||||
result += fmt.Sprintf("%dd ", days)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if it's more than an hour
|
|
||||||
hours := int(d.Hours()) % 24
|
|
||||||
if hours > 0 {
|
|
||||||
result += fmt.Sprintf("%dh ", hours)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if it's more than a minute
|
|
||||||
minutes := int(d.Minutes()) % 60
|
|
||||||
if minutes > 0 {
|
|
||||||
result += fmt.Sprintf("%dm ", minutes)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if it's more than a second
|
|
||||||
seconds := int(d.Seconds()) % 60
|
|
||||||
if seconds > 0 {
|
|
||||||
result += fmt.Sprintf("%ds", seconds)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// report in milliseconds
|
|
||||||
ms := int(d.Milliseconds())
|
|
||||||
if ms > 100 {
|
|
||||||
// todo: print .3s, etc ?
|
|
||||||
}
|
|
||||||
if ms > 0 {
|
|
||||||
result += fmt.Sprintf("%dms", ms)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// report in milliseconds
|
|
||||||
mc := int(d.Microseconds())
|
|
||||||
if mc > 0 {
|
|
||||||
result += fmt.Sprintf("%dmc", mc)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
ns := int(d.Nanoseconds())
|
|
||||||
result += fmt.Sprintf("%dns", ns)
|
|
||||||
return result
|
|
||||||
}
|
|
5
wget.go
5
wget.go
|
@ -34,7 +34,7 @@ func Wget(url string) *bytes.Buffer {
|
||||||
|
|
||||||
log.Log(INFO, "res.StatusCode: %d\n", resp.StatusCode)
|
log.Log(INFO, "res.StatusCode: %d\n", resp.StatusCode)
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
handleError(fmt.Errorf("%d", resp.StatusCode), -1)
|
handleError(fmt.Errorf(fmt.Sprint("%d", resp.StatusCode)), -1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,8 @@ func WgetToFile(filepath string, url string) error {
|
||||||
// BUGS: The author's idea of friendly may differ to that of many other people.
|
// BUGS: The author's idea of friendly may differ to that of many other people.
|
||||||
func Write(filepath string, data string) bool {
|
func Write(filepath string, data string) bool {
|
||||||
// TODO: this isn't working for some reason and is making two '\n' chars
|
// TODO: this isn't working for some reason and is making two '\n' chars
|
||||||
data = strings.TrimSpace(data) + "\n"
|
// probably because Chomp() isn't fixed yet
|
||||||
|
data = Chomp(data) + "\n"
|
||||||
// Create the file
|
// Create the file
|
||||||
ospath := Path(filepath)
|
ospath := Path(filepath)
|
||||||
log.Log(INFO, "shell.Write() START ospath =", ospath, "filepath =", filepath)
|
log.Log(INFO, "shell.Write() START ospath =", ospath, "filepath =", filepath)
|
||||||
|
|
30
xterm.go
30
xterm.go
|
@ -3,25 +3,11 @@ package shell
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// not sure why I wrote this or what it is for
|
|
||||||
// this is because I'm crazy. how crazy you ask? fucking crazy!
|
|
||||||
// hehe. If you haven't ever written code that you look back
|
|
||||||
// at like this and go 'what the fuck is this shit', then
|
|
||||||
// realize that you wrote it, then go 'what the fuck is this shit'
|
|
||||||
// and then still look at it and wonder, "how early was this. how much did I not know when I wrote this"
|
|
||||||
// then, if you haven't done those kinds of things, then don't
|
|
||||||
// ever fucking come up to me and tell me that I'm nuts
|
|
||||||
// because, you are not as good as me then. It's very complicated
|
|
||||||
// to work on very complicated things. I don't care how smart you are,
|
|
||||||
// you can totally forget about shit you wrote and then have to come back to it later
|
|
||||||
|
|
||||||
// also, what the fuck was I thinking with the function 'scanToParent()' as a function that takes
|
|
||||||
// a pid arg? what? is this like pstree? I'm not sure what I wanted this for
|
|
||||||
// but it sounds interesting.
|
|
||||||
func scanToParent(pid int) (bool, string) {
|
func scanToParent(pid int) (bool, string) {
|
||||||
ppid, err := GetPPID(pid)
|
ppid, err := GetPPID(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -49,6 +35,8 @@ func scanToParent(pid int) (bool, string) {
|
||||||
case "make":
|
case "make":
|
||||||
// keep digging for the parent xterm
|
// keep digging for the parent xterm
|
||||||
return scanToParent(ppid)
|
return scanToParent(ppid)
|
||||||
|
default:
|
||||||
|
return false, comm
|
||||||
}
|
}
|
||||||
if comm == "bash" {
|
if comm == "bash" {
|
||||||
}
|
}
|
||||||
|
@ -93,6 +81,8 @@ func Shell() string {
|
||||||
switch envsh {
|
switch envsh {
|
||||||
case "/bin/bash":
|
case "/bin/bash":
|
||||||
return "bash"
|
return "bash"
|
||||||
|
default:
|
||||||
|
return envsh
|
||||||
}
|
}
|
||||||
return envsh
|
return envsh
|
||||||
}
|
}
|
||||||
|
@ -126,17 +116,16 @@ func XtermCmd(path string, cmd []string) {
|
||||||
// runs an xterm
|
// runs an xterm
|
||||||
// waits until xterm exits
|
// waits until xterm exits
|
||||||
func XtermCmdWait(path string, cmd []string) {
|
func XtermCmdWait(path string, cmd []string) {
|
||||||
// var argsXterm = getXtermCmd(cmd)
|
var argsXterm = getXtermCmd(cmd)
|
||||||
|
|
||||||
log.Info("XtermCmd() path =", path, "cmd =", cmd)
|
log.Info("XtermCmd() path =", path, "cmd =", argsXterm)
|
||||||
|
|
||||||
// keeps git diff from exiting on small diffs
|
// keeps git diff from exiting on small diffs
|
||||||
os.Setenv("LESS", "-+F -+X -R")
|
os.Setenv("LESS", "-+F -+X -R")
|
||||||
|
|
||||||
PathRunLog(path, cmd, INFO)
|
RunCmdRun(path, argsXterm)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// spawns an xterm with something you can run at a command line
|
// spawns an xterm with something you can run at a command line
|
||||||
// then executes bash
|
// then executes bash
|
||||||
func XtermCmdBash(path string, cmd []string) {
|
func XtermCmdBash(path string, cmd []string) {
|
||||||
|
@ -147,6 +136,5 @@ func XtermCmdBash(path string, cmd []string) {
|
||||||
bash += "'; bash\""
|
bash += "'; bash\""
|
||||||
tmp = append(argsXterm, "bash", bash)
|
tmp = append(argsXterm, "bash", bash)
|
||||||
log.Info("XtermCmd() path =", path, "cmd =", tmp)
|
log.Info("XtermCmd() path =", path, "cmd =", tmp)
|
||||||
go PathRunLog(path, tmp, INFO)
|
go RunCmd(path, tmp)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
Loading…
Reference in New Issue