Skip to content

Commit

Permalink
Doc update; project rename
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimirvivien committed May 2, 2021
1 parent dc21c26 commit bf8e994
Show file tree
Hide file tree
Showing 23 changed files with 125 additions and 102 deletions.
111 changes: 67 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -48,17 +69,17 @@ 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
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())
Expand All @@ -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
2 changes: 1 addition & 1 deletion config.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package echo
package gexe

// Config stores configuration
type Config struct {
Expand Down
6 changes: 3 additions & 3 deletions echo.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down
12 changes: 6 additions & 6 deletions examples/build/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
}
}
}
14 changes: 7 additions & 7 deletions examples/filesys/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@ import (
"fmt"
"os"

"github.com/vladimirvivien/echo"
"github.com/vladimirvivien/echo/str"
"github.com/vladimirvivien/gexe"
"github.com/vladimirvivien/gexe/str"
)


// This example uses local git to create a file with commit logs.
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)
Expand Down
8 changes: 4 additions & 4 deletions examples/git/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import (
"fmt"
"strings"

"github.com/vladimirvivien/echo"
"github.com/vladimirvivien/gexe"
)

// This example uses local git to print logs and commit info.
// Notice the use of /bin/sh to start a shell for more complex
// 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))
}
}
4 changes: 2 additions & 2 deletions examples/helloecho/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"os"

"github.com/vladimirvivien/echo"
"github.com/vladimirvivien/gexe"
)

var (
Expand All @@ -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") == "" {
Expand Down
6 changes: 3 additions & 3 deletions examples/ping/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ 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.
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())
Expand Down
4 changes: 2 additions & 2 deletions exec/cmd_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`},
},
}

Expand Down
2 changes: 1 addition & 1 deletion exec/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
2 changes: 1 addition & 1 deletion exec/proc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"strings"
"testing"

"github.com/vladimirvivien/echo/vars"
"github.com/vladimirvivien/gexe/vars"
)

func TestProc(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions filesys.go
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion filesys_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package echo
package gexe

import (
"os"
Expand Down
10 changes: 5 additions & 5 deletions fs/file_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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))
}
Expand All @@ -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 {
Expand All @@ -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)
Expand Down
Loading

0 comments on commit bf8e994

Please sign in to comment.