Skip to content

Commit

Permalink
Merge pull request #32 from vladimirvivien/repackage
Browse files Browse the repository at this point in the history
Complete refactor of codebase and additional functionalities.
  • Loading branch information
vladimirvivien authored May 2, 2021
2 parents 8320e28 + bf8e994 commit 6149013
Show file tree
Hide file tree
Showing 48 changed files with 2,350 additions and 1,590 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ jobs:
runs-on: ubuntu-latest
steps:

- name: Set up Go 1.14
- name: Set up Go 1.16
uses: actions/setup-go@v1
with:
go-version: 1.14
go-version: 1.16.3
id: go

- name: Code checkout
Expand Down
112 changes: 69 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,85 @@
# `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 and IO
* Support for different programming styles
* Programs can be used as pre-compiled binaries or with `go run`


## Using `echo`
Create a session:
```
e := echo.New()
[![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!

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, optionally configure your session:

### Run a process
The following executes command `echo "Hello World!"` and prints the result:
```go=
fmt.Println(gexe.Run(`echo "Hello World!"`))
```
e.Conf.SetPanicOnError(true)

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() {
e := echo.New()
for _, arch := range []string{"amd64"} {
for _, opsys := range []string{"darwin", "linux"} {
e.SetVar("arch", arch).SetVar("os", opsys)
e.SetVar("binpath", fmt.Sprintf("build/%s/%s/mybinary", arch, opsys))
result := e.Env("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, e.Eval("$binpath"))
fmt.Printf("Build %s/%s: %s OK\n", arch, opsys, echo.Eval("$binpath"))
}
}
}
```
> 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...")

e := echo.New()
p := e.StartProc("ping golang.org")
p := gexe.StartProc("ping golang.org")

if p.Err() != nil {
fmt.Println("ping failed:", p.Err())
Expand All @@ -71,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 e.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
10 changes: 5 additions & 5 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@
* [x] Doc update

## v0.0.1-alpha.5
* [ ] Namespace all methods
* [ ] Add package level shortcut funcs for popular methods
* [x] Namespace all methods
* [x] Add package level shortcut funcs for popular methods

## v0.0.1-alpha.6
* [ ] Default shell: sets a default shell for method Run (`e.Conf.Shell()`).
* [ ] Enhanced text parsing with parameter expansion escape
* [ ] ~Default shell: sets a default shell for method Run (`e.Conf.Shell()`)~.
* [x] Enhanced text parsing with parameter expansion escape


## Upcoming
## ~Upcoming~
- Ability to pipe result from previous cmd i.e.:
e.Pipe(e.Run(cmd), e.Run(cmd), e.Run(cmd))
- Item iterator:
Expand Down
94 changes: 0 additions & 94 deletions cmd.go

This file was deleted.

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
45 changes: 16 additions & 29 deletions echo.go
Original file line number Diff line number Diff line change
@@ -1,45 +1,32 @@
package echo
package gexe

import (
"fmt"
"regexp"

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

var (
DefaultEcho = New()
spaceRgx = regexp.MustCompile("\\s")
lineRgx = regexp.MustCompile("\\n")
)

// Echo represents a new Echo session
type Echo struct {
vars map[string]string // session var
Conf *Config // session config
Procs []Proc // executed processes
Prog *prog // progam info
err error
vars *vars.Variables // session vars
prog *prog.ProgInfo
Conf *Config // session config
}

var (
spaceRgx = regexp.MustCompile("\\s")
lineRgx = regexp.MustCompile("\\n")
)

// New creates a new Echo session
func New() *Echo {
e := &Echo{
vars: make(map[string]string),
vars: vars.New(),
prog: prog.Prog(),
Conf: &Config{escapeChar: '\\'},
Prog: new(prog),
}
return e
}

func (e *Echo) shouldPanic(msg string) {
if e.Conf.IsPanicOnErr() {
panic(msg)
}
}

func (e *Echo) shouldLog(msg string) {
if e.Conf.IsVerbose() {
fmt.Println(msg)
}
}

func (e *Echo) String() string {
return fmt.Sprintf("Vars[%#v]", e.vars)
}
4 changes: 4 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
# `echo` Examples
* [build](./build/main.go) - Show how to build Go binaries for multiple platforms and OSes.
* [git](./git/main.go) - Uses git to print logs and commit info
* [helloecho](./helloecho/main.go) - Simple example Echo
* [ping](./ping/main.go) - Stream output of long-running processes
13 changes: 6 additions & 7 deletions examples/build/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +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() {
e := echo.New()
for _, arch := range []string{"amd64"} {
for _, opsys := range []string{"darwin", "linux"} {
e.SetVar("arch", arch).SetVar("os", opsys)
e.SetVar("binpath", fmt.Sprintf("build/%s/%s/mybinary", arch, opsys))
result := e.Env("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, e.Eval("$binpath"))
fmt.Printf("Build %s/%s: %s OK\n", arch, opsys, gexe.Eval("$binpath"))
}
}
}
Loading

0 comments on commit 6149013

Please sign in to comment.