package task

import "github.com/amonks/run/task"
Example (BringYourOwnTasks)

In this example, we generate our own Task and run it.

package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"strings"
	"time"

	"github.com/amonks/run/task"
)

func main() {
	tasks := task.NewLibrary(
		task.FuncTask(func(ctx context.Context, onReady chan<- struct{}, w io.Writer) error {
			w.Write([]byte("sleep"))
			time.Sleep(1 * time.Second)
			w.Write([]byte("done"))
			close(onReady)
			return nil
		}, task.TaskMetadata{ID: "custom", Type: "short"}),
	)

	ids := tasks.IDs()
	if len(ids) != 1 {
		log.Fatal("expected 1 task")
	}

	fmt.Println(strings.Join(ids, ", "))
}

Output:

custom

Index

Examples

Types

type Library

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

Library is an opaque data structure representing an immutable, ordered collection of [Task]s. You can create a [runner.Run] by passing a Library into [runner.New].

func NewLibrary

func NewLibrary(tasks ...Task) Library

NewLibrary creates a Library from the given tasks.

func (Library) Get

func (lib Library) 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 (Library) Has

func (lib Library) Has(id string) bool

Has returns true if the given ID is present among the Library.

func (Library) HasWatch

func (lib Library) HasWatch(path string) bool

HasWatch returns true if any task in the Library watches the given path.

func (Library) IDs

func (lib Library) IDs() []string

IDs returns the task IDs in their canonical order.

func (Library) LongestID

func (lib Library) LongestID() int

LongestID returns the length of the longest task ID.

func (Library) Size

func (lib Library) Size() int

Size returns the number of tasks in the Library.

func (Library) Subtree

func (lib Library) Subtree(ids ...string) Library

Subtree returns a new Library containing only the given task IDs and their transitive dependencies and triggers, preserving the canonical order from the original Library.

func (Library) Validate

func (lib Library) Validate() error

Validate inspects a Library and returns an error if it is invalid. If the error is not nil, its [error.Error] will return a formatted multiline string describing the problems with the task set.

func (Library) ValidateWithCWD

func (lib Library) ValidateWithCWD(cwd string) error

ValidateWithCWD inspects a Library and returns an error if it is invalid, using the given working directory for path validation.

func (Library) Watches

func (lib Library) Watches() []string

Watches returns a sorted slice of unique watched paths across all tasks.

func (Library) WithDependency

func (lib Library) WithDependency(dep string) []string

WithDependency returns the IDs of tasks that list dep as a dependency.

func (Library) WithTrigger

func (lib Library) WithTrigger(trigger string) []string

WithTrigger returns the IDs of tasks that list trigger as a trigger.

func (Library) WithWatch

func (lib Library) WithWatch(path string) []string

WithWatch returns the IDs of tasks that watch the given path.

type Task

type Task interface {
	// Start runs the task. It should write output to stdout and close
	// onReady when the task is ready (i.e., has produced whatever output
	// dependents need). For short tasks, close onReady on successful
	// completion. For long tasks, close onReady as soon as the task is
	// serving / ready.
	Start(ctx context.Context, onReady chan<- struct{}, stdout io.Writer) error
	Metadata() TaskMetadata
}

Anything implementing Task can be run by bundling it into a Library and then passing it into [runner.New].

ScriptTask and FuncTask can be used to create Tasks.

A Task must be safe to access concurrently from multiple goroutines.

func FuncTask

func FuncTask(fn func(ctx context.Context, onReady chan<- struct{}, w io.Writer) error, metadata TaskMetadata) Task

FuncTask produces a runnable Task from a go function. The function receives an onReady channel that it should close when it is ready (i.e., has produced whatever output dependents need). metadata.Dir is ignored.

func ScriptTask

func ScriptTask(scriptText 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

type TaskMetadata

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 mechanism, 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, the dependent is started once
	// the long task signals readiness by closing its onReady channel.
	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
}

TaskMetadata contains the data which, regardless of the type of Task, a [runner.Run] uses for task execution.