MultiWriter is the interface Runs use to display UI. To start a Run, you must pass a MultiWriter into Run.Start.
MultiWriter is a subset of UI, so the UIs produced by NewTUI and NewPrinter implement MultiWriter.
type MultiWriter interface { Writer(id string) io.Writer }
A Run represents an execution of a task, including,
A Run is safe to access concurrently from multiple goroutines.
type Run struct {
// contains filtered or unexported fields
}
func RunTask(dir string, allTasks Tasks, taskID string) (*Run, error)
RunTask creates an executable Run from a taskList and a taskID.
The run will handle task dependencies, watches, and triggers as documented in the README.
func (r *Run) IDs() []string
IDs returns the list of output stream names that a Run would write to. This includes the IDs of each Task that will be used in the run, plus (if applicable) the id "@watch", which the Run uses for messaging about file watchers.
func (r *Run) Invalidate(id string)
Invalidate asks a task to rerun. It will block until the Run gets the message (which is BEFORE the task is restarted).
func (r *Run) Start(ctx context.Context, out MultiWriter) error
Start starts the Run, waits for it to complete, and returns an error. Remember that "long" runs will never complete until canceled.
func (r *Run) TaskStatus(id string) TaskStatus
TaskStatus, given a task ID, returns that task's TaskStatus.
func (r *Run) Tasks() Tasks
Tasks returns the Tasks that a Run would execute.
func (r *Run) Type() RunType
Type returns the RunType of a run. It is RunTypeLong if any task is "long", otherwise it is RunTypeShort.
If a run is RunTypeShort, it will exit once all of its tasks have succeeded. If a run is RunTypeLong, it will continue running until it is interrupted. File watches are only used if a run is RunTypeLong.
A Run's RunType is RunTypeLong if any task is "long", otherwise it is RunTypeShort.
If a run is RunTypeShort, it will exit once all of its tasks have succeeded. If a run is RunTypeLong, it will continue running until it is interrupted. File watches are only used if a run is RunTypeLong.
type RunType int
const ( RunTypeShort RunType RunTypeLong )
func (i RunType) String() string
Anything implementing Task can be run by bundling it into a Tasks and then passing it into RunTask.
ScriptTask and FuncTask can be used to create Tasks.
A Task must be safe to access concurrently from multiple goroutines.
type Task interface { Start(ctx context.Context, stdout io.Writer) error Metadata() TaskMetadata }
func FuncTask(fn func(ctx context.Context, w io.Writer) error, metadata TaskMetadata) Task
FuncTask produces a runnable Task from a go function. metadata.Dir is ignored.
func ScriptTask(script string, dir string, env []string, metadata TaskMetadata) Task
ScriptTask produces a runnable Task from a bash script and working directory. The script will execute in metadata.Dir. The script's Stdout and Stderr will be provided by the Run, and will be forwarded to the UI. The script will not get a Stdin.
Script runs in a new bash process, and can have multiple lines. It is run basically like this:
$ cd $DIR $ bash -c "$CMD" 2&>1 /some/ui
TaskMetadata contains the data which, regardless of the type of Task, a Run uses for task execution.
type TaskMetadata struct { // ID identifies a task, for example, // - for command line invocation, as in `$ run <id>` // - in the TUI's task list. ID string // Description optionally provides additional information about a task, // which can be displayed, for example, by running `run -list`. It can // be one line or many lines. Description string // Type specifies how we manage a task. // // If the Type is "long", // - We will keep the task alive by restarting it if it exits. // - If the long task A is a dependency of task B, we will begin B as // soon as A starts. // - It is invalid to use a long task as a trigger, since long tasks // aren't expected to end. // // If the Type is "short", // - If the Start returns nil, we will consider it done. // - If the Start returns an error, we will wait 1 second and rerun it. // - If the short task A is a dependency or trigger of task B, we will // wait for A to complete before starting B. // // Any Type besides "long" or "short" is invalid. There is no default // type: every task must specify its type. Type string // Dependencies are other tasks IDs which should always run alongside // this task. If a task A lists B as a dependency, running A will first // run B. // // Dependencies do not set up an invalidation relationship: if long // task A lists short task B as a dependency, and B reruns because a // watched file is changed, we will not restart A, assuming that A has // its own mechanism for detecting file changes. If A does not have // such a mechanhism, use a trigger rather than a dependency. // // Dependencies can be task IDs from child directories. For example, // the dependency "css/build" specifies the task with ID "build" in the // tasks file "./css/tasks.toml". // // If a task depends on a "long" task, Run doesn't really know when the // long task has produced whatever output is depended on, so the // dependent is run 500ms after the long task starts. Dependencies []string // Triggers are other task IDs which should always be run alongside // this task, and whose success should cause this task to re-execute. // If a task A lists B as a dependency, and both A and B are running, // successful execution of B will always trigger an execution of A. // // Triggers can be task IDs from child directories. For example, the // trigger "css/build" specifies the task with ID "build" in the tasks // file "./css/tasks.toml". // // It is invalid to use a "long" task as a trigger. Triggers []string // Watch specifies file paths where, if a change to // the file path is detected, we should restart the // task. Watch supports globs, and does **not** // support the "./..." style used typical of Go // command line tools. // // For example, // - `"."` watches for changes to the working // directory only, but not changes within // subdirectories. // - `"**" watches for changes at any level within // the working directory. // - `"./some/path/file.txt"` watches for changes // to the file, which must already exist. // - `"./src/website/**/*.js"` watches for changes // to javascript files within src/website. Watch []string }
type TaskStatus int
const ( TaskStatusNotStarted TaskStatus TaskStatusRunning TaskStatusRestarting TaskStatusFailed TaskStatusDone )
func (i TaskStatus) String() string
Tasks is an opaque data structure representing an immutable, ordered collection of [Task]s. You can create a Run by passing a Tasks into RunTask.
type Tasks struct {
// contains filtered or unexported fields
}
func Load(cwd string) (Tasks, error)
Load loads a task file from the specified directory, producing a set of Tasks.
func NewTasks(tasks []Task) Tasks
NewTasks creates a Tasks from the given slice of tasks.
func (ts Tasks) Get(id string) Task
Get looks up a specific task by its ID. If no task bearing that ID is present, the task will be nil.
func (ts Tasks) Has(id string) bool
Has returns true if the given ID is present among the Tasks.
func (ts Tasks) IDs() []string
IDs returns the task IDs in their canonical order.
func (ts Tasks) Validate() error
Validate inspects a set of Tasks and returns an error if the set is invalid. If the error is not nill, its [error.Error] will return a formatted multiline string describing the problems with the task set.
A UI is essentially a multiplexed io.Writer that can be started and stopped. Since UIs implement MultiWriter, they can be passed into Run.Start to display run execution.
The functions NewTUI and NewPrinter produce implementors of UI.
type UI interface { Start(ctx context.Context, ready chan<- struct{}, stdin io.Reader, stdout io.Writer) error Writer(id string) io.Writer }
func NewPrinter(run *Run) UI
NewPrinter produces a non-interactive UI for displaying interleaved multiplexed streams. The UI prints interleaved output from all of the streams to its Stdout. The output is suitable for piping to a file.
The UI can be passed into Run.Start to display a run's execution.
The UI is safe to access concurrently from multiple goroutines.
func NewTUI(run *Run) UI
NewTUI produces an interactive terminal UI for displaying mulitplexed streams. The UI shows a list of the streams, and allows keyboard and mouse navigation for selecting a particular stream to inspect.
The UI can be passed into Run.Start to display a run's execution.
The UI is safe to access concurrently from multiple goroutines.