How to Define a Simple YAML Workflow for Probe
Probe is a lightweight Go library that reads YAML workflow descriptions, expands variables using Go-template syntax, and executes jobs and steps with built-in support for dependencies, retries, and conditional skipping.
Defining a YAML workflow for Probe allows you to automate sequences of actions with minimal configuration. The linyows/probe repository implements a declarative execution engine that parses workflow files, handles variable interpolation, and manages job dependencies through a simple YAML interface.
Core Architecture and Data Structures
Understanding the underlying data structures helps you write valid YAML that maps correctly to Probe's execution model.
Top-Level Workflow Configuration
The Workflow struct, defined in workflow.go (lines 8-19), serves as the root container for your definition. It accepts a global name, optional description, and a vars map for variable definitions that become available throughout the workflow.
When Probe loads your file via Probe.Load() in probe.go (lines 72-90), it validates the YAML against these struct tags using go-playground/validator. The Workflow.evalVars() method (lines 84-105 in workflow.go) processes top-level variables immediately after loading, making them available for Go-template expansion in subsequent jobs and steps.
Job Definitions and Dependencies
Each entry in the jobs array maps to a Job struct defined in job.go (lines 9-16). Key fields include:
- name – Display identifier that supports template expansion
- id – Optional unique identifier for dependency references
- needs – Array of job IDs that must complete successfully before this job starts
- skipif – Boolean expression that conditionally omits the entire job
- steps – Ordered list of actions to execute
The JobScheduler, instantiated within Workflow.Start() (lines 22-61 in workflow.go), resolves needs dependencies to ensure proper execution order.
Step Configuration and Actions
The Step struct in step.go (lines 18-36) represents the atomic unit of work. Every step requires a uses field specifying the action name, and accepts a with map for parameters. Advanced configuration includes:
- test – Go-template expression evaluating to boolean; failure marks the step as error
- skipif – Boolean expression to conditionally bypass the step
- wait – Duration delay (e.g.,
"5s"or numeric seconds) - outputs – Key-value pairs saved to the shared
Outputsobject - retry – Configuration for
max_attempts,interval, andinitial_delay - timeout – Override for the default 5-minute execution limit
The Step.Do() method (lines 39-58 in step.go) orchestrates preparation, execution via Step.executeActionWithRetry (lines 41-94), result handling, and output recording.
Variable Expansion and Templating
Probe uses Go's text/template package for dynamic content. Define variables in the top-level vars section:
vars:
host: localhost
port: 8080
Reference these anywhere using dot notation: {{ .host }} or {{ .port }}. The expansion occurs during Workflow.evalVars() for global variables and during Job.Start() for job-specific name expansion.
Execution Pipeline
When you run probe workflow.yml, the execution follows this precise sequence:
- Loading Phase –
Probe.Load()reads the YAML file, validates struct constraints, and unmarshals into theWorkflowstruct - Initialization Phase –
Workflow.Start()creates the printer, initializes sharedOutputs, evaluates variables, and builds theJobScheduler - Job Execution –
Job.Start()expands the job name, checksskipifconditions, and iterates through steps - Step Execution –
Step.Do()prepares the context, executes the action with retry logic, processes results, and saves declared outputs
Each StepResult is collected for final reporting via the Printer implementation in printer.go.
Practical YAML Workflow Examples
Minimal Workflow Example
The smallest functional workflow uses the built-in echo action to print a message. Save this as simple.yml:
name: Simple demo
vars:
greeting: Hello
jobs:
- name: Echo greeting
steps:
- uses: echo
with:
message: "{{ .greeting }}, Probe!"
This workflow defines a single job with one step. The greeting variable interpolates into the message parameter before execution.
Conditional Execution and Job Dependencies
For complex scenarios, combine conditional skipping with dependency chains:
name: Conditional demo
vars:
run_extra: false
api_endpoint: https://api.example.com
jobs:
- id: setup
name: Initial setup
steps:
- uses: echo
with:
message: "Setting up environment"
- id: main
name: Main processing
needs: [setup]
steps:
- uses: echo
with:
message: "Processing on {{ .api_endpoint }}"
- id: cleanup
name: Cleanup tasks
needs: [main]
skipif: "{{ not .run_extra }}"
steps:
- uses: echo
with:
message: "Running cleanup"
retry:
max_attempts: 3
interval: 5s
The cleanup job depends on main, which depends on setup. The scheduler respects this chain via the JobScheduler. The cleanup job skips entirely because run_extra evaluates to false.
Running Probe from the Command Line
Execute your workflow using the probe CLI:
probe simple.yml # Standard execution with concise reporting
probe -v simple.yml # Verbose mode showing spinners and step details
The command internally invokes Probe.Load() followed by Workflow.Start(), rendering progress and final results through the printer interface.
Summary
- Probe workflows are defined in YAML files that map directly to the
Workflow,Job, andStepstructs in thelinyows/probesource code - Top-level
varssupport Go-template expansion viaWorkflow.evalVars()and become available to all jobs and steps - Jobs declare dependencies using the
needsfield, resolved by theJobSchedulerduringWorkflow.Start() - Steps support conditional execution via
skipif, retry logic via theretrymap, and output collection for downstream consumption - The execution pipeline moves from
Probe.Load()throughWorkflow.Start()to individualJob.Start()andStep.Do()calls
Frequently Asked Questions
How does Probe handle variable interpolation in YAML workflows?
Probe evaluates top-level vars immediately after loading via Workflow.evalVars() (lines 84-105 in workflow.go). These variables expand using Go-template syntax ({{ .varname }}) in job names, step parameters, and conditional expressions. The expansion occurs before job execution, ensuring all references resolve to literal values before Step.Do() processes the action.
What file format and validation does Probe require for workflow definitions?
Probe accepts files with .yml or .yaml extensions. The Probe.Load() function in probe.go (lines 72-90) unmarshals YAML into the Workflow struct and validates required fields using go-playground/validator. Invalid YAML syntax or missing required fields trigger validation errors before execution begins.
How do I configure retry logic for steps that may fail intermittently?
Add a retry configuration map to any step definition. According to step.go (lines 41-94), you can specify max_attempts, interval (as duration strings like "10s"), and initial_delay. The Step.executeActionWithRetry method implements the backoff logic, re-attempting the action until success or maximum attempts are exhausted.
Can I skip specific jobs or steps based on runtime conditions?
Yes. Both Job and Step structs support a skipif field accepting a Go-template boolean expression. In job.go (lines 19-48), Job.Start() evaluates the job-level expression before processing steps. Similarly, Step.Do() checks step-level skipif during preparation. If the expression evaluates to true, Probe omits that job or step from execution and continues with the remaining workflow.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →