script

package
v0.0.15 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2020 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package script makes it easy to write declarative scripts in go.

The core abstraction is a Task. A shell command is an example of a task and this can be created and executed via:

task := script.Cmd("echo", "hello")
err := script.Run(context.Background(), task)

A pure Go task can be created via script.Func:

task := script.Func(func(ctx context.Context, r io.Reader, w io.Writer) error {
    ...
    _, err := io.Copy(w, r)
    return err
})
err := script.Run(context.Background(), task)

Tasks can be composed in sequence:

task := script.Sequence(
    script.Cmd("echo", "hello"),
    script.Cmd("echo", "world"),
)
err := script.Run(context.Background(), task)

This type of composition allows for expressions without having to constantly check for errors. A sequential task stops with an error when one of its tasks fails.

The other types of control flow structures are "Parallel", "Or" and "If".

Tasks can also be "Piped" in a chain via `Pipe`:

task := script.Pipe(
    script.Cmd("echo", "hello"),
    script.Cmd("sed", "s/hello/world"),
    script.Cmd("grep", "world"),
)
err := script.Run(context.Background(), task)

Tasks can also be piped to a file (or piped from a file) by just using a file task `File(path)` in a pipe.

Non shell tasks can be mingled within via the `Func(...)` method. When the Func task is used in a Pipe, its input and output are provided via the reader and writer arg.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Run

func Run(ctx context.Context, t Task) error

Run runs a task.

Types

type Logger

type Logger interface {
	Println(v ...interface{})
}

type Task

type Task interface {
	// Start starts a task asynchronously.
	Start(ctx context.Context) error

	// Wait waits for the task to finish.
	// Wait can be called before the task is started. In this
	// case, it should do nothing and return nil.
	Wait(ctx context.Context) error

	// Stdin redirects input.
	// This is immutable returning a new task.
	Stdin(r io.Reader) Task

	// Stdout redirects output.
	// This is immutable returning a new task.
	Stdout(w io.Writer) Task
}

Task defines a task that can be run.

func Cmd

func Cmd(program string, args ...string) Task

Cmd runs a program with the provided args.

func CmdWithLog

func CmdWithLog(logger Logger, program string, args ...string) Task

CmdWithLog runs a program with the provided args and also logs output.

func File

func File(path string) Task

File runs a task which allows either input to be piped to it or for the file to be piped into another task.

Example (Redirect)
package main

import (
	"context"
	"fmt"

	"github.com/tvastar/gotools/pkg/script"
)

func main() {
	spec := script.Sequence(
		// echo hello > /tmp/hello.txt
		script.Pipe(
			script.Cmd("echo", "hello"),
			script.File("/tmp/hello.txt"),
		),

		// cat < /tmp/hello.txt
		script.Pipe(
			script.File("/tmp/hello.txt"),
			script.Cmd("cat"),
		),
		// script.Cmd("rm", "/tmp/hello.txt"),
	)
	err := script.Run(context.Background(), spec)
	if err != nil {
		fmt.Println("error", err)
	}

}
Output:

hello

func Func

func Func(f func(ctx context.Context, r io.Reader, w io.Writer) error) Task

Func runs a task function.

Example
package main

import (
	"context"
	"fmt"
	"io"

	"github.com/tvastar/gotools/pkg/script"
)

func main() {
	spec := script.Pipe(
		script.Cmd("echo", "hello"),
		script.Func(func(ctx context.Context, r io.Reader, w io.Writer) error {
			_, err := io.Copy(w, r)
			return err
		}),
		script.Cmd("cat"),
	)
	err := script.Run(context.Background(), spec)
	if err != nil {
		fmt.Println("error", err)
	}

}
Output:

hello

func If

func If(condition, thenTask, elseTask Task) Task

If runs the "then" task if the "condition" succeeds. It runs the else task if the "condition" fails. It is legal for thenTask and elseTask to be nil. If either of them are nil, the result of the condition is propagated.

Example
package main

import (
	"context"
	"fmt"

	"github.com/tvastar/gotools/pkg/script"
)

func main() {
	spec := script.If(
		script.Cmd("false"),
		script.Cmd("echo", "wrongly succeeded"),
		script.Cmd("echo", "rightly failed"),
	)
	err := script.Run(context.Background(), spec)
	if err != nil {
		fmt.Println("error", err)
	}

	spec = script.If(
		script.IgnoreError(script.Cmd("false")),
		script.Cmd("echo", "rightly succeeded"),
		script.Cmd("echo", "wrongly failed"),
	)
	err = script.Run(context.Background(), spec)
	if err != nil {
		fmt.Println("error", err)
	}

}
Output:

rightly failed
rightly succeeded

func IgnoreError

func IgnoreError(t Task) Task

IgnoreError runs a task and ignores any errors.

func Or

func Or(tasks ...Task) Task

Or runs a sequence of commands until one succeeds.

Example
package main

import (
	"context"
	"fmt"

	"github.com/tvastar/gotools/pkg/script"
)

func main() {
	spec := script.Or(
		script.Cmd("false"),
		script.Cmd("false"),
		script.Cmd("echo", "hello"),
	)
	err := script.Run(context.Background(), spec)
	if err != nil {
		fmt.Println("error", err)
	}

}
Output:

hello

func Parallel

func Parallel(tasks ...Task) Task

Parallel runs all tasks in parallel. If any tasks fail, it returns one of the errors.

func Pipe

func Pipe(tasks ...Task) Task

Pipe runs all tasks in a "pipe" chaining their input and outputs.

Example (If)
package main

import (
	"context"
	"fmt"

	"github.com/tvastar/gotools/pkg/script"
)

func main() {
	spec := script.If(
		script.Pipe(
			script.Cmd("echo", "hello"),
			script.Cmd("sed", "s/hello/world/"),
		),
		script.Cmd("echo", "rightly succeeded"),
		script.Cmd("echo", "wrongly failed"),
	)
	err := script.Run(context.Background(), spec)
	if err != nil {
		fmt.Println("error", err)
	}

}
Output:

world
rightly succeeded

func Sequence

func Sequence(tasks ...Task) Task

Sequence chains a sequence of tasks together. If any task fails, the sequence is aborted.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/tvastar/gotools/pkg/script"
)

func main() {
	logger := log.New(os.Stdout, "", 0)
	spec := script.Sequence(
		script.CmdWithLog(logger, "echo", "hello"),
		script.CmdWithLog(logger, "echo", "world"),
	)
	err := script.Run(context.Background(), spec)
	if err != nil {
		fmt.Println("error", err)
	}

}
Output:

> echo hello
hello
> echo world
world

Jump to

Keyboard shortcuts

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