run

package module
v0.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 30, 2023 License: Apache-2.0 Imports: 19 Imported by: 19

README

🏃‍♂️ run

Go Reference Tests Coverage

A new way to execute commands in Go

Example usage

package main

import (
  "bytes"
  "context"
  "fmt"
  "io"
  "log"
  "os"

  "github.com/sourcegraph/run"
)

func main() {
  ctx := context.Background()

  // Easily stream all output back to standard out
  err := run.Cmd(ctx, "echo", "hello world").Run().Stream(os.Stdout)
  if err != nil {
    log.Fatal(err.Error())
  }

  // Or collect, map, and modify output, then collect string lines from it
  lines, err := run.Cmd(ctx, "ls").Run().
    Map(func(ctx context.Context, line []byte, dst io.Writer) (int, error) {
      if !bytes.HasSuffix(line, []byte(".go")) {
        return 0, nil
      }
      return dst.Write(bytes.TrimSuffix(line, []byte(".go")))
    }).
    Lines()
  if err != nil {
    log.Fatal(err.Error())
  }
  for i, l := range lines {
    fmt.Printf("line %d: %q\n", i, l)
  }

  // Errors include standard error by default, so we can just stream stdout.
  err = run.Cmd(ctx, "ls", "foobar").StdOut().Run().Stream(os.Stdout)
  if err != nil {
    println(err.Error()) // exit status 1: ls: foobar: No such file or directory
  }

  // Generate data from a file, replacing tabs with spaces for Markdown purposes
  var exampleData bytes.Buffer
  exampleData.Write([]byte(exampleStart + "\n\n```go\n"))
  if err = run.Cmd(ctx, "cat", "cmd/example/main.go").Run().
    Map(func(ctx context.Context, line []byte, dst io.Writer) (int, error) {
      return dst.Write(bytes.ReplaceAll(line, []byte("\t"), []byte("  ")))
    }).
    Stream(&exampleData); err != nil {
    log.Fatal(err)
  }
  exampleData.Write([]byte("```\n\n" + exampleEnd))

  // Render new README file
  var readmeData bytes.Buffer
  if err = run.Cmd(ctx, "cat", "README.md").Run().Stream(&readmeData); err != nil {
    log.Fatal(err)
  }
  replaced := exampleBlockRegexp.ReplaceAll(readmeData.Bytes(), exampleData.Bytes())

  // Pipe data to command
  err = run.Cmd(ctx, "cp /dev/stdin README.md").Input(bytes.NewReader(replaced)).Run().Wait()
  if err != nil {
    log.Fatal(err)
  }
}

Documentation

Index

Examples

Constants

This section is empty.

Variables

StrictBashOpts contains options that effectively enforce safe execution of bash commands.

Functions

func Arg added in v0.8.0

func Arg(v string) string

Arg quotes a value such that it gets treated as an argument by a command.

It is currently an alias for shell.Quote

func DefaultTraceAttributes added in v0.10.0

func DefaultTraceAttributes(e ExecutedCommand) []attribute.KeyValue

DefaultTraceAttributes adds Args and Dir as attributes. Note that Args may contain sensitive data.

func ExitCode added in v0.2.0

func ExitCode(err error) int

ExitCode returns the exit code associated with err if there is one, otherwise 1. If err is nil, returns 0.

In practice, this replicates the behaviour observed when running commands in the shell, running a command with an incorrect syntax for example will set $? to 1, which is what is expected in script. Not implementing this creates a confusing case where an error such as not finding the binary would either force the code to account for the absence of exit code, which defeats the purpose of this library which is to provide a convenient replacement for shell scripting.

Example
package main

import (
	"context"
	"fmt"

	"github.com/sourcegraph/run"
)

func main() {
	ctx := context.Background()

	err := run.Bash(ctx, "exit 123").Run().Wait()
	fmt.Println(run.ExitCode(err))

	err = run.Cmd(ctx, "echo", run.Arg("hello world!")).Run().Wait()
	fmt.Println(run.ExitCode(err))

	err = run.Cmd(ctx, "non-existing-binary").Run().Wait()
	fmt.Println(run.ExitCode(err))

}
Output:

123
0
1

func LogCommands added in v0.10.0

func LogCommands(ctx context.Context, log LogFunc) context.Context

LogCommands enables logging on all commands executed by sourcegraph/run within this context. Set to nil to disable (default), or use run.DefaultTraceAttributes for some recommended defaults.

Note that arguments and environments may contain sensitive information.

If you use loggers carrying contexts, e.g. via sourcegraph/log, it is recommended that you only enable this within relevant scopes.

func TraceCommands added in v0.10.0

func TraceCommands(ctx context.Context, attrs TraceAttributesFunc) context.Context

TraceCommands toggles OpenTelemetry tracing on all usages of sourcegraph/run within this context. Set to nil to disable (default).

Note that arguments and environments may contain sensitive information.

Types

type BashOpt added in v0.11.0

type BashOpt string

BashOpt denotes options for running bash commands. For more options see 'man bash'

const (
	// 'pipefail' instructs bash to fail an entire statement if any command in a pipefails.
	BashOptPipeFail BashOpt = "pipefail"
	// 'errexit' lets bash exit with an err exit code if a command fails .
	BashOptErrExit BashOpt = "errexit"
)

type Command

type Command struct {
	// contains filtered or unexported fields
}

Command builds a command for execution. Functions modify the underlying command.

func Bash added in v0.5.0

func Bash(ctx context.Context, parts ...string) *Command

Bash joins all the parts and builds a command from it to be run by 'bash -c'.

Arguments are not implicitly quoted - to quote arguemnts, you can use Arg.

func BashWith added in v0.11.0

func BashWith(ctx context.Context, opts []BashOpt, parts ...string) *Command

BashWith appends all the given bash options to the bash command with '-o'. The given parts is then joined together to be executed with 'bash -c'

The final command will have the following format: bash -o option-1 -c command. For recommended strict bash options see StrictBashOpts, which has 'pipefail' and 'errexit' options

func Cmd

func Cmd(ctx context.Context, parts ...string) *Command

Cmd joins all the parts and builds a command from it.

Arguments are not implicitly quoted - to quote arguments, you can use Arg.

func (*Command) Dir

func (c *Command) Dir(dir string) *Command

Dir sets the directory this command should be executed in.

func (*Command) Env

func (c *Command) Env(env map[string]string) *Command

Env adds the given environment variables to the command.

func (*Command) Environ

func (c *Command) Environ(environ []string) *Command

Environ adds the given strings representing the environment (key=value) to the command, for example os.Environ().

func (*Command) Input

func (c *Command) Input(input io.Reader) *Command

Input pipes the given io.Reader to the command. If an input is already set, the given input is appended.

func (*Command) ResetInput added in v0.2.0

func (c *Command) ResetInput() *Command

ResetInput sets the command's input to nil.

func (*Command) Run

func (c *Command) Run() Output

Run starts command execution and returns Output, which defaults to combined output.

func (*Command) StdErr added in v0.7.0

func (c *Command) StdErr() *Command

StdErr configures the command Output to only provide StdErr. By default, Output includes combined output.

func (*Command) StdOut added in v0.7.0

func (c *Command) StdOut() *Command

StdOut configures the command Output to only provide StdOut. By default, Output includes combined output.

type ExecutedCommand added in v0.10.0

type ExecutedCommand struct {
	Args    []string
	Dir     string
	Environ []string
}

ExecutedCommand represents a command that has been started.

type ExitCoder added in v0.2.0

type ExitCoder interface {
	error
	ExitCode() int
}

ExitCoder is an error that also denotes an exit code to exit with. Users of Output can check if an error implements this interface to get the underlying exit code of a command execution.

For convenience, the ExitCode function can be used to get the code.

type LineMap added in v0.6.0

type LineMap func(ctx context.Context, line []byte, dst io.Writer) (int, error)

LineMap allows modifications of individual lines from Output and enables callbacks that operate on lines from Output. Bytes written to dst are collected and passed to subsequent LineMaps before being written to output aggregation, e.g. Output.Stream().

The return value mirrors the signature of (Writer).Write(), and should be used to indicate what was written to dst.

Errors interrupt line processing and are returned if and only if the command itself did not exit with an error.

func MapJQ added in v0.6.0

func MapJQ(query string) (LineMap, error)

MapJQ creates a LineMap that executes a JQ query against each line and replaces the output with the result.

Refer to https://github.com/itchyny/gojq for the specifics of supported syntax.

type LogFunc added in v0.10.0

type LogFunc func(ExecutedCommand)

LogFunc can be used to generate a log entry for the executed command.

type Output

type Output interface {
	// Map adds a LineMap function to be applied to this Output.
	//
	// Deprecated: Use Pipeline instead.
	Map(f LineMap) Output
	// Pipeline is similar to Map, but uses a new interface that provides more flexible
	// ways of manipulating output on the stream. It is only applied at aggregation time
	// using e.g. Stream, Lines, and so on. Multiple Pipelines are applied sequentially,
	// with the result of previous Pipelines propagated to subsequent Pipelines.
	//
	// For more details, refer to the pipeline.Pipeline documentation.
	Pipeline(p pipeline.Pipeline) Output

	// Stream writes mapped output from the command to the destination writer until
	// command completion.
	Stream(dst io.Writer) error
	// StreamLines writes mapped output from the command and sends it line by line to the
	// destination callback until command completion.
	StreamLines(dst func(line string)) error
	// Lines waits for command completion and aggregates mapped output from the command as
	// a slice of lines.
	Lines() ([]string, error)
	// Lines waits for command completion and aggregates mapped output from the command as
	// a combined string.
	String() (string, error)
	// JQ waits for command completion executes a JQ query against the entire output.
	//
	// Refer to https://github.com/itchyny/gojq for the specifics of supported syntax.
	JQ(query string) ([]byte, error)
	// Reader is implemented so that Output can be provided directly to another Command
	// using Input().
	io.Reader
	// WriterTo is implemented for convenience when chaining commands in LineMap.
	io.WriterTo

	// Wait waits for command completion and returns.
	Wait() error
}

Output configures output and aggregation from a command.

It is behind an interface to more easily enable mock outputs and build different types of outputs, such as multi-outputs and error-only outputs, without complicating the core commandOutput implementation.

func NewErrorOutput added in v0.4.0

func NewErrorOutput(err error) Output

NewErrorOutput creates an Output that just returns error. Useful for allowing function that help run Commands and want to just return an Output even if errors can happen before command execution.

type TraceAttributesFunc added in v0.10.0

type TraceAttributesFunc func(ExecutedCommand) []attribute.KeyValue

TraceAttributesFunc can be used to generate attributes to attach to a span for the executed command.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL