Advanced Queue Playbook
These examples illustrate how to orchestrate non-trivial workflows with Ziva:
- split a process into multiple stages and append tasks dynamically;
- react to user choices and task outcomes;
- recover from errors without leaving the TUI.
All snippets rely on the public ziva
API. Keep them in an examples
package and call the helpers when you need to demonstrate behaviour.
Multi-stage deployment template
// DeployCluster orchestrates a sequential setup that gathers user input,
// displays progress, and stores intermediate results for later stages.
func DeployCluster() error {
summary := ziva.NewFuncTask(
"Preflight checks",
runPreflightChecks,
ziva.WithSummaryFunction(func() []string {
return []string{"DNS: ok", "Network: ok"}
}),
ziva.WithStopOnError(true),
)
topology := ziva.NewSingleSelectTask(
"Topology",
[]string{
"single-node::Single node",
"multi-node::Multi node",
"ha::High availability",
},
).WithDefaultItem("multi-node")
nodes := ziva.NewInputTask(
"Node count",
"How many worker nodes do we provision?",
).
WithInputType(ziva.InputTypeNumber).
WithValidator(ziva.DefaultValidators.Range(1, 20)).
WithTimeout(30*time.Second, "3")
queue := ziva.NewQueue("Ziva Deployment").
WithAppName("Ziva CLI").
WithTasksNumbered(true, "[%02d]")
queue.AddTasks(summary, topology, nodes)
if err := queue.Run(); err != nil {
return err
}
// Add context-aware tasks based on the selected topology.
followUp := ziva.NewQueue("Follow-up tasks")
switch topology.GetSelected() {
case "single-node":
followUp.AddTasks(planBackupTask(), confirmMetricsTask())
case "multi-node":
followUp.AddTasks(configureIngressTask(), scaleOutTask(nodes))
case "ha":
followUp.AddTasks(haPrecheckTask(), configureQuorumTask(nodes))
}
return followUp.Run()
}
Highlights
- Two queue runs. The first queue captures input; the second queue is assembled at runtime.
- Summary function.
WithSummaryFunction
displays a concise recap under the task and improves operator feedback.
- Timeouts.
WithTimeout
prevents headless sessions from hanging indefinitely.
Retrying with user confirmation
func RunWithRetries(label string, fn func() error, maxAttempts int) error {
attempt := 1
for {
task := ziva.NewFuncTask(
fmt.Sprintf("%s · attempt %d", label, attempt),
fn,
ziva.WithStopOnError(false),
)
queue := ziva.NewQueue(label)
queue.AddTasks(task)
if err := queue.Run(); err == nil {
return nil
}
if attempt >= maxAttempts {
return fmt.Errorf("reached max attempts (%d)", maxAttempts)
}
retry := ziva.NewYesNoTask(
"Retry",
fmt.Sprintf("Attempt %d failed. Retry?", attempt),
).WithDefaultYesAndTimeout(10 * time.Second)
confirmation := ziva.NewQueue("Retry decision")
confirmation.AddTasks(retry)
confirmation.Run()
if !retry.IsYes() {
return fmt.Errorf("operation cancelled at attempt %d", attempt)
}
attempt++
}
}
When to use it
- flaky infrastructure calls;
- firmware updates over unstable connections;
- long-running operations that deserve a human-confirmed retry.
Building tasks on the fly
func ConfigureServers() error {
count := ziva.NewInputTask("Server count", "How many servers do we configure?").
WithInputType(ziva.InputTypeNumber).
WithValidator(ziva.DefaultValidators.Range(1, 8))
baseQueue := ziva.NewQueue("Configuration parameters")
baseQueue.AddTasks(count)
if err := baseQueue.Run(); err != nil {
return err
}
n, _ := strconv.Atoi(count.GetValue())
var tasks []ziva.Task
for i := 1; i <= n; i++ {
prefix := fmt.Sprintf("Server %d", i)
host := ziva.NewInputTask(prefix+" · host", "Enter FQDN")
host.WithValidator(ziva.DefaultValidators.Domain())
port := ziva.NewInputTask(prefix+" · port", "Enter port")
port.WithInputType(ziva.InputTypeNumber).
WithValidator(ziva.DefaultValidators.Port())
tasks = append(tasks, host, port)
}
servers := ziva.NewQueue("Server configuration").
WithTasksNumbered(true, "[%02d]")
servers.AddTasks(tasks...)
return servers.Run()
}
If a workflow can generate hundreds of tasks, split the execution into several queues and run them in batches to keep memory usage predictable.
Quick checklist for complex flows
- Keep references to tasks whose results you need later.
- Use separate queues for distinct stages — it simplifies reasoning and module tests.
- Call
AutoConfigure()
on startup so Ziva tunes itself for the target environment.
- Combine
EnableEmbeddedMode()
with WithOutResultLine()
on constrained devices to keep the output compact.
- Remember that the queue is sequential; if you need pseudo-parallel work, launch extra queues from
FuncTask
handlers.
Continue with the validation demo to see how to compose robust forms.