From bf8e994f5d2eef30e3fcf3f026be88d472a070d7 Mon Sep 17 00:00:00 2001 From: Vladimir Vivien Date: Sun, 2 May 2021 14:30:03 -0400 Subject: [PATCH] Doc update; project rename --- README.md | 111 ++++++++++++++++++++++--------------- config.go | 2 +- echo.go | 6 +- examples/build/main.go | 12 ++-- examples/filesys/main.go | 14 ++--- examples/git/main.go | 8 +-- examples/helloecho/main.go | 4 +- examples/ping/main.go | 6 +- exec/cmd_parser_test.go | 4 +- exec/proc.go | 2 +- exec/proc_test.go | 2 +- filesys.go | 4 +- filesys_test.go | 2 +- fs/file_reader_test.go | 10 ++-- fs/file_writer_test.go | 8 +-- functions.go | 10 ++-- go.mod | 4 +- procs.go | 4 +- procs_test.go | 2 +- prog.go | 4 +- variables.go | 4 +- variables_test.go | 2 +- vars/variables.go | 2 +- 23 files changed, 125 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index d4e00c8..785b789 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,63 @@ -# `echo` -OS interaction wrapped in the security and type safety of the Go programming language! - -The goal of `echo` is to make it easy to write code that interacts with the OS (or other infrastructure components) -using the security and type safety of the Go programming language. - -## What `echo` is -* Not a tool for shell-scripting Go code -* Designed to be used as idiomatic Go -* Rich types for easy interactions with OS (exec, IO, etc) -* Programs can be used as pre-compiled binaries or with `go run` - - -## Using `echo` -The `echo` package comes with several functions ready to be used. For instance -to execute an external command, you can use the following:" -``` -echo.Run(`echo "Hello World!"`) -``` +[![Go Reference](https://pkg.go.dev/badge/github.com/vladimirvivien/gexe.svg)](https://pkg.go.dev/github.com/vladimirvivien/gexe) +[![Go Report Card](https://goreportcard.com/badge/github.com/vladimirvivien/echo)](https://goreportcard.com/report/github.com/vladimirvivien/echo) +![Build](https://github.com/vladimirvivien/gexe/actions/workflows/build.yml/badge.svg) +# Project `gexe` +Script-like OS interaction wrapped in the security and type safety of the Go programming language! -Alternatively, you can create your own echo session with: -``` -e := echo.New() +The goal of project `gexe` is to make it dead simple to write code that interacts with the OS (and/or other components) with the type safety of the Go programming language. + +## What can you do with `gexe`? +* Prse and execute OS comands provided as plain and clear text as you would in a shell. +* Support for variable exapansion in command string (i.e. `gexe.Run("echo $HOME")`) +* Get process information (i.e. PID, status, exit code, etc) +* Stream data from stdout while process is executing +* Get program information (i.e. args, binary name, working dir, etc) +* Easily read file content into different targets (string, bytes, io.Writer, etc) +* Easily write file content from different sources (i.e. string, bytes, io.Reader, etc) +* Integrate with your shell script using `go run` + + +## Using `gexe` + +### Get the package +```bash= +go get github.com/vladimirvivien/gexe ``` -Then run the echo methods available: + +### Run a process +The following executes command `echo "Hello World!"` and prints the result: +```go= +fmt.Println(gexe.Run(`echo "Hello World!"`)) ``` -e.Run(...) + +Alternatively, you can create your own `gexe` session for more control and error hanlding: + +```go= +g := gexe.New() +proc := g.RunProc(`echo "Hello World"`) +if proc.Err() != nil { + fmt.Println(proc.Err()) + os.Exit(proc.ExitCode()) +} +fmt.Println(proc.Result()) ``` -### Building Go with `echo` -This example shows how `echo` can be used to build Go project binaries for multiple -platforms and OSes. +## Examples +Find more examples [here](./examples/)! + +### Building project `$gexe` with `gexe` +This example shows how `gexe` can be used to build Go project binaries for multiple +platforms and OSes. Note the followings: +* The command string is naturally expressed as you would in a shell. +* The use of variable expansion in the commands. + ```go func main() { for _, arch := range []string{"amd64"} { for _, opsys := range []string{"darwin", "linux"} { - echo.SetVar("arch", arch).SetVar("os", opsys) - echo.SetVar("binpath", fmt.Sprintf("build/%s/%s/mybinary", arch, opsys)) - result := echo.Envs("CGO_ENABLED=0 GOOS=$os GOARCH=$arch").Run("go build -o $binpath .") + gexe.SetVar("arch", arch).SetVar("os", opsys) + gexe.SetVar("binpath", fmt.Sprintf("build/%s/%s/mybinary", arch, opsys)) + result := gexe.Envs("CGO_ENABLED=0 GOOS=$os GOARCH=$arch").Run("go build -o $binpath .") if result != "" { fmt.Printf("Build for %s/%s failed: %s\n", arch, opsys, result) os.Exit(1) @@ -48,9 +69,9 @@ func main() { ``` > See [./examples/build/main.go](./examples/build/main.go) -### Example of a long running process -The following shows how `echo` can be used to launch a long running process and stream -its output. The code invokese the `ping` command, streams its output, displays the result, +### Long-running process +This example shows how `gexe` can be used to launch a long-running process and stream +its output. The code invokes the `ping` command, streams its output, displays the result, and then kills the process after 5 seconds. ```go @@ -58,7 +79,7 @@ func main() { execTime := time.Second * 5 fmt.Println("ping golang.org...") - p := echo.StartProc("ping golang.org") + p := gexe.StartProc("ping golang.org") if p.Err() != nil { fmt.Println("ping failed:", p.Err()) @@ -74,24 +95,26 @@ func main() { <-time.After(execTime) p.Kill() - fmt.Printf("Pingged golang.org for %s\n", execTime) + fmt.Printf("Pinged golang.org for %s\n", execTime) } ``` -### Example using shell -This example uses git to print logs and commit info. -The code invokes git as a sub-command of `/bin/sh` to start a shell -for more complex functionalities (such as piping). +### Using a shell +This example uses the `git` command to print logs and commit info by using `/bin/sh` to start a shell for command piping: + ```go func main() { - e := echo.New() cmd := `/bin/sh -c "git log --reverse --abbrev-commit --pretty=oneline | cut -d ' ' -f1"` - for _, p := range strings.Split(e.Run(cmd), "\n") { - e.SetVar("patch", p) + for _, p := range strings.Split(gexe.Run(cmd), "\n") { + gexe.SetVar("patch", p) cmd := `/bin/sh -c "git show --abbrev-commit -s --pretty=format:'%h %s (%an) %n' ${patch}"` - fmt.Println(e.Run(cmd)) + fmt.Println(gexe.Run(cmd)) } } ``` -## License + +# Project Name +Originally this project was named `echo`. However, another Go project by that name has gotten really popular. +So this project was renamed `gexe` (pronounced Jesse). +# License MIT \ No newline at end of file diff --git a/config.go b/config.go index ac0a3a0..881dc76 100644 --- a/config.go +++ b/config.go @@ -1,4 +1,4 @@ -package echo +package gexe // Config stores configuration type Config struct { diff --git a/echo.go b/echo.go index a5fe0e4..ea7a36b 100644 --- a/echo.go +++ b/echo.go @@ -1,10 +1,10 @@ -package echo +package gexe import ( "regexp" - "github.com/vladimirvivien/echo/prog" - "github.com/vladimirvivien/echo/vars" + "github.com/vladimirvivien/gexe/prog" + "github.com/vladimirvivien/gexe/vars" ) var ( diff --git a/examples/build/main.go b/examples/build/main.go index 98b0f47..149e6fe 100644 --- a/examples/build/main.go +++ b/examples/build/main.go @@ -4,23 +4,23 @@ import ( "fmt" "os" - "github.com/vladimirvivien/echo" + "github.com/vladimirvivien/gexe" ) -// This example shows how echo can be used in a CI +// This example shows how gexe can be used in a CI // pipeline to build Go project binaries for multiple // platforms and OSes. func main() { for _, arch := range []string{"amd64"} { for _, opsys := range []string{"darwin", "linux"} { - echo.SetVar("arch", arch).SetVar("os", opsys) - echo.SetVar("binpath", fmt.Sprintf("build/%s/%s/mybinary", arch, opsys)) - result := echo.Envs("CGO_ENABLED=0 GOOS=$os GOARCH=$arch").Run("go build -o $binpath .") + gexe.SetVar("arch", arch).SetVar("os", opsys) + gexe.SetVar("binpath", fmt.Sprintf("build/%s/%s/mybinary", arch, opsys)) + result := gexe.Envs("CGO_ENABLED=0 GOOS=$os GOARCH=$arch").Run("go build -o $binpath .") if result != "" { fmt.Printf("Build for %s/%s failed: %s\n", arch, opsys, result) os.Exit(1) } - fmt.Printf("Build %s/%s: %s OK\n", arch, opsys, echo.Eval("$binpath")) + fmt.Printf("Build %s/%s: %s OK\n", arch, opsys, gexe.Eval("$binpath")) } } } diff --git a/examples/filesys/main.go b/examples/filesys/main.go index 1357d4a..9c8cf21 100644 --- a/examples/filesys/main.go +++ b/examples/filesys/main.go @@ -5,8 +5,8 @@ import ( "fmt" "os" - "github.com/vladimirvivien/echo" - "github.com/vladimirvivien/echo/str" + "github.com/vladimirvivien/gexe" + "github.com/vladimirvivien/gexe/str" ) @@ -14,21 +14,21 @@ import ( func main() { buf := new(bytes.Buffer) cmd := `/bin/sh -c "git log --reverse --abbrev-commit --pretty=oneline | cut -d ' ' -f1"` - for _, p := range str.SplitLines(echo.Run(cmd)) { - echo.SetVar("patch", p) + for _, p := range str.SplitLines(gexe.Run(cmd)) { + gexe.SetVar("patch", p) cmd := `/bin/sh -c "git show --abbrev-commit -s --pretty=format:'%h %s (%an) %n' ${patch}"` - buf.WriteString(fmt.Sprintf("%s\n",echo.Run(cmd))) + buf.WriteString(fmt.Sprintf("%s\n", gexe.Run(cmd))) } gitfile := "./gitlog.txt" - if w := echo.Write(gitfile).ReadFrom(buf); w.Err() != nil { + if w := gexe.Write(gitfile).ReadFrom(buf); w.Err() != nil { fmt.Println(w.Err()) os.Exit(1) } // read the file and print - fmt.Println(echo.Read(gitfile).String()) + fmt.Println(gexe.Read(gitfile).String()) if err := os.Remove(gitfile); err != nil { fmt.Println(err) diff --git a/examples/git/main.go b/examples/git/main.go index 005bdd6..760ef44 100644 --- a/examples/git/main.go +++ b/examples/git/main.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/vladimirvivien/echo" + "github.com/vladimirvivien/gexe" ) // This example uses local git to print logs and commit info. @@ -12,9 +12,9 @@ import ( // commands (such as piping). func main() { cmd := `/bin/sh -c "git log --reverse --abbrev-commit --pretty=oneline | cut -d ' ' -f1"` - for _, p := range strings.Split(echo.Run(cmd), "\n") { - echo.SetVar("patch", p) + for _, p := range strings.Split(gexe.Run(cmd), "\n") { + gexe.SetVar("patch", p) cmd := `/bin/sh -c "git show --abbrev-commit -s --pretty=format:'%h %s (%an) %n' ${patch}"` - fmt.Println(echo.Run(cmd)) + fmt.Println(gexe.Run(cmd)) } } diff --git a/examples/helloecho/main.go b/examples/helloecho/main.go index 161e7f9..6d9a312 100644 --- a/examples/helloecho/main.go +++ b/examples/helloecho/main.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/vladimirvivien/echo" + "github.com/vladimirvivien/gexe" ) var ( @@ -19,7 +19,7 @@ func init() { // local or environment variables used in expansion // at runtime. func main() { - e := echo.New() + e := gexe.New() e.Conf.SetPanicOnErr(false) e.Vars("MYUSERNAME=$USER") if e.Eval("$MYUSERNAME") == "" { diff --git a/examples/ping/main.go b/examples/ping/main.go index 5abbbe0..3eaae7e 100644 --- a/examples/ping/main.go +++ b/examples/ping/main.go @@ -6,10 +6,10 @@ import ( "os" "time" - "github.com/vladimirvivien/echo" + "github.com/vladimirvivien/gexe" ) -// This example shows how echo can be used to launch and stream +// This example shows how gexe can be used to launch and stream // the output of the process as it happens. The following code // starts a `ping` command, streams the output, displays the result, // then kill the process after 5 seconds. @@ -17,7 +17,7 @@ func main() { execTime := time.Second * 5 fmt.Println("ping golang.org...") - p := echo.StartProc("ping golang.org") + p := gexe.StartProc("ping golang.org") if p.Err() != nil { fmt.Println("ping failed:", p.Err()) diff --git a/exec/cmd_parser_test.go b/exec/cmd_parser_test.go index 98fe98a..e0562b6 100644 --- a/exec/cmd_parser_test.go +++ b/exec/cmd_parser_test.go @@ -60,8 +60,8 @@ func TestEchoSplitWords(t *testing.T) { }, { name: "actual exec command", - str: `/bin/bash -c 'echo "Hello World"'`, - words: []string{`/bin/bash`, `-c`, `echo "Hello World"`}, + str: `/bin/bash -c 'gexe "Hello World"'`, + words: []string{`/bin/bash`, `-c`, `gexe "Hello World"`}, }, } diff --git a/exec/proc.go b/exec/proc.go index e1933fc..b29459e 100644 --- a/exec/proc.go +++ b/exec/proc.go @@ -41,7 +41,7 @@ func StartProc(cmdStr string) *Proc { } if err := command.Start(); err != nil { - return &Proc{id: command.Process.Pid, cmd: command, state: command.ProcessState, err: err} + return &Proc{cmd: command, err: err} } return &Proc{ diff --git a/exec/proc_test.go b/exec/proc_test.go index c8f73be..269583f 100644 --- a/exec/proc_test.go +++ b/exec/proc_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/vladimirvivien/echo/vars" + "github.com/vladimirvivien/gexe/vars" ) func TestProc(t *testing.T) { diff --git a/filesys.go b/filesys.go index 01da5d3..a4516dc 100644 --- a/filesys.go +++ b/filesys.go @@ -1,7 +1,7 @@ -package echo +package gexe import ( - "github.com/vladimirvivien/echo/fs" + "github.com/vladimirvivien/gexe/fs" ) func (e *Echo) Read(path string) fs.FileReader { diff --git a/filesys_test.go b/filesys_test.go index e54925e..e6551bf 100644 --- a/filesys_test.go +++ b/filesys_test.go @@ -1,4 +1,4 @@ -package echo +package gexe import ( "os" diff --git a/fs/file_reader_test.go b/fs/file_reader_test.go index 950f2ba..b11b333 100644 --- a/fs/file_reader_test.go +++ b/fs/file_reader_test.go @@ -20,7 +20,7 @@ func TestFileReader(t *testing.T) { }{ { name: "read.string", - file: testFile{path:"/tmp/echo_test_read_string.txt", content:"Hello from echo"}, + file: testFile{path:"/tmp/echo_test_read_string.txt", content:"Hello from gexe"}, test: func(t *testing.T, file testFile) { fr := Read(file.path) if fr.Err() != nil { @@ -34,14 +34,14 @@ func TestFileReader(t *testing.T) { }, { name: "read.lines", - file: testFile{path:"/tmp/echo_test_read_lines.txt", content:"Hello from\necho\necho\necho"}, + file: testFile{path:"/tmp/echo_test_read_lines.txt", content:"Hello from\ngexe\ngexe\ngexe"}, test: func(t *testing.T, file testFile) { fr := Read(file.path) if fr.Err() != nil { t.Fatal(fr.Err()) } actual := fr.Lines() - expected := []string{"Hello from", "echo", "echo", "echo"} + expected := []string{"Hello from", "gexe", "gexe", "gexe"} if len(actual) != len(expected) { t.Errorf("Read().Lines(): unexpected length: want %d, got %d", len(expected), len(actual)) } @@ -55,7 +55,7 @@ func TestFileReader(t *testing.T) { }, { name: "read.bytes", - file: testFile{path:"/tmp/echo_test_read_bytes.txt", content:"Hello from echo"}, + file: testFile{path:"/tmp/echo_test_read_bytes.txt", content:"Hello from gexe"}, test: func(t *testing.T, file testFile) { fr := Read(file.path) if fr.Err() != nil { @@ -69,7 +69,7 @@ func TestFileReader(t *testing.T) { }, { name: "read.writeTo", - file: testFile{path:"/tmp/echo_test_read_writeTo.txt", content:"Hello from echo"}, + file: testFile{path:"/tmp/echo_test_read_writeTo.txt", content:"Hello from gexe"}, test: func(t *testing.T, file testFile) { buf := new(bytes.Buffer) fr := Read(file.path).WriteTo(buf) diff --git a/fs/file_writer_test.go b/fs/file_writer_test.go index 79d45aa..1365486 100644 --- a/fs/file_writer_test.go +++ b/fs/file_writer_test.go @@ -38,7 +38,7 @@ func TestFileWriter (t *testing.T) { name:"write.lines", write: func(t *testing.T) testFile { file := testFile{path:"/tmp/echo_test_write_lines.txt", content:""} - f := Write(file.path).Lines([]string{"Hello", "echo", "echo"}) + f := Write(file.path).Lines([]string{"Hello", "gexe", "gexe"}) if f.Err() != nil { t.Fatal(f.Err()) } @@ -46,7 +46,7 @@ func TestFileWriter (t *testing.T) { }, test: func(t *testing.T, file testFile){ actual := Read(file.path).Lines() - expected := []string{"Hello", "echo", "echo"} + expected := []string{"Hello", "gexe", "gexe"} if len(actual) != len(expected) { t.Fatalf("Write().Lines unexpected length: want %d, got %d", len(expected), len(actual)) } @@ -154,12 +154,12 @@ func TestFileAppender (t *testing.T) { if f.Err() != nil { t.Fatal(f.Err()) } - Append(file.path).Lines([]string{"Hello", "echo", "echo"}) + Append(file.path).Lines([]string{"Hello", "gexe", "gexe"}) return file }, test: func(t *testing.T, file testFile){ actual := Read(file.path).Lines() - expected := []string{"Alo","Hello", "echo", "echo"} + expected := []string{"Alo","Hello", "gexe", "gexe"} if len(actual) != len(expected) { t.Fatalf("Write().Lines unexpected length: want %d, got %d", len(expected), len(actual)) } diff --git a/functions.go b/functions.go index 8a4347c..313d3e5 100644 --- a/functions.go +++ b/functions.go @@ -1,10 +1,10 @@ -package echo +package gexe import ( - "github.com/vladimirvivien/echo/exec" - "github.com/vladimirvivien/echo/fs" - "github.com/vladimirvivien/echo/prog" - "github.com/vladimirvivien/echo/vars" + "github.com/vladimirvivien/gexe/exec" + "github.com/vladimirvivien/gexe/fs" + "github.com/vladimirvivien/gexe/prog" + "github.com/vladimirvivien/gexe/vars" ) func Variables() *vars.Variables { diff --git a/go.mod b/go.mod index ffe0f80..f51a307 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/vladimirvivien/echo +module github.com/vladimirvivien/gexe -go 1.13 +go 1.16 diff --git a/procs.go b/procs.go index 1d4577d..adbad95 100644 --- a/procs.go +++ b/procs.go @@ -1,9 +1,9 @@ -package echo +package gexe import ( "fmt" - "github.com/vladimirvivien/echo/exec" + "github.com/vladimirvivien/gexe/exec" ) // StartProc executes the command in cmdStr and returns immediately diff --git a/procs_test.go b/procs_test.go index 7cfc3b9..26e85f4 100644 --- a/procs_test.go +++ b/procs_test.go @@ -1,4 +1,4 @@ -package echo +package gexe import ( "bytes" diff --git a/prog.go b/prog.go index 7729414..5270ea7 100644 --- a/prog.go +++ b/prog.go @@ -1,7 +1,7 @@ -package echo +package gexe import ( - "github.com/vladimirvivien/echo/prog" + "github.com/vladimirvivien/gexe/prog" ) func (e *Echo) Prog() *prog.ProgInfo { diff --git a/variables.go b/variables.go index 8589f98..15022da 100644 --- a/variables.go +++ b/variables.go @@ -1,7 +1,7 @@ -package echo +package gexe import ( - "github.com/vladimirvivien/echo/vars" + "github.com/vladimirvivien/gexe/vars" ) func (e *Echo) Variables() *vars.Variables { return e.vars diff --git a/variables_test.go b/variables_test.go index c156c7e..0b34224 100644 --- a/variables_test.go +++ b/variables_test.go @@ -1,4 +1,4 @@ -package echo +package gexe import ( "testing" diff --git a/vars/variables.go b/vars/variables.go index 8f2590b..f7a0a4c 100644 --- a/vars/variables.go +++ b/vars/variables.go @@ -62,7 +62,7 @@ func (v *Variables) SetEnv(name, value string) *Variables { return v } -// Vars declares an internal variable used during current echo session. +// Vars declares an internal variable used during current gexe session. // It uses a multi-line, space-separated list of KEY=VAL format: // i.e. foo=bar fuzz=buzz func (v *Variables) Vars(val string) *Variables {