probe

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 22, 2026 License: MIT Imports: 33 Imported by: 0

README

English | 日本語







PROBE







GitHub Workflow Status GitHub Release Go Documentation Deepwiki Documentation

A powerful YAML-based workflow automation tool designed for testing, monitoring, and automation tasks. Probe uses plugin-based actions to execute workflows, making it highly flexible and extensible.

Architecture

Quick Start

name: API Health Check
jobs:
- name: Check API Status
  steps:
  - name: Ping API
    uses: http
    with:
      url: https://api.example.com
      get: /health
    test: res.code == 200
probe health-check.yml

Features

  • Simple YAML Syntax: Easy-to-read workflow definitions
  • Plugin Architecture: Built-in HTTP, Database, Browser, Shell, SSH, SMTP, IMAP, and Hello actions with extensibility
  • Job Dependencies: Control execution order with needs
  • Step Outputs: Share data between steps and jobs using outputs
  • Repetition: Repeat jobs with configurable intervals
  • Iteration: Execute steps with different variable sets
  • Expression Engine: Powerful templating and condition evaluation
  • Testing Support: Built-in assertion system
  • Timing Controls: Wait conditions and delays
  • Rich Output: Buffered, consistent output formatting
  • Security: Safe expression evaluation with timeout protection

Installation

Using Go

go install github.com/linyows/probe/cmd/probe@latest

From Source

git clone https://github.com/linyows/probe.git
cd probe
go build -o probe ./cmd/probe

Usage

Basic Usage

# Run a workflow
probe ./workflow.yml

# Verbose output
probe ./workflow.yml --verbose

# Show response times
probe ./workflow.yml --rt

# Show job dependency graph as ASCII art
probe ./workflow.yml --dag-ascii

# Show job dependency graph in Mermaid format
probe ./workflow.yml --dag-mermaid

CLI Options

  • <workflow>: Specify YAML workflow file path
  • --verbose: Enable detailed output
  • --rt: Show response times
  • --dag-ascii: Display job dependency graph as ASCII art
  • --dag-mermaid: Display job dependency graph in Mermaid format
  • --help: Show help information

Workflow Syntax

Basic Structure

name: Workflow Name
description: Optional description
vars:
  global_var: value
jobs:
- name: Job Name
  steps:
  - name: Step Name
    uses: action_name
    with:
      param: value

Jobs

Jobs are executed in parallel by default. Use needs for dependencies:

jobs:
- name: Setup
  id: setup
  steps:
  - name: Initialize
    uses: http
    with:
      post: /setup

- name: Test
  needs: [setup]  # Wait for setup to complete
  steps:
  - name: Run test
    uses: http
    with:
      get: /test

Steps

Steps within a job execute sequentially:

steps:
- name: Login
  id: login
  uses: http
  with:
    post: /auth/login
    body:
      username: admin
      password: secret
  outputs:
    token: res.body.token

- name: Get Profile
  uses: http
  with:
    get: /profile
    headers:
      authorization: "Bearer {{outputs.login.token}}"
  test: res.code == 200

Variables and Expressions

Use {{expression}} syntax for dynamic values:

vars:
  api_url: https://api.example.com
  user_id: 123

steps:
- name: Get User
  uses: http
  with:
    url: "{{vars.api_url}}"
    get: "/users/{{vars.user_id}}"
  test: res.body.id == vars.user_id

Built-in Functions

test: |
  res.code == 200 &&
  match_json(res.body, vars.expected) &&
  random_int(10) > 5

Available functions:

  • match_json(actual, expected): JSON pattern matching with regex support
  • diff_json(actual, expected): Show differences between JSON objects
  • random_int(max): Generate random integer
  • random_str(length): Generate random string
  • unixtime(): Current Unix timestamp

Output Management

Share data between steps and jobs:

- name: Get Token
  id: auth
  uses: http
  with:
    post: /auth
  outputs:
    token: res.body.access_token
    expires: res.body.expires_in

- name: Use Token
  uses: http
  with:
    get: /protected
    headers:
      authorization: "Bearer {{outputs.auth.token}}"

Iteration

Execute steps with different variable sets:

- name: Test Multiple Users
  uses: http
  with:
    post: /users
    body:
      name: "{{vars.name}}"
      role: "{{vars.role}}"
  test: res.code == 201
  iteration:
  - {name: "Alice", role: "admin"}
  - {name: "Bob", role: "user"}
  - {name: "Carol", role: "editor"}

Job Repetition

Repeat entire jobs with intervals:

- name: Health Check
  repeat:
    count: 10
    interval: 30s
  steps:
  - name: Ping
    uses: http
    with:
      get: /health
    test: res.code == 200

Conditional Execution

Skip steps based on conditions:

- name: Conditional Step
  uses: http
  with:
    get: /api/data
  skipif: vars.skip_test == true
  test: res.code == 200

Timing and Delays

- name: Wait and Check
  uses: http
  with:
    get: /status
  wait: 5s  # Wait before execution
  test: res.code == 200

Built-in Actions

HTTP Action

- name: HTTP Request
  uses: http
  with:
    url: https://api.example.com
    # GET, POST, PUT, DELETE, etc.
    post: /
    headers:
      content-type: application/json
      authorization: Bearer token
    body:
      key: value
    timeout: 30s

SMTP Action

- name: Send Email
  uses: smtp
  with:
    addr: smtp.example.com:587
    from: sender@example.com
    to: recipient@example.com
    subject: Test Email
    body: Email content
    my-hostname: localhost

Database Action

- name: Database Query
  uses: db
  with:
    dsn: "mysql://user:password@localhost:3306/database"
    query: "SELECT * FROM users WHERE active = ?"
    params: [true]
    timeout: 30s
  test: res.code == 0 && res.rows_affected > 0

Supported databases:

  • MySQL: mysql://user:pass@host:port/database
  • PostgreSQL: postgres://user:pass@host:port/database?sslmode=disable
  • SQLite: file:./testdata/sqlite.db or /absolute/path/database

Browser Action

- name: Web Automation
  uses: browser
  with:
    action: navigate
    url: "https://example.com"
    headless: true
    timeout: 30s
  test: res.success == "true"

Supported actions:

  • navigate: Navigate to URL
  • text: Extract text content from elements
  • value: Get input field values
  • get_attribute: Get element attribute values
  • get_html: Extract HTML content from elements
  • click: Click on elements
  • double_click: Double-click on elements
  • right_click: Right-click on elements
  • hover: Hover over elements
  • focus: Set focus to elements
  • type / send_keys: Type text into input fields
  • select: Select dropdown options
  • submit: Submit forms
  • scroll: Scroll elements into view
  • screenshot: Capture element screenshots
  • capture_screenshot: Capture full page screenshots
  • full_screenshot: Capture full page screenshots with quality settings
  • wait_visible: Wait for elements to become visible
  • wait_not_visible: Wait for elements to become invisible
  • wait_ready: Wait for page to be ready
  • wait_text: Wait for specific text to appear
  • wait_enabled: Wait for elements to become enabled

Shell Action

- name: Run Build Script
  uses: shell
  with:
    cmd: "npm run build"
    workdir: "/app"
    shell: "/bin/bash"
    timeout: "5m"
    env:
      NODE_ENV: production
  test: res.code == 0 && (res.stdout | contains("Build successful"))

SSH Action

- name: Deploy Application
  uses: ssh
  with:
    host: "prod.example.com"
    port: 22
    user: "deploy"
    # Authentication methods (use either password or key_file)
    password: "secure_password"
    # key_file: "~/.ssh/deploy_key"
    # key_passphrase: "key_passphrase"  # if key is encrypted
    cmd: |
      cd /opt/myapp
      git pull origin main
      systemctl restart myapp
    timeout: "300s"
    workdir: "/opt/myapp"
    # Environment variables
    env:
      DEPLOY_ENV: production
      APP_VERSION: v1.2.3
    # Security settings
    strict_host_check: true
    known_hosts: "~/.ssh/known_hosts"
  test: res.code == 0 && !contains(res.stderr, "error")

IMAP Action

- name: Email Operations
  uses: imap
  with:
    host: "imap.example.com"
    port: 993
    username: "user@example.com"
    password: "password"
    tls: true
    timeout: 30s
    strict_host_check: true
    insecure_skip_tls: false
    commands:
    - name: "select"
      mailbox: "INBOX"
    - name: "search"
      criteria:
        since: "today"
        flags: ["unseen"]
    - name: "fetch"
      sequence: "*"
      dataitem: "ALL"
  test: res.code == 0 && res.data.search.count > 0

Supported IMAP commands:

  • select: Select a mailbox for read-write operations
  • examine: Select a mailbox for read-only operations
  • search: Search messages using criteria
  • uid search: Search using UID instead of sequence numbers
  • list: List available mailboxes
  • fetch: Fetch message data
  • uid fetch: Fetch using UID instead of sequence numbers
  • store: Store message flags (basic implementation)
  • uid store: Store using UID (basic implementation)
  • copy: Copy messages to another mailbox (basic implementation)
  • uid copy: Copy using UID (basic implementation)
  • create: Create a new mailbox
  • delete: Delete a mailbox
  • rename: Rename a mailbox
  • subscribe: Subscribe to a mailbox
  • unsubscribe: Unsubscribe from a mailbox
  • noop: No operation (keepalive)

Search criteria support:

  • seq_nums: Sequence number ranges (e.g., "1:10", "*")
  • uids: UID ranges for UID search commands
  • since: Messages received since date ("today", "yesterday", "2024-01-01", "1 hour ago")
  • before: Messages received before date
  • sent_since: Messages sent since date
  • sent_before: Messages sent before date
  • headers: Header field matches (e.g., {"from": "sender@example.com"})
  • bodies: Body text contains
  • texts: Text (headers + body) contains
  • flags: Message flags (e.g., ["seen", "answered"])
  • not_flags: Messages without these flags

Hello Action (Testing)

- name: Test Action
  uses: hello
  with:
    name: World

Advanced Examples

REST API Testing

name: User API Test
vars:
  base_url: https://api.example.com
  admin_token: "{{env.ADMIN_TOKEN}}"

jobs:
- name: User CRUD Operations
  defaults:
    http:
      url: "{{vars.base_url}}"
      headers:
        authorization: "Bearer {{vars.admin_token}}"
        content-type: application/json

  steps:
  - name: Create User
    id: create
    uses: http
    with:
      post: /users
      body:
        name: Test User
        email: test@example.com
    test: res.code == 201
    outputs:
      user_id: res.body.id

  - name: Get User
    uses: http
    with:
      get: "/users/{{outputs.create.user_id}}"
    test: |
      res.code == 200 &&
      res.body.name == "Test User"

  - name: Update User
    uses: http
    with:
      put: "/users/{{outputs.create.user_id}}"
      body:
        name: Updated User
    test: res.code == 200

  - name: Delete User
    uses: http
    with:
      delete: "/users/{{outputs.create.user_id}}"
    test: res.code == 204

Load Testing with Repetition

name: Load Test
jobs:
- name: Concurrent Requests
  repeat:
    count: 100
    interval: 100ms
  steps:
  - name: API Call
    uses: http
    with:
      url: https://api.example.com
      get: /endpoint
    test: res.code == 200

Multi-Service Integration

name: E2E Test
jobs:
- name: Setup Database
  id: db-setup
  steps:
  - name: Initialize
    uses: http
    with:
      post: http://db-service/init

- name: API Tests
  needs: [db-setup]
  steps:
  - name: Test API
    uses: http
    with:
      get: http://api-service/data
    test: res.code == 200

- name: Cleanup
  needs: [db-setup]
  steps:
  - name: Reset Database
    uses: http
    with:
      post: http://db-service/reset

Expression Reference

Context Variables

  • vars.*: Workflow and step variables (including environment variables defined in vars)
  • res.*: Previous step response
  • req.*: Previous step request
  • outputs.*: Step outputs

Response Object

res:
  code: 200
  status: "200 OK"
  headers:
    content-type: application/json
  body: {...}  # Parsed JSON or raw string
  rawbody: "..." # Original response body

Operators

  • Arithmetic: +, -, *, /, %
  • Comparison: ==, !=, <, <=, >, >=
  • Logical: &&, ||, !
  • Ternary: condition ? true_value : false_value

Extending Probe

Probe supports custom actions through Protocol Buffers. Create your own actions to extend functionality beyond the built-in HTTP, SMTP, and Hello actions.

Configuration

Environment Variables

  • Environment variables can be accessed by defining them in the root vars section

Defaults

Use YAML anchors for common configurations:

x_defaults: &api_defaults
  http:
    url: https://api.example.com
    headers:
      authorization: Bearer token

jobs:
- name: Test Job
  defaults: *api_defaults

Troubleshooting

Common Issues

Expression Evaluation Errors

  • Check syntax: {{expression}} not {expression}
  • Verify variable names and paths
  • Use quotes around string values

HTTP Action Issues

  • Verify URL format and accessibility
  • Check headers and authentication
  • Review timeout settings

Job Dependencies

  • Ensure job IDs are unique
  • Check needs references
  • Avoid circular dependencies

Debug Output

Use --verbose flag for detailed execution information:

probe test.yml --verbose

Best Practices

  1. Use descriptive names for workflows, jobs, and steps
  2. Leverage outputs for data sharing between steps
  3. Implement proper testing with meaningful assertions
  4. Use defaults to reduce repetition
  5. Structure workflows logically with clear dependencies
  6. Handle errors gracefully with appropriate tests
  7. Use variables for configuration management

FAQ

Common questions about Probe.

How is Probe different from Common Workflow Language (CWL) or Workflow Description Language (WDL)?

While CWL and WDL are powerful workflow languages designed for scientific computing and bioinformatics with complex data processing pipelines, Probe takes a different approach by specializing in web-based use cases.

Key Differences:

  • Target Use Cases: CWL focuses on scientific data processing with Docker-based distributed execution, while WDL targets genomics pipelines with strong type systems and cloud-based distributed computing. Probe is designed specifically for API monitoring, test automation, and operational tasks.
  • Internet Protocol Focus: Probe specializes in web use cases, providing built-in support for HTTP, SMTP, SSH, IMAP, and browser automation. This specialization allows you to define and execute workflows with minimal configuration for internet protocol-based tasks.
  • Simplicity vs. Flexibility: While Probe offers less general-purpose flexibility compared to CWL or WDL, this is an intentional design choice. By focusing on web and internet protocols, Probe dramatically improves productivity in its target domain. The plugin-based architecture using Protocol Buffers still allows for extensibility when needed.
  • Execution Model: Unlike CWL and WDL which are designed for distributed computing environments, Probe is optimized for lightweight, local execution with real-time monitoring capabilities.
  • Syntax: Probe uses intuitive YAML-based configuration instead of the more complex type systems found in CWL and WDL, making it accessible to operations teams and developers working with web services.

Probe's value lies in providing the most efficient path for web-based automation tasks while maintaining the ability to extend functionality through custom actions when necessary. For more examples and advanced usage, check the examples directory.

Contributing

We welcome contributions! Please feel free to submit issues, feature requests, or pull requests.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

linyows

Documentation

Index

Constants

View Source
const (
	// MaxLogStringLength is the maximum length for log output to prevent log bloat
	MaxLogStringLength = 200
	// MaxStringLength is the maximum length for general string processing
	MaxStringLength = 1000000
)
View Source
const (
	IconSuccess  = "✓ "
	IconError    = "✗ "
	IconTriangle = "△ "
	IconCircle   = "⏺"
	IconWait     = "🕐︎"
	IconSkip     = "⏭ "
)

Icon constants

View Source
const (
	// DefaultMaxRepeatCount is the default upper limit for repeat count
	DefaultMaxRepeatCount = 10000
	// DefaultMaxAttempts is the default upper limit for max attempts
	DefaultMaxAttempts = 10000
	// EnvMaxRepeatCount is the environment variable name for overriding max repeat count
	EnvMaxRepeatCount = "PROBE_MAX_REPEAT_COUNT"
	// EnvMaxAttempts is the environment variable name for overriding max attempts
	EnvMaxAttempts = "PROBE_MAX_ATTEMPTS"
)
View Source
const (
	// DefaultStepTimeout is the default timeout for action execution
	DefaultStepTimeout = 5 * time.Minute
)

Variables

View Source
var (
	BuiltinCmd = "builtin-actions"
	Handshake  = plugin.HandshakeConfig{ProtocolVersion: 1, MagicCookieKey: "probe", MagicCookieValue: "actions"}
	PluginMap  = map[string]plugin.Plugin{"actions": &ActionsPlugin{}}
)

Functions

func AnyToString added in v0.4.0

func AnyToString(value any) (string, bool)

AnyToString attempts to convert any type to a string. Returns the string representation and a boolean indicating success.

Example:

str, ok := AnyToString(42)        // "42", true
str, ok := AnyToString(3.14)      // "3.14", true
str, ok := AnyToString("hello")   // "hello", true
str, ok := AnyToString(nil)       // "nil", true
str, ok := AnyToString([]int{1})  // "", false

func AssignStruct

func AssignStruct(pa ActionsParams, st any) error

AssignStruct assigns values from an ActionsParams map to a struct using struct tags. Supports string and int fields with validation. Used for legacy action parameter assignment.

Example:

type Config struct {
  Name    string `map:"name" validate:"required"`
  Timeout int    `map:"timeout"`
}

params := ActionsParams{"name": "test", "timeout": "30"}
var config Config
err := AssignStruct(params, &config)
// config.Name = "test", config.Timeout = 30

func ConvertNumericStrings added in v0.12.0

func ConvertNumericStrings(data map[string]any) map[string]any

ConvertNumericStrings provides backward compatibility for numeric conversion

func DiffJSON added in v0.4.0

func DiffJSON(src, target map[string]any) string

DiffJSON compares two `map[string]any` objects strictly and collects differences.

func EnvMap added in v0.3.0

func EnvMap() map[string]string

EnvMap returns all environment variables as a map[string]string. Each environment variable is parsed from "KEY=VALUE" format.

Example:

env := EnvMap()
// env contains all environment variables like:
// {"PATH": "/usr/bin:/bin", "HOME": "/home/user", "USER": "username", ...}

func FromAnySlice added in v0.20.0

func FromAnySlice[T any](s []any) ([]T, error)

func GetTruncationMessage added in v0.9.0

func GetTruncationMessage() string

GetTruncationMessage returns a colored truncation message

func HeaderToStringValue added in v0.12.0

func HeaderToStringValue(data map[string]any) map[string]any

HeaderToStringValue converts header values to strings for HTTP processing. This function ensures all header values are strings, converting numbers and other types as needed.

Example:

data := map[string]any{
  "headers": map[string]any{
    "Content-Length": 1024,
    "X-Rate-Limit": 100.5,
    "Authorization": "Bearer token",
  },
}

result := HeaderToStringValue(data)
// result["headers"] = map[string]any{
//   "Content-Length": "1024",
//   "X-Rate-Limit": "100.5",
//   "Authorization": "Bearer token",
// }

func IsTextualMimeType added in v0.21.0

func IsTextualMimeType(contentType string) bool

IsTextualMimeType determines if the given MIME type represents textual data

func MapToStruct added in v0.21.0

func MapToStruct(m map[string]any) (*structpb.Struct, error)

MapToStruct converts a map[string]any to a protobuf Struct

func MapToStructByTags

func MapToStructByTags(params map[string]any, dest any) error

MapToStructByTags converts a map[string]any to a struct using struct tags. Fields are mapped using the "map" tag, and validation is performed using the "validate" tag. Supports nested structs, []byte fields, and map[string]string fields.

Example:

type User struct {
  Name     string            `map:"name" validate:"required"`
  Age      int               `map:"age"`
  Metadata map[string]string `map:"metadata"`
}

params := map[string]any{
  "name": "John",
  "age": 30,
  "metadata": map[string]any{"role": "admin", "dept": "IT"},
}

var user User
err := MapToStructByTags(params, &user)
// user.Name = "John", user.Age = 30, user.Metadata = {"role": "admin", "dept": "IT"}

func MatchJSON added in v0.4.0

func MatchJSON(src, target map[string]any) bool

MatchJSON compares two `map[string]any` objects strictly. All fields in `src` and `target` must match, including structure and values.

func MergeMaps added in v0.4.0

func MergeMaps(base, over map[string]any) map[string]any

MergeMaps recursively merges two maps of type map[string]any. If keys conflict, values from 'over' override those in 'base'. Nested maps are merged recursively, preserving data from both maps.

Example:

base := map[string]any{
  "a": 1,
  "nested": map[string]any{"x": 1, "y": 2},
}
over := map[string]any{
  "nested": map[string]any{"y": 3, "z": 4},
  "c": 5,
}
result := MergeMaps(base, over)
// result: map[string]any{
//   "a": 1,
//   "nested": map[string]any{"x": 1, "y": 3, "z": 4},
//   "c": 5,
// }

func MergeStringMaps

func MergeStringMaps(base map[string]string, over map[string]any) map[string]string

MergeStringMaps merges two string maps, where values from 'over' override values from 'base'. Only string values from 'over' are included; non-string values are ignored.

Example:

base := map[string]string{"a": "1", "b": "2"}
over := map[string]any{"b": "overridden", "c": "3", "d": 123}
result := MergeStringMaps(base, over)
// result: map[string]string{"a": "1", "b": "overridden", "c": "3"}
// Note: "d": 123 is ignored because it's not a string

func ProcessHttpBody added in v0.21.0

func ProcessHttpBody(data []byte, contentType string) (string, string, error)

ProcessHttpBody processes HTTP body data based on Content-Type Returns (bodyString, filePath, error)

func SaveBinaryToTempFile added in v0.21.0

func SaveBinaryToTempFile(data []byte, contentType string) (string, error)

SaveBinaryToTempFile saves binary data to a temporary file and returns the file path

func StrmapToAnymap added in v0.3.0

func StrmapToAnymap(strmap map[string]string) map[string]any

StrmapToAnymap converts a map[string]string to map[string]any. This is a simple type conversion utility function.

Example:

input := map[string]string{"name": "John", "age": "30"}
result := StrmapToAnymap(input)
// result: map[string]any{"name": "John", "age": "30"}

func StructToMap added in v0.21.0

func StructToMap(s *structpb.Struct) map[string]any

StructToMap converts a protobuf Struct to a map[string]any

func StructToMapByTags

func StructToMapByTags(src any) (map[string]any, error)

StructToMapByTags converts a struct to a map[string]any using struct tags. Fields are mapped using the "map" tag. Supports nested structs, []byte fields, and map[string]string fields. This is the inverse operation of MapToStructByTags.

Example:

type User struct {
  Name     string            `map:"name"`
  Age      int               `map:"age"`
  Metadata map[string]string `map:"metadata"`
}

user := User{
  Name: "John",
  Age: 30,
  Metadata: map[string]string{"role": "admin", "dept": "IT"},
}

result, err := StructToMapByTags(user)
// result: map[string]any{
//   "name": "John",
//   "age": 30,
//   "metadata": map[string]string{"role": "admin", "dept": "IT"},
// }

func TitleCase

func TitleCase(st string, char string) string

TitleCase converts a string to title case using a specified separator character. Each part separated by the character has its first letter capitalized.

Example:

TitleCase("content-type", "-")     // "Content-Type"
TitleCase("user_name", "_")        // "User_Name"
TitleCase("hello-world-test", "-") // "Hello-World-Test"

func ToAnySlice added in v0.20.0

func ToAnySlice[T any](s []T) []any

func TruncateMapStringAny added in v0.21.0

func TruncateMapStringAny(params map[string]any, maxLen int) map[string]any

TruncateMapStringAny truncates long values in map[string]any for logging

func TruncateMapStringString added in v0.9.0

func TruncateMapStringString(params map[string]string, maxLen int) map[string]string

TruncateMapStringString truncates long values in map[string]string for logging

func TruncateString added in v0.9.0

func TruncateString(s string, maxLen int) string

TruncateString truncates a string if it exceeds the maximum length

Types

type ActionRunner added in v0.18.0

type ActionRunner interface {
	RunActions(name string, args []string, with map[string]any, verbose bool) (map[string]any, error)
}

ActionRunner defines the interface for running actions

type Actions

type Actions interface {
	Run(args []string, with map[string]any) (map[string]any, error)
}

type ActionsArgs

type ActionsArgs []string

type ActionsClient

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

func (*ActionsClient) Run

func (m *ActionsClient) Run(args []string, with map[string]any) (map[string]any, error)

type ActionsParams

type ActionsParams map[string]any

type ActionsPlugin

type ActionsPlugin struct {
	plugin.Plugin
	Impl Actions
}

func (*ActionsPlugin) GRPCClient

func (p *ActionsPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (any, error)

func (*ActionsPlugin) GRPCServer

func (p *ActionsPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error

type ActionsServer

type ActionsServer struct {
	Impl Actions
	// contains filtered or unexported fields
}

func (*ActionsServer) Run

func (m *ActionsServer) Run(ctx context.Context, req *pb.RunRequest) (*pb.RunResponse, error)

type Config

type Config struct {
	Log     io.Writer
	Verbose bool
	RT      bool
}

type DagAsciiJobNode added in v1.0.0

type DagAsciiJobNode struct {
	Job         *Job
	JobID       string
	Level       int      // Depth in DAG (0 = root)
	Width       int      // Box width
	Lines       []string // Rendered lines
	CenterX     int      // X coordinate of center (for connections)
	HasChildren bool     // Whether this job has dependent jobs
}

DagAsciiJobNode represents a rendered job node

type DagAsciiRenderer added in v1.0.0

type DagAsciiRenderer struct {
	DagRendererBase
	// contains filtered or unexported fields
}

DagAsciiRenderer renders detailed workflow graphs with job nodes and steps

func NewDagAsciiRenderer added in v1.0.0

func NewDagAsciiRenderer(w *Workflow) *DagAsciiRenderer

NewDagAsciiRenderer creates a new DagAsciiRenderer

func (*DagAsciiRenderer) Render added in v1.0.0

func (r *DagAsciiRenderer) Render() string

Render generates the detailed ASCII art graph

type DagMermaidRenderer added in v1.0.0

type DagMermaidRenderer struct {
	DagRendererBase
	// contains filtered or unexported fields
}

DagMermaidRenderer renders workflow graphs in Mermaid format

func NewDagMermaidRenderer added in v1.0.0

func NewDagMermaidRenderer(w *Workflow) *DagMermaidRenderer

NewDagMermaidRenderer creates a new DagMermaidRenderer

func (*DagMermaidRenderer) Render added in v1.0.0

func (r *DagMermaidRenderer) Render() string

Render generates the Mermaid flowchart representation

type DagRenderer added in v1.1.0

type DagRenderer interface {
	Render() string
}

DagRenderer is the interface for DAG rendering

type DagRendererBase added in v1.1.0

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

DagRendererBase provides common functionality for DAG renderers

func NewDagRendererBase added in v1.1.0

func NewDagRendererBase(w *Workflow) DagRendererBase

NewDagRendererBase creates a new DagRendererBase

func (*DagRendererBase) ExpandPath added in v1.1.0

func (b *DagRendererBase) ExpandPath(path string) string

ExpandPath expands template variables in the path using evaluated workflow vars

func (*DagRendererBase) GetEvaluatedVars added in v1.1.0

func (b *DagRendererBase) GetEvaluatedVars() map[string]any

GetEvaluatedVars returns evaluated vars with lazy loading

func (*DagRendererBase) ResolvePath added in v1.1.0

func (b *DagRendererBase) ResolvePath(path string) string

ResolvePath resolves a (potentially relative) path using a fixed priority order:

  1. If the path is absolute, it is returned as-is.
  2. If workflow.basePath is set, first try the path relative to the workflow directory (workflow.basePath/path).
  3. If not found, try the path relative to the parent of the workflow directory (for project-root relative paths; parentDir/path).
  4. If still not found, fall back to resolving the path from the current working directory using filepath.Abs, which matches the runtime's default behavior.

type ErrorType added in v0.7.0

type ErrorType string

ErrorType represents different categories of errors

const (
	ErrorTypeValidation    ErrorType = "validation"
	ErrorTypeExecution     ErrorType = "execution"
	ErrorTypeConfiguration ErrorType = "configuration"
	ErrorTypeNetwork       ErrorType = "network"
	ErrorTypeFile          ErrorType = "file"
	ErrorTypeAction        ErrorType = "action"
	ErrorTypeDependency    ErrorType = "dependency"
)

type Executor added in v0.8.0

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

Executor handles job execution with buffered output

func NewExecutor added in v0.8.0

func NewExecutor(w *Workflow, job *Job) *Executor

NewExecutor creates a new job executor

func (*Executor) Execute added in v0.8.0

func (e *Executor) Execute(ctx JobContext) bool

Execute runs a job with buffered output

type ExitStatus added in v0.19.0

type ExitStatus int

ExitStatus represents the unified status across all actions

const (
	ExitStatusSuccess ExitStatus = 0 // Action succeeded
	ExitStatusFailure ExitStatus = 1 // Action failed
)

func (ExitStatus) Int added in v0.19.0

func (e ExitStatus) Int() int

Int returns the ExitStatus as an int for comparison

func (ExitStatus) String added in v0.19.0

func (e ExitStatus) String() string

String returns the ExitStatus as a string

type Expr added in v0.2.0

type Expr struct{}

func (*Expr) Eval added in v0.3.0

func (e *Expr) Eval(input string, env any) (any, error)

func (*Expr) EvalOrEvalTemplate added in v0.4.0

func (e *Expr) EvalOrEvalTemplate(input string, env any) (string, error)

func (*Expr) EvalTemplate added in v0.2.0

func (e *Expr) EvalTemplate(input string, env any) (string, error)

func (*Expr) EvalTemplateMap added in v0.4.0

func (e *Expr) EvalTemplateMap(input map[string]any, env any) map[string]any

func (*Expr) EvalTemplateWithTypePreservation added in v0.21.0

func (e *Expr) EvalTemplateWithTypePreservation(input string, env any) (any, error)

func (*Expr) Options added in v0.4.0

func (e *Expr) Options(env any) []ex.Option

type Interval added in v0.6.0

type Interval struct {
	time.Duration
}

Interval represents a time interval that can be specified as a number (seconds) or duration string

func (Interval) MarshalYAML added in v0.6.0

func (i Interval) MarshalYAML() (interface{}, error)

MarshalYAML implements custom YAML marshaling for Interval

func (*Interval) UnmarshalYAML added in v0.6.0

func (i *Interval) UnmarshalYAML(unmarshal func(interface{}) error) error

UnmarshalYAML implements custom YAML unmarshaling for Interval

type Job

type Job struct {
	Name     string   `yaml:"name" validate:"required"`
	ID       string   `yaml:"id,omitempty"`
	Needs    []string `yaml:"needs,omitempty"`
	Steps    []*Step  `yaml:"steps" validate:"required"`
	Repeat   *Repeat  `yaml:"repeat"`
	Defaults any      `yaml:"defaults"`
	SkipIf   string   `yaml:"skipif,omitempty"`
}

func LoadEmbeddedJob added in v1.1.0

func LoadEmbeddedJob(path string) (*Job, error)

LoadEmbeddedJob loads a job definition from an embedded YAML file

func (*Job) RunIndependently added in v0.18.0

func (j *Job) RunIndependently(vars map[string]any, printer *Printer, jobID string) (bool, map[string]any, string, string, time.Duration)

RunIndependently executes a job independently with its own context and result tracking Returns success/failure status, outputs, report, error message, and duration

func (*Job) Start

func (j *Job) Start(ctx JobContext) error

type JobContext

type JobContext struct {
	Vars map[string]any `expr:"vars"`
	Config
	Failed bool
	// Current job ID for this context
	CurrentJobID string
	// Repeat tracking
	IsRepeating   bool
	RepeatCurrent int
	RepeatTotal   int
	StepCounters  map[int]StepRepeatCounter // step index -> counter

	// Print writer
	Printer *Printer
	// Result for managing job-level output
	Result *Result
	// Job scheduler for managing job dependencies and execution
	JobScheduler *JobScheduler
	// Shared outputs across all jobs (accessible via expressions as "outputs")
	Outputs *Outputs `expr:"outputs"`
	// contains filtered or unexported fields
}

JobContext provides context data for job execution

func (*JobContext) SetFailed added in v0.2.0

func (j *JobContext) SetFailed()

SetFailed marks the job context as failed

type JobResult added in v0.8.0

type JobResult struct {
	JobName     string
	JobID       string
	Status      string
	StartTime   time.Time
	EndTime     time.Time
	Success     bool
	StepResults []StepResult // Store all step results for this job
	// contains filtered or unexported fields
}

JobResult stores execution results for a job

type JobScheduler added in v0.5.0

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

func NewJobScheduler added in v0.5.0

func NewJobScheduler() *JobScheduler

func (*JobScheduler) AddJob added in v0.5.0

func (js *JobScheduler) AddJob(job *Job) error

func (*JobScheduler) AllJobsCompleted added in v0.5.0

func (js *JobScheduler) AllJobsCompleted() bool

func (*JobScheduler) CanRunJob added in v0.5.0

func (js *JobScheduler) CanRunJob(jobID string) bool

func (*JobScheduler) GetRepeatInfo added in v0.5.0

func (js *JobScheduler) GetRepeatInfo(jobID string) (current, target int)

GetRepeatInfo returns current repeat counter and target for a job

func (*JobScheduler) GetRunnableJobs added in v0.5.0

func (js *JobScheduler) GetRunnableJobs() []string

func (*JobScheduler) IncrementRepeatCounter added in v0.5.0

func (js *JobScheduler) IncrementRepeatCounter(jobID string)

IncrementRepeatCounter increments the repeat counter for a job

func (*JobScheduler) MarkJobsWithFailedDependencies added in v0.6.0

func (js *JobScheduler) MarkJobsWithFailedDependencies() []string

MarkJobsWithFailedDependencies marks jobs as failed if their dependencies have failed

func (*JobScheduler) SetJobStatus added in v0.5.0

func (js *JobScheduler) SetJobStatus(jobID string, status JobStatus, success bool)

func (*JobScheduler) ShouldRepeatJob added in v0.5.0

func (js *JobScheduler) ShouldRepeatJob(jobID string) bool

ShouldRepeatJob checks if a job should be repeated

func (*JobScheduler) ValidateDependencies added in v0.5.0

func (js *JobScheduler) ValidateDependencies() error

type JobStatus added in v0.5.0

type JobStatus int
const (
	JobPending JobStatus = iota
	JobRunning
	JobCompleted
	JobFailed
)

type LogLevel added in v0.7.0

type LogLevel int

LogLevel defines different logging levels

const (
	LogLevelDebug LogLevel = iota
	LogLevelInfo
	LogLevelWarn
	LogLevelError
)

type MockActionRunner added in v0.18.0

type MockActionRunner struct {
	Results map[string]map[string]any
	Errors  map[string]error
}

MockActionRunner implements ActionRunner for testing

func NewMockActionRunner added in v0.18.0

func NewMockActionRunner() *MockActionRunner

NewMockActionRunner creates a new mock action runner

func (*MockActionRunner) RunActions added in v0.18.0

func (m *MockActionRunner) RunActions(name string, args []string, with map[string]any, verbose bool) (map[string]any, error)

RunActions returns the mocked result or error for the given action

func (*MockActionRunner) SetError added in v0.18.0

func (m *MockActionRunner) SetError(actionName string, err error)

SetError sets the expected error for an action

func (*MockActionRunner) SetResult added in v0.18.0

func (m *MockActionRunner) SetResult(actionName string, result map[string]any)

SetResult sets the expected result for an action

type Outputs added in v0.7.0

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

Outputs manages step outputs across the entire workflow

func NewOutputs added in v0.7.0

func NewOutputs() *Outputs

NewOutputs creates a new Outputs instance

func (*Outputs) Get added in v0.7.0

func (o *Outputs) Get(stepID string) (map[string]any, bool)

Get retrieves outputs for a step (existing functionality)

func (*Outputs) GetAll added in v0.7.0

func (o *Outputs) GetAll() map[string]any

GetAll returns all outputs (safe copy for expression evaluation)

func (*Outputs) GetAllWithFlat added in v0.15.0

func (o *Outputs) GetAllWithFlat() map[string]any

GetAllWithFlat returns all outputs including flat access for expression evaluation

func (*Outputs) GetConflicts added in v0.15.0

func (o *Outputs) GetConflicts() map[string][]string

GetConflicts returns information about conflicted output names

func (*Outputs) GetFlat added in v0.15.0

func (o *Outputs) GetFlat(outputName string) (any, bool)

GetFlat retrieves output by name directly (new functionality)

func (*Outputs) Set added in v0.7.0

func (o *Outputs) Set(stepID string, outputs map[string]any) error

Set stores outputs for a step with flat access support

type PluginActionRunner added in v0.18.0

type PluginActionRunner struct{}

PluginActionRunner implements ActionRunner using the plugin system

func (*PluginActionRunner) RunActions added in v0.18.0

func (p *PluginActionRunner) RunActions(name string, args []string, with map[string]any, verbose bool) (map[string]any, error)

RunActions executes an action using the plugin system

type Printer added in v0.7.0

type Printer struct {
	Buffer    map[string]*strings.Builder
	BufferIDs []string // Order preservation
	// contains filtered or unexported fields
}

Printer implements PrintWriter for console print

func NewPrinter added in v0.7.0

func NewPrinter(verbose bool, bufferIDs []string) *Printer

NewPrinter creates a new console print writer

func (*Printer) AddSpinnerSuffix added in v0.8.0

func (p *Printer) AddSpinnerSuffix(txt string)

func (*Printer) Fprint added in v0.18.0

func (p *Printer) Fprint(w io.Writer, a ...any)

func (*Printer) Fprintf added in v0.18.0

func (p *Printer) Fprintf(w io.Writer, f string, a ...any)

func (*Printer) Fprintln added in v0.18.0

func (p *Printer) Fprintln(w io.Writer, a ...any)

func (*Printer) GenerateReport added in v0.15.0

func (p *Printer) GenerateReport(rs *Result) string

GenerateReport generates a complete workflow report string using Result data

func (*Printer) GenerateReportOnlySteps added in v0.16.2

func (p *Printer) GenerateReportOnlySteps(rs *Result) string

func (*Printer) LogDebug added in v0.7.0

func (p *Printer) LogDebug(format string, args ...interface{})

LogDebug prints debug messages (only in verbose mode)

func (*Printer) LogError added in v0.7.0

func (p *Printer) LogError(format string, args ...interface{})

LogError prints error messages to stderr

func (*Printer) PrintEchoContent added in v0.10.0

func (p *Printer) PrintEchoContent(content string)

PrintEchoContent prints echo content with proper indentation in verbose mode

func (*Printer) PrintError added in v0.7.0

func (p *Printer) PrintError(format string, args ...interface{})

PrintError prints an error message

func (*Printer) PrintHeader added in v0.8.0

func (p *Printer) PrintHeader(name, description string)

PrintHeader prints the workflow name and description

func (*Printer) PrintMapData added in v0.10.0

func (p *Printer) PrintMapData(data map[string]any)

PrintMapData prints map data with proper formatting for nested structures

func (*Printer) PrintReport added in v0.8.0

func (p *Printer) PrintReport(rs *Result)

PrintReport prints a complete workflow report using Result data

func (*Printer) PrintRequestResponse added in v0.10.0

func (p *Printer) PrintRequestResponse(stepIdx int, stepName string, req, res map[string]any, rt string)

PrintRequestResponse prints request and response data with proper formatting

func (*Printer) PrintSeparator added in v0.7.0

func (p *Printer) PrintSeparator()

PrintSeparator prints a separator line for verbose output

func (*Printer) PrintTestResult added in v0.10.0

func (p *Printer) PrintTestResult(success bool, testExpr string, context interface{})

PrintTestResult prints test result in verbose mode

func (*Printer) PrintVerbose added in v0.7.0

func (p *Printer) PrintVerbose(format string, args ...interface{})

PrintVerbose prints verbose output (only if verbose mode is enabled)

func (*Printer) StartSpinner added in v0.8.0

func (p *Printer) StartSpinner()

func (*Printer) StopSpinner added in v0.8.0

func (p *Printer) StopSpinner()

type Probe

type Probe struct {
	FilePath string

	Config Config
	// contains filtered or unexported fields
}

func New

func New(path string, v bool) *Probe

func (*Probe) DagAscii added in v1.0.0

func (p *Probe) DagAscii() (string, error)

DagAscii returns the ASCII art representation of the workflow job dependencies with steps

func (*Probe) DagMermaid added in v1.0.0

func (p *Probe) DagMermaid() (string, error)

DagMermaid returns the Mermaid format representation of the workflow job dependencies

func (*Probe) Do

func (p *Probe) Do() error

func (*Probe) ExitStatus added in v0.2.0

func (p *Probe) ExitStatus() int

func (*Probe) Load

func (p *Probe) Load() error

type ProbeError added in v0.7.0

type ProbeError struct {
	Type      ErrorType
	Operation string
	Message   string
	Cause     error
	Context   map[string]interface{}
}

ProbeError is the base error type with context information

func NewActionError added in v0.7.0

func NewActionError(operation, message string, cause error) *ProbeError

func NewConfigurationError added in v0.7.0

func NewConfigurationError(operation, message string, cause error) *ProbeError

func NewExecutionError added in v0.7.0

func NewExecutionError(operation, message string, cause error) *ProbeError

func NewFileError added in v0.7.0

func NewFileError(operation, message string, cause error) *ProbeError

func NewProbeError added in v0.7.0

func NewProbeError(errorType ErrorType, operation, message string, cause error) *ProbeError

NewProbeError creates a new ProbeError

func (*ProbeError) Error added in v0.7.0

func (e *ProbeError) Error() string

func (*ProbeError) Is added in v0.7.0

func (e *ProbeError) Is(target error) bool

func (*ProbeError) Unwrap added in v0.7.0

func (e *ProbeError) Unwrap() error

func (*ProbeError) WithContext added in v0.7.0

func (e *ProbeError) WithContext(key string, value interface{}) *ProbeError

WithContext adds context information to the error

type Repeat

type Repeat struct {
	Count    int      `yaml:"count" validate:"required,gte=0"`
	Interval Interval `yaml:"interval"`
	Async    bool     `yaml:"async,omitempty"`
}

Repeat defines the repeat configuration for jobs

func (*Repeat) Validate added in v0.25.0

func (r *Repeat) Validate() error

Validate performs custom validation for Repeat

type ResponseTime added in v0.18.0

type ResponseTime struct {
	Duration string  `expr:"duration"`
	Sec      float64 `expr:"sec"`
}

ResponseTime provides response time information for expressions

type Result added in v0.8.0

type Result struct {
	Jobs map[string]*JobResult
}

Result manages execution results for multiple jobs

func NewResult added in v0.8.0

func NewResult() *Result

NewResult creates a new Result instance

func (*Result) AddStepResult added in v0.8.0

func (rs *Result) AddStepResult(jobID string, stepResult StepResult)

AddStepResult adds a StepResult to the specified job result

type StatusType added in v0.6.0

type StatusType int

StatusType represents the status of execution

const (
	StatusSuccess StatusType = iota
	StatusError
	StatusWarning
	StatusSkipped
)

type Step

type Step struct {
	Name      string            `yaml:"name"`
	ID        string            `yaml:"id,omitempty"`
	Uses      string            `yaml:"uses" validate:"required"`
	With      map[string]any    `yaml:"with"`
	Test      string            `yaml:"test"`
	Echo      string            `yaml:"echo"`
	Vars      map[string]any    `yaml:"vars"`
	Iteration []map[string]any  `yaml:"iteration"`
	Wait      string            `yaml:"wait,omitempty"`
	SkipIf    string            `yaml:"skipif,omitempty"`
	Outputs   map[string]string `yaml:"outputs,omitempty"`
	Retry     *StepRetry        `yaml:"retry,omitempty"`
	Timeout   Interval          `yaml:"timeout,omitempty"`

	Idx  int   `yaml:"-"`
	Expr *Expr `yaml:"-"`
	// contains filtered or unexported fields
}

func (*Step) Do added in v0.5.0

func (st *Step) Do(jCtx *JobContext)

func (*Step) DoEcho added in v0.5.0

func (st *Step) DoEcho(jCtx *JobContext)

func (*Step) DoTest added in v0.5.0

func (st *Step) DoTest(printer *Printer) (string, bool)

func (*Step) SetCtx added in v0.4.0

func (st *Step) SetCtx(j JobContext, override map[string]any)

type StepContext added in v0.5.0

type StepContext struct {
	Vars        map[string]any `expr:"vars"`
	Res         map[string]any `expr:"res"`
	Req         map[string]any `expr:"req"`
	RT          ResponseTime   `expr:"rt"`
	Report      string         `expr:"report"`
	Outputs     map[string]any `expr:"outputs"`
	Status      int            `expr:"status"`
	RepeatIndex int            `expr:"repeat_index"`
}

StepContext provides context data for step expression evaluation

type StepRepeatCounter added in v0.6.0

type StepRepeatCounter struct {
	SuccessCount int
	FailureCount int
	Name         string
	LastResult   bool
	RepeatTotal  int // Total number of times the step should be repeated
}

StepRepeatCounter tracks the execution results of repeated steps

type StepResult added in v0.6.0

type StepResult struct {
	Index         int
	Name          string
	Status        StatusType
	RT            string
	RTSec         float64
	WaitTime      string
	TestOutput    string
	EchoOutput    string
	Report        string
	HasTest       bool
	RepeatCounter *StepRepeatCounter // For repeat execution information
}

StepResult represents the result of a step execution

type StepRetry added in v0.19.0

type StepRetry struct {
	MaxAttempts  int      `yaml:"max_attempts" validate:"required,gte=1"`
	Interval     Interval `yaml:"interval"`
	InitialDelay Interval `yaml:"initial_delay,omitempty"`
}

StepRetry defines the retry configuration for steps until success (status 0)

func (*StepRetry) Validate added in v0.25.0

func (s *StepRetry) Validate() error

Validate performs custom validation for StepRetry

type ValidationError

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

ValidationError for validation-specific errors

func (*ValidationError) AddMessage

func (e *ValidationError) AddMessage(s string)

func (*ValidationError) Error

func (e *ValidationError) Error() string

func (*ValidationError) HasError

func (e *ValidationError) HasError() bool

type Workflow

type Workflow struct {
	Name        string         `yaml:"name" validate:"required"`
	Description string         `yaml:"description,omitempty"`
	Jobs        []Job          `yaml:"jobs" validate:"required"`
	Vars        map[string]any `yaml:"vars"`
	// contains filtered or unexported fields
}

func (*Workflow) Env added in v0.3.0

func (w *Workflow) Env() map[string]string

func (*Workflow) RenderDagAscii added in v1.0.0

func (w *Workflow) RenderDagAscii() string

RenderDagAscii renders the workflow job dependencies as ASCII art with steps

func (*Workflow) RenderDagMermaid added in v1.0.0

func (w *Workflow) RenderDagMermaid() string

RenderDagMermaid returns the Mermaid format representation of workflow job dependencies

func (*Workflow) SetExitStatus added in v0.2.0

func (w *Workflow) SetExitStatus(isErr bool)

func (*Workflow) Start

func (w *Workflow) Start(c Config) error

Start executes the workflow with the given configuration

Directories

Path Synopsis
actions
db
ssh
Package asciidag provides a zero-dependency ASCII DAG rendering engine.
Package asciidag provides a zero-dependency ASCII DAG rendering engine.
cmd
probe command
Package dag provides generic DAG algorithms that work with any data structure.
Package dag provides generic DAG algorithms that work with any data structure.
testserver command
testserver command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL