Skip to content

Go package exposing a simple interface for executing commands, enabling easy mocking and wrapping of executed commands.

License

Notifications You must be signed in to change notification settings

krystal/go-runner

Repository files navigation

go-runner

Go package exposing a simple interface for executing commands, enabling easy mocking and wrapping of executed commands.

Go Reference GitHub tag (latest SemVer) Actions Status Coverage GitHub last commit GitHub issues GitHub pull requests License Status

The Runner interface is basic and minimal, but it is sufficient for most use cases. This makes it easy to mock Runner for testing purposes.

It's also easy to create wrapper runners which modify commands before executing them. The Sudo struct is a simple example of this.

Import

import "github.com/krystal/go-runner"

Interface

type Runner interface {
	Run(
		stdin io.Reader,
		stdout, stderr io.Writer,
		command string,
		args ...string,
	) error
	RunContext(
		ctx context.Context,
		stdin io.Reader,
		stdout, stderr io.Writer,
		command string,
		args ...string,
	) error
	Env(env ...string)
}

Usage

Basic:

var stdout bytes.Buffer

r := runner.New()
_ = r.Run(nil, &stdout, nil, "echo", "Hello world!")

fmt.Print(stdout.String())
Hello world!

Environment:

var stdout bytes.Buffer

r := runner.New()
r.Env("USER=johndoe", "HOME=/home/johnny")
_ = r.Run(nil, &stdout, nil, "sh", "-c", `echo "Hi, ${USER} (${HOME})"`)

fmt.Print(stdout.String())
Hi, johndoe (/home/johnny)

Stdin, Stdout, and Stderr:

stdin := bytes.NewBufferString("Hello world!")
var stdout, stderr bytes.Buffer

r := runner.New()
err := r.Run(
	stdin, &stdout, &stderr,
	"sh", "-c", "cat; echo 'Oh noes! :(' >&2",
)
if err != nil {
	fmt.Println(err)
}

fmt.Print(stderr.String())
fmt.Print(stdout.String())
Oh noes! :(
Hello world!

Failure:

var stdout, stderr bytes.Buffer

r := runner.New()
err := r.Run(
	nil, &stdout, &stderr,
	"sh", "-c", "echo 'Hello world!'; echo 'Oh noes! :(' >&2; exit 3",
)
if err != nil {
	fmt.Printf("%s: %s", err.Error(), stderr.String())
}
exit status 3: Oh noes! :(

Context:

var stdout bytes.Buffer

ctx, cancel := context.WithTimeout(
	context.Background(), 1*time.Second,
)
defer cancel()

r := runner.New()
err := r.RunContext(
	ctx, nil, &stdout, nil,
	"sh", "-c", "sleep 0.5 && echo 'Hello world!'",
)
if err != nil {
	fmt.Println(err)
}

fmt.Print(stdout.String())
Hello world!

Sudo (requires NOPASS in sudoers file):

var stdout bytes.Buffer
r := runner.New()

sudo := &runner.Sudo{Runner: r}
_ = sudo.Run(nil, &stdout, nil, "whoami")

sudo.User = "web"
_ = sudo.Run(nil, &stdout, nil, "whoami")

fmt.Print(stdout.String())
root
web

Documentation

Please see the Go Reference for documentation and examples.

License

MIT