Documentation
¶
Overview ¶
Package boone provides mechanisms for configuration, file-activity monitoring, command execution, and UI.
Index ¶
- Constants
- func FinalizeConfig(all []*Target, c *Config) error
- func GetGlobInclude(globs []cage_filepath.GlobAnyOutput) (paths map[string]cage_filepath.Glob, err error)
- func GetTargetGlob(include []cage_filepath.Glob, exclude []cage_filepath.Glob) (list []cage_filepath.GlobAnyOutput, err error)
- func VisitDownstream(t *Target, visit func(t *Target) error) (err error)
- type CmdTemplateData
- type Config
- type DataConfig
- type Dispatcher
- type Exec
- type ExecRequest
- type GlobalConfig
- type Handler
- type ListItemWidget
- type Session
- type SessionConfig
- type Status
- type Target
- type TargetContext
- type TargetPass
- type TargetStatus
- type TargetTree
- type TreePass
- type UI
- type Watcher
Constants ¶
const ( // DefaultDebounce is the default Target.Debounce value. DefaultDebounce = "15s" // DefaultTimeout is the default Target.Handler.Exec.Timeout value. DefaultCmdTimeout = "15m" // DefaultCooldown is the default Global value. DefaultCooldown = "5s" )
const ( // BodyBoxTopPad selects top-padding of ListItemWidget body areas. BodyBoxTopPad = 1 // DetailListMaxLen is the static row length of the status-detail list. DetailListMaxLen = 3 // DetailStderrPos positions standard error as the first status-detail list item. DetailStderrPos = 0 // DetailStdoutPos positions standard error as the second status-detail list item. DetailStdoutPos = 1 // DetailMiscPos positions misc. details as the third status-detail list item. DetailMiscPos = 2 // ListItemWidgetPad is the all-sides padding of every ListItemWidget. ListItemWidgetPad = 1 // StatusListMaxLen is the static row length of the status list. StatusListMaxLen = 9 )
const ( // ExecRequestQueueTick defines how often to dequeue exectution requests which have been // debounced and are ready to be fulfilled. ExecRequestQueueTick = time.Second )
Variables ¶
This section is empty.
Functions ¶
func FinalizeConfig ¶
FinalizeConfig validates and finalizes Config fields.
func GetGlobInclude ¶
func GetGlobInclude(globs []cage_filepath.GlobAnyOutput) (paths map[string]cage_filepath.Glob, err error)
GetGlobInclude extracts all included file/directory paths from the GlobAny outputs.
If a single path was covered by multiple globs, the first encountered Include will be output.
func GetTargetGlob ¶
func GetTargetGlob(include []cage_filepath.Glob, exclude []cage_filepath.Glob) (list []cage_filepath.GlobAnyOutput, err error)
GetTargetGlob searches for files which match at least one Include or one Exclude pattern.
It returns one GlobAnyOutput per input inclusion Glob. GlobAnyOutput.Include holds the concrete paths which matched at least one inclusion pattern and no exclusion pattern. GlobAnyOutput.Exclude holds concrete paths which matched at least one inclusion pattern but was rejected because it matched at least one exclusion pattern.
Types ¶
type CmdTemplateData ¶
type CmdTemplateData struct { // Dir is the absolute path of the parent directory of Path. Dir string // HandlerLabel is a copy of Target.Handler.Label. HandlerLabel string // IncludeGlob pattern from the config file Include responsible for the command being triggered. IncludeGlob string // IncludeRoot is the ancestor root directory from the config file Include responsible for the // command being triggered. IncludeRoot string // Path is the absolute path of the file/directory that was created or written to. Path string // TargetLabel is a copy of Target.Label. TargetLabel string }
CmdTemplateData describes the built-in template variables available in Target.Handler.Exec.Cmd config strings.
type Config ¶
type Config struct { // Data defines how to store program state. Data DataConfig // Global defines properties which should be applied to all targets, e.g. as default values. Global GlobalConfig // Target defines file/directory paths to watch and commands to run when they receive writes. Target []Target // Template holds key/value pairs which can be used in some string fields via {{.key_name}} syntax. // // Key names must use lowercase due to viper(/mapstructure?) limitation. Convention: "some_key_name". // https://github.com/spf13/viper/issues/411 // https://github.com/spf13/viper/pull/635 Template map[string]string // AutoStartTarget holds an Id for each Target that should run when the main process starts. AutoStartTarget []string // contains filtered or unexported fields }
Config defines the structure of a config file.
func ReadConfigFile ¶
ReadConfigFile converts a file to a Config value.
func (*Config) GetStartTarget ¶
GetStartTarget returns all targets selected in the AutoStartTarget config.
type DataConfig ¶
type DataConfig struct { // Session defines how to store sessions. Session SessionConfig }
DataConfig defines how to store program state.
Its config section is Data.
type Dispatcher ¶
type Dispatcher struct { // Clock supports timer mocking for debounce-sensitive tests. Clock cage_time.Clock // Cooldown is how long to wait after one command finishes before starting another. Cooldown time.Duration // Executor supports os/exec.Cmd mocking for tests. Executor cage_exec.Executor // ExecReqCh receives requests from Watcher when a target has been triggered. // Sends are blocked only for as long as it takes to manually add them to a slice queue. ExecReqCh chan ExecRequest // Log receives debug/info-level messages. Log *zap.Logger // TreePassCh transports messages from the Dispatcher to the UI about the successful execution of all // commands of the activity-triggered target and all commands of downstream targets. TreePassCh chan TreePass // TargetStartCh transports messages from Dispatcher to the UI about newly pending targets and the start // of every executed command. TargetStartCh chan Status // TargetPassCh transports messages from the Dispatcher to the UI about the successful execution of all // commands of a single target. TargetPassCh chan TargetPass // TargetFailCh transports messages from the Dispatcher to the UI about the failed execution of a command. TargetFailCh chan Status // contains filtered or unexported fields }
Dispatcher receives ExecRequest messages from Watcher, runs/cancels target commands, and informs the UI of new target statuses via channels.
func NewDispatcher ¶
func NewDispatcher(log *zap.Logger, targets []Target, panicCh chan interface{}, globalConfig GlobalConfig) (*Dispatcher, error)
NewDispatcher returns a new instance which is already watching for writes to targets' configured paths and sending messages to its channels about target run starts, failures, etc.
func (*Dispatcher) Start ¶
func (d *Dispatcher) Start()
Start debounces activity messages from Watcher, cancels in-progress commands if newer activity will make them redundant, and enqueues targets to run.
It should run in its own goroutine because its for-select blocks.
func (*Dispatcher) Stop ¶
func (d *Dispatcher) Stop()
Stop prevents the Dispatcher from receiving any more Watcher messages (and running any more targets), and kills the in-progress command if present.
type Exec ¶
type Exec struct { // Cmd holds a single command or multiple commands in a "|" pipeline. Cmd string // Dir is the working directory. // // It is relative to Target.Root and Target.Root by default. Dir string // Timeout is a time.Duration compatible string from the config file that defines // how long to wait before cancelling the command. Timeout string // Env holds "KEY=VALUE" pairs to overwrite in the current environment. Env []string // contains filtered or unexported fields }
Exec defines what command to run and how to run it.
func (Exec) GetTimeout ¶
GetTimeout returns the parsed value of Timeout.
type ExecRequest ¶
type ExecRequest struct { // Origin is a free-form value, currently only for logging, which indicates the cause // of the request. Cause string // Debounce is how long to wait for file activity to stop before running the target. Debounce time.Duration // Event describes the filesystem operation which led to the request. Event watcher.Event // Include is the path pattern responsible for the Watcher capturing the activity. Include cage_filepath.Glob // RecvTime is when Dispatcher received the ExecRequest. // // It is used to cancel target runs before they start when the request has alraedy been sent to // Dispatcher.runTargetCh but should be ignored because a newer request was received in the meantime. RecvTime time.Time // Tree holds all targets downstream from the activity-triggered target. Tree []TargetTree // TargetId is a copy of the Id field of the activity-triggered Target. TargetId string // TargetLabel is a copy of the Label field of the activity-triggered Target. TargetLabel string }
ExecRequest is a channel-sent request to run a target (and its downstream targets). Typically it orignates from a Watcher which detected target-specific activity, but it may also originate directly from sub-commands, e.g. to support AutoStartTarget.
type GlobalConfig ¶
type GlobalConfig struct { // Cooldown is a time.Duration compatible string which selects how long to wait after one command // finishes before starting another. Cooldown string // Exclude are appended to every Target.Exclude list. Exclude []cage_filepath.Glob // contains filtered or unexported fields }
GlobalConfig defines properties which should be applied to all targets, e.g. as default values.
func (GlobalConfig) GetCooldown ¶
func (c GlobalConfig) GetCooldown() time.Duration
GetCooldown returns the converted value of Cooldown.
type Handler ¶
type Handler struct { // Label is displayed to users in output for reference/debugging/etc. and also // provides a documentation in the config file on the intent. // // It is a required field. Label string // Exec defines the commands to execute. Exec []Exec }
Handler defines one or more commands that must execute in response to a target trigger.
type ListItemWidget ¶
type ListItemWidget struct { // Container is the flexible height/width box which bounds the Header and Body areas. Container *tview.Flex // Header areas are single-lined and display target labels/statuses/shortcuts in the status list, // and display detail types/shortcuts in the status-detail list. Header *tview.TextView // Body areas expand to use all space unused by the Header and display a snippet of standard error // in the status list, and display a detail snippets in the status-detail list. Body *tview.TextView }
ListItemWidget is used to represent the status and status-detail lists.
func NewListItemWidget ¶
func NewListItemWidget() *ListItemWidget
NewListItemWidget returns a widget initialized with its container, header, and body areas.
type Session ¶
type Session struct { // Statuses holds one Status per Target which was displayed in the UI when the Session value is // created. Statuses []Status // Version is a copy of the SessionVersion constant when the Session value is created. Version int }
Session is written to file periodically to support resumption of targets which were pending/running, and tracking unresolved target failures.
type SessionConfig ¶
type SessionConfig struct {
File string
}
SessionConfig defines how to store sessions.
Its config section is Data.Session.
type Status ¶
type Status struct { // Cause explains why the status is in the list. Cause TargetStatus // Cmd was the final command string after template expansion. Cmd string // Downstream holds labels of all downstream targets included in the run. Downstream []string // EndTime is when the Cmd finished. EndTime time.Time // Err is non-nil if Cmd failed. Err string // HandlerLabel is from the source of the status. HandlerLabel string // Include is the one responsible for the capturing the file activity which led to running the target. Include cage_filepath.Glob // Op is the type of filesystem operation which led to target execution. // // It is "Create", "Rename", or "Write" Op string // Path identifies the file whose Op activity triggered the target. Path string // Pid is the process Id of Cmd. Pid []int // RunLen is how long Cmd ran. RunLen time.Duration // StartTime is when Cmd started. StartTime time.Time // Stderr is collected from Cmd exceution. Stderr string // Stdout is collected from Cmd exceution. Stdout string // TargetId is from the source of the status. TargetId string // TargetLabel is from the source of the status. TargetLabel string // UpstreamTargetLabel is the one whose activity triggered the handler execution flow // that may include one or more (of its) downstream targets. If there were no // downstream targets, it should equal the Target field. UpstreamTargetLabel string }
Status describes a target listed in the UI on its initial screen.
type Target ¶
type Target struct { // Debounce is a time.Duration compatible string from the config file that defines // how long to wait after file activity settles before executing handlers. Debounce string // Downstream holds all direct descendants. // // It is generated at startup. Downstream []*Target // Exclude defines the path patterns of files/directories which should invalidate an Include match. Exclude []cage_filepath.Glob // Handler defines the commands to run if the target is triggered by write-activity. Handler []Handler // Id is user-defined, ideally short, and must be unique in the config file. // // It is an optional field and supports features like upstream-target triggers. Id string // Include defines the path patterns of files/directories whose write-activity can trigger this target. Include []cage_filepath.Glob // Label is displayed to users in output for reference/debugging/etc. and also // provides a documentation in the config file on the intent. // // It is a required field. Label string // Root is the default path prefix value for Include.Root fields. Root string // Tree holds one item per Target which Dispatcher should execute when this Target is // triggered. It includes ths Target in the first item, followed by all downstream // targets found recursively. // // It only holds the minimum details of each target in order to avoid data races, // e.g. that might happen with a map of Target/*Target. // // It is generated at startup. Tree []TargetTree // Upstream holds Id values of targets that, when triggered, also trigger this target. Upstream []string // contains filtered or unexported fields }
Target defines upstream-target and/or filesystem triggers, and the handlers to run as a result.
Upstream Target.Id values will be stored in an app-level map instead of this type.
func (*Target) ContainsDownstream ¶
ContainsDownstream returns true if a target is found downstream recursively.
func (*Target) ExpandTemplateVars ¶
ExpandTemplateVars updates Target configuration string fields by expanding template variables with associated input values.
func (Target) GetDebounce ¶
GetDebounce returns the parsed version of Debounce.
func (*Target) MatchPath ¶
func (t *Target) MatchPath(name string) (cage_filepath.MatchAnyOutput, error)
MatchPath checks if the input path matches one of the target's inclusion patterns and no exclusion pattern.
type TargetContext ¶
type TargetContext struct { // Ctx is initialized by Dispatcher when it starts the first handler. // If there are multiple handlers, they all share the same value in order // to allow Dispatcher to cancel the target regardless of which handler // is running. Ctx context.Context // Cancel is invoked to kill the running command. Cancel context.CancelFunc }
TargetContext enables Dispatcher to cancel a target's command execution if its watched paths receive activity in the meantime, invalidating the current execution.
type TargetPass ¶
type TargetPass struct { // RunLen is how long it took to run a target's command list. RunLen time.Duration // TargetId is a copy of Target.Id. TargetId string }
TargetPass describes a target whose commands all finished successfully.
type TargetStatus ¶
type TargetStatus string
TargetStatus explains why a target is listed in the UI on the initial screen.
const ( // PreDebounce prevents cage/os/file/watcher.Fsnotify from sending duplicate events, // in some situations, when both a file and its directory are watched. // // This value was selected because it's assumed to be long enough to capture all the // duplicates and less than user-selected per-Target debounce values. PreDebounce = 500 * time.Millisecond // SessionVersion is included in the encoded Session file to support potential compatibility work. SessionVersion = 1 // Target TargetCanceled TargetStatus = "canceled" // TargetFailed indicates a target command returned a non-zero exit code and the Dispatcher // will not proceed any further with that target until it is activated again. TargetFailed TargetStatus = "failed" // TargetPending indicates the target's latest file activity has been debounced, the target // has been enqueued to run, and it is waiting to start. TargetPending TargetStatus = "pending" // TargetResumed indicates the program saved a TargetStarted-status target in its session file // (if configured) at shutdown, then enqueued it during startup. TargetResumed TargetStatus = "resumed" // TargetStarted indicates a Dispatcher has started running the target's command(s). TargetStarted TargetStatus = "started" )
type TargetTree ¶
TargetTree is a limited copy of Target fields which describe a single downstream target.
It is used in Target as an abbreviated inventory of which targets should also run after the upstream target finishes.
type TreePass ¶
type TreePass struct { // DispatchTargetId is the first target in the tree and whose activity led to the tree execution. DispatchTargetId string }
TreePass describes a set of targets (activity-triggered target and all its downstream targets) whose commands all finished successfully.
type UI ¶
type UI struct {
// contains filtered or unexported fields
}
UI displays the status of targets which are scheduled, currently running, or have stopped due to an error. It maintains the data necessary to describe the target statuses based on channel messages from Dispatcher. It also responds to keyboard events in order to support screen navigation.
func NewUI ¶
func NewUI(log *zap.Logger, targetStartCh chan Status, targetPassCh chan TargetPass, targetFailCh chan Status, statusList []Status) *UI
NewUI returns a UI instance configured to listen for status updates from the input channel.
func (*UI) ExitCh ¶
func (u *UI) ExitCh() <-chan struct{}
ExitCh provides external listeners to know when the UI is shutting down based on a keyboard event.
func (*UI) InputCapture ¶
InputCapture listens for keyboard events from all screens.
func (*UI) SessionCh ¶
SessionCh provides external listeners to know when the newest session description is available
func (*UI) Start ¶
Start begins the goroutines which update the UI based on new data from a Dispatcher, periodically update the displayed relative times, and which render the UI.
It blocks until the UI is exited via keyboard shortcut.
func (*UI) Stop ¶
func (u *UI) Stop()
Stop ends UI rendering and keyboard event capturing.
It must be called to prevent corrupting the terminal such that `reset` is required. See tview's Fini function (https://github.com/rivo/tview/blob/11727c933b6d128d588006cc14105160c5413585/application.go#L90).
It unblocks the goroutine which executes Start.
type Watcher ¶
type Watcher struct { // Watcher is the actual filesystem monitor. Watcher is a subscriber of events emitted by the monitor. // // See NewDispatcher for how it and Watcher are wired together. watcher.Watcher // PanicCh transports messages from Watcher to the CLI to support cleaner shutdowns. PanicCh chan<- interface{} // ExecReqCh transports messages from Watcher to the Dispatcher to run activated targets. ExecReqCh chan<- ExecRequest // AddPathCh transports messages from Watcher to listeners which contain paths to newly // created files/directories that are now themselves watched for writes. // // It is currently only used by tests. AddPathCh chan<- string // Target is the scope of this Watcher's write-activity detection. Target Target // Log receives debug/info-level messages. Log *zap.Logger // contains filtered or unexported fields }
Watcher listens for the write-activity of a single target's files/directories and sends Dispatcher requests to execute the activated targets.
It does not itself monitor filesystem events and instead implements ca/cage/os/file/watcher.Subscriber to receive events/errors from the actual monitor (Watcher.watcher).
func (*Watcher) Error ¶
Error receives errors from the filesystem monitor (Watcher.watcher).
It implements ca/cage/os/file/watcher.Subscriber.
func (*Watcher) Event ¶
Event receives activity descriptions from the filesystem monitor (Watcher.watcher).
It implements ca/cage/os/file/watcher.Subscriber.
func (*Watcher) SetInclude ¶
func (w *Watcher) SetInclude(i map[string]cage_filepath.Glob)
SetInclude assigns the inclusion patterns to use when filtering write-activity.