porch is a sophisticated Go-based process orchestration framework designed for running and managing complex command workflows. It provides a flexible, YAML-driven approach to define, compose, and execute command chains with advanced flow control, parallel processing, and comprehensive error handling.
It was designed to solve the problem of orchestrating complex command workflows in a portable and easy-to-use manner. Its portability means that you can confidently run the same workflows locally, in CI/CD pipelines, or on any server without worrying about compatibility issues.
- Serial Execution: Run commands sequentially with dependency management
- Parallel Execution: Execute independent commands concurrently for optimal performance
- Nested Batches: Compose complex workflows with serial and parallel command combinations
- Shell Commands: Execute any shell command with full environment control
- Directory Iteration: Execute commands across multiple directories with flexible traversal options
- YAML-Based Workflows: Define command chains using simple, readable YAML syntax
- Working Directory Management: Control execution context with per-command working directories
- Environment Variables: Set and inherit environment variables at any level
- Conditional Execution: Run commands based on success, failure, or specific exit codes
- Command Groups: Define reusable command sets that can be referenced by container commands
- Graceful Signal Handling: Proper SIGINT/SIGTERM handling with graceful shutdown
- Context-Aware Execution: Full context propagation for cancellation and timeouts
- Comprehensive Results: Detailed execution results with exit codes, stdout, and stderr
- Error Aggregation: Collect and report errors across complex command hierarchies
- Skip Controls: Configure commands to skip remaining tasks based on exit codes
- TUI: Real-time terminal user interface for live command progress monitoring
- Tree Visualization: Clear hierarchical display of command execution
- Colorized Output: Terminal-aware colored output for better readability
- Structured Results: JSON and pretty-printed result formatting
- Progress Tracking: Real-time execution status and progress indication
- Plugin-Based Commands: Modular command registry for easy extension
- Custom Commanders: Implement your own command types via the Commander interface
- Provider System: Extensible item providers for dynamic command generation
- Temporary Directory Support: Isolated execution environments for testing and builds
Porch currently supports the following operating systems:
- Linux
- macOS
- Windows
go install github.com/matt-FFFFFF/porch@latest
git clone https://github.com/matt-FFFFFF/porch.git
cd porch
make build
See DEVELOPER.md for detailed instructions on setting up a development environment, running tests, and contributing to the project.
Create a YAML file defining your command workflow:
name: "Build and Test Workflow"
description: "Complete CI/CD pipeline example"
commands:
- type: "shell"
name: "Setup Environment"
command_line: "echo 'Setting up build environment...'"
- type: "parallel"
name: "Quality Checks"
commands:
- type: "shell"
name: "Run Tests"
command_line: "go test ./..."
- type: "shell"
name: "Run Linter"
command_line: "golangci-lint run"
- type: "shell"
name: "Security Scan"
command_line: "gosec ./..."
- type: "serial"
name: "Build and Package"
commands:
- type: "shell"
name: "Build Application"
command_line: "go build -o app ."
- type: "copycwdtotemp"
name: "Copy to Temp"
- type: "shell"
name: "Create Archive"
command_line: "tar -czf app.tar.gz app"
porch run -f workflow.yaml
porch show results
porch provides a comprehensive CLI with the following commands:
Execute a workflow defined in a YAML file.
Usage:
# Execute workflow
porch run --file workflow.yaml
# Execute and save results
porch run --file workflow.yaml --out results
# Execute from a remote repository
porch run --file git:://github.com/yourusername/yourrepo/workflow.yaml --out results
# Execute multiple workflows (in series)
porch run --file workflow1.yaml --file workflow2.yaml --out results
Options:
--output-success-details
,--success
: Include successful results in the output--no-output-stderr
,--no-stderr
: Exclude stderr output in the results--output-stdout
,--stdout
: Include stdout output in the results
Description:
Reads a YAML workflow definition, executes commands according to their type (serial, parallel, shell, etc.), handles signal interruption gracefully, and optionally saves execution results for later analysis.
Display the results of a previous workflow execution.
Usage:
porch show results
Options:
--output-success-details
,--success
: Include successful results in the output--no-output-stderr
,--no-stderr
: Exclude stderr output in the results--output-stdout
,--stdout
: Include stdout output in the results
Description:
Displays saved execution results with pretty-printed tree visualization, colorized output with error highlighting, detailed execution metrics, and supports JSON export capability.
Get information about configuration format and available commands.
Usage:
porch config # List all available commands
porch config shell # Show shell command schema and example
porch config serial --markdown # Show serial command docs in Markdown format
Options:
--markdown
,--md
: Output the configuration example in Markdown format
Available Commands:
porch config schema
: Output the complete JSON schema for configuration files
Description:
Provides comprehensive documentation for each command type, including YAML schema definitions, examples, and detailed descriptions of all available configuration options.
Every porch workflow starts with a root configuration that defines the overall structure:
name: "Workflow Name" # Required: Descriptive name for the workflow
description: "Workflow description" # Optional: Description of what this workflow does
commands: [] # Required: List of commands to execute
command_groups: [] # Optional: Named groups of commands for reuse
Command groups allow you to define reusable sets of commands that can be referenced by container commands:
command_groups:
- name: "test-group"
description: "Common testing commands"
commands:
- type: "shell"
name: "Unit Tests"
command_line: "go test ./..."
- type: "shell"
name: "Integration Tests"
command_line: "go test -tags=integration ./..."
porch supports five built-in command types for different execution patterns:
Execute any shell command with full environment control and configurable exit code handling.
Required Attributes:
type: "shell"
name
: Descriptive name for the commandcommand_line
: The shell command to execute
Optional Attributes:
working_directory
: Directory to execute the command inenv
: Environment variables as key-value pairsruns_on_condition
: When to run (success
,error
,always
,exit-codes
)runs_on_exit_codes
: Specific exit codes that trigger execution (used withruns_on_condition: exit-codes
)success_exit_codes
: Exit codes that indicate success (defaults to[0]
)skip_exit_codes
: Exit codes that skip remaining commands in the current batch
Using Redirection:
Porch captures stdout and stderr automatically, and will by default display stderr in the results if the step fails or returns a skippable exit code. Therefore you can use redirection to capture output. E.g. to output to stderr, use command_line: "your_command 1>&2"
.
command_line: |
if [ -z "$FOO" ]; then
echo "FOO is not set. Skipping" 1>&2
exit 99
fi
skip_exit_codes: [99]
Example:
- type: "shell"
name: "Build Go Application"
command_line: "go build -ldflags='-s -w' -o dist/app ."
working_directory: "/path/to/project"
env:
CGO_ENABLED: "0"
GOOS: "linux"
GOARCH: "amd64"
success_exit_codes: [0]
skip_exit_codes: [2]
runs_on_condition: "success"
Execute any PowerShell script with full environment control and configurable exit code handling.
Required Attributes:
type: "pwsh"
name
: Descriptive name for the commandscript
: Path to the PowerShell script file to execute. Mutually exclusive withscript_file
.script_file
: Path to the PowerShell script file to execute. Mutually exclusive withscript
.
Optional Attributes:
working_directory
: Directory to execute the command inenv
: Environment variables as key-value pairsruns_on_condition
: When to run (success
,error
,always
,exit-codes
)runs_on_exit_codes
: Specific exit codes that trigger execution (used withruns_on_condition: exit-codes
)success_exit_codes
: Exit codes that indicate success (defaults to[0]
)skip_exit_codes
: Exit codes that skip remaining commands in the current batch
Example:
- type: "pwsh"
name: "Run PowerShell Script"
script: |
Write-Host "Starting PowerShell script..."
# Your PowerShell commands here
Write-Host "PowerShell script completed."
working_directory: "/path/to/project"
env:
VAR1: "value1"
VAR2: "value2"
success_exit_codes: [0]
skip_exit_codes: [2]
runs_on_condition: "success"
Execute commands sequentially where order matters. Each command waits for the previous one to complete before starting.
Required Attributes:
type: "serial"
name
: Descriptive name for the command batch
Optional Attributes:
working_directory
: Directory to execute commands inenv
: Environment variables inherited by all child commandsruns_on_condition
: When to run (success
,error
,always
,exit-codes
)runs_on_exit_codes
: Specific exit codes that trigger executioncommands
: List of commands to execute sequentially (either this orcommand_group
)command_group
: Reference to a named command group (either this orcommands
)
Example:
- type: "serial"
name: "Setup and Build"
working_directory: "/project"
env:
BUILD_MODE: "production"
commands:
- type: "shell"
name: "Install Dependencies"
command_line: "npm install"
- type: "shell"
name: "Build"
command_line: "npm run build"
- type: "shell"
name: "Test"
command_line: "npm test"
Execute independent commands concurrently for optimal performance. All commands start simultaneously.
Required Attributes:
type: "parallel"
name
: Descriptive name for the command batch
Optional Attributes:
working_directory
: Directory to execute commands inenv
: Environment variables inherited by all child commandsruns_on_condition
: When to run (success
,error
,always
,exit-codes
)runs_on_exit_codes
: Specific exit codes that trigger executioncommands
: List of commands to execute in parallel (either this orcommand_group
)command_group
: Reference to a named command group (either this orcommands
)
Example:
- type: "parallel"
name: "Quality Assurance"
commands:
- type: "shell"
name: "Unit Tests"
command_line: "go test -race ./..."
- type: "shell"
name: "Linting"
command_line: "golangci-lint run --timeout=5m"
- type: "shell"
name: "Security Scan"
command_line: "gosec -quiet ./..."
- type: "shell"
name: "Vulnerability Check"
command_line: "govulncheck ./..."
Execute commands in each directory found by traversing the filesystem. Useful for monorepos or multi-module projects.
For each command, an environment variable called ITEM
is set to the path of the current directory being processed.
Required Attributes:
type: "foreachdirectory"
name
: Descriptive name for the commandmode
: Execution mode (parallel
orserial
)depth
: Directory traversal depth (0 for unlimited, 1 for immediate children only)include_hidden
: Whether to include hidden directories (true
orfalse
)working_directory_strategy
: How to set working directory (none
,item_relative
)
Optional Attributes:
working_directory
: Base directory to start traversal fromenv
: Environment variables inherited by all child commandsruns_on_condition
: When to run (success
,error
,always
,exit-codes
)runs_on_exit_codes
: Specific exit codes that trigger executioncommands
: List of commands to execute in each directory (either this orcommand_group
)command_group
: Reference to a named command group (either this orcommands
)
Working Directory Strategies:
none
: Don't change working directory for child commandsitem_relative
: Set working directory relative to the current directory
Example:
- type: "foreachdirectory"
name: "Test All Modules"
working_directory: "./modules"
mode: "parallel"
depth: 1
include_hidden: false
working_directory_strategy: "item_relative"
commands:
- type: "shell"
name: "Run Module Tests"
command_line: "go test ./..."
- type: "shell"
name: "Check Module"
command_line: "go mod verify"
A specialized command for working in temporary directories. Copies the current working directory to a temporary location for isolated execution.
Required Attributes:
type: "copycwdtotemp"
name
: Descriptive name for the command
Optional Attributes:
working_directory
: Directory to copy (defaults to current working directory)env
: Environment variablesruns_on_condition
: When to run (success
,error
,always
,exit-codes
)runs_on_exit_codes
: Specific exit codes that trigger execution
Example:
- type: "copycwdtotemp"
name: "Create Isolated Environment"
working_directory: "."
This command is particularly useful for:
- Testing builds without affecting the source directory
- Creating clean environments for packaging
- Isolating potentially destructive operations
All commands support conditional execution based on the results of previous commands:
- type: "shell"
name: "Cleanup on Error"
command_line: "rm -rf temp_files"
runs_on_condition: "error" # Only run if previous commands failed
- type: "shell"
name: "Deploy on Success"
command_line: "./deploy.sh"
runs_on_condition: "success" # Only run if previous commands succeeded
- type: "shell"
name: "Always Cleanup"
command_line: "cleanup.sh"
runs_on_condition: "always" # Always run regardless of previous results
- type: "shell"
name: "Run on Specific Exit Codes"
command_line: "handle_warning.sh"
runs_on_condition: "exit-codes"
runs_on_exit_codes: [1, 2] # Only run if previous command exited with code 1 or 2
Environment variables can be set at any level and are inherited by child commands:
env:
GLOBAL_VAR: "global_value"
commands:
- type: "serial"
name: "Build Process"
env:
BUILD_TYPE: "release"
commands:
- type: "shell"
name: "Compile"
command_line: "make build"
env:
OPTIMIZATION: "O3" # This command sees GLOBAL_VAR, BUILD_TYPE, and OPTIMIZATION
Control execution context with flexible working directory options:
- type: "shell"
name: "Build in Subdirectory"
command_line: "make build"
working_directory: "./subproject" # Relative to current directory
- type: "serial"
name: "Build Multiple Projects"
working_directory: "/absolute/path" # Absolute path
commands:
- type: "shell"
name: "Build Project A"
command_line: "make -C project-a"
- type: "shell"
name: "Build Project B"
command_line: "make -C project-b"
name: "Complete CI/CD Pipeline"
description: "Full build, test, and deployment workflow"
commands:
# Environment setup
- type: "serial"
name: "Environment Setup"
commands:
- type: "shell"
name: "Check Go Version"
command_line: "go version"
- type: "shell"
name: "Install Dependencies"
command_line: "go mod download"
# Quality assurance (parallel)
- type: "parallel"
name: "Quality Assurance"
commands:
- type: "shell"
name: "Unit Tests"
command_line: "go test -race -coverprofile=coverage.out ./..."
- type: "shell"
name: "Linting"
command_line: "golangci-lint run --timeout=5m"
- type: "shell"
name: "Security Scan"
command_line: "gosec -quiet ./..."
- type: "shell"
name: "Vulnerability Check"
command_line: "govulncheck ./..."
# Build process
- type: "serial"
name: "Build Process"
runs_on_condition: "success"
commands:
- type: "shell"
name: "Build for Linux"
command_line: "go build -o dist/app-linux ."
env:
GOOS: "linux"
GOARCH: "amd64"
CGO_ENABLED: "0"
- type: "shell"
name: "Build for macOS"
command_line: "go build -o dist/app-macos ."
env:
GOOS: "darwin"
GOARCH: "amd64"
CGO_ENABLED: "0"
- type: "shell"
name: "Build for Windows"
command_line: "go build -o dist/app-windows.exe ."
env:
GOOS: "windows"
GOARCH: "amd64"
CGO_ENABLED: "0"
# Deployment (only on success)
- type: "shell"
name: "Deploy to Staging"
command_line: "./scripts/deploy.sh staging"
runs_on_condition: "success"
name: "Monorepo Testing"
description: "Test all modules in a monorepo structure"
commands:
- type: "foreachdirectory"
name: "Test All Modules"
working_directory: "./modules"
mode: "parallel"
depth: 1
include_hidden: false
working_directory_strategy: "item_relative"
commands:
- type: "shell"
name: "Check for Tests"
command_line: |
if [ ! -d ./tests ]; then
echo "No tests found in $(pwd)"
exit 99
fi
skip_exit_codes: [99]
- type: "shell"
name: "Install Dependencies"
command_line: "npm install"
- type: "shell"
name: "Run Tests"
command_line: "npm test"
- type: "shell"
name: "Build Module"
command_line: "npm run build"
name: "Isolated Testing"
description: "Run tests in isolated temporary directory"
commands:
- type: "serial"
name: "Isolated Test Workflow"
commands:
- type: "copycwdtotemp"
name: "Copy to Temp Directory"
- type: "shell"
name: "Modify Files Safely"
command_line: "echo 'test content' > test.txt"
- type: "shell"
name: "Run Tests"
command_line: "go test ./..."
- type: "shell"
name: "Generate Report"
command_line: "go test -json ./... > test-results.json"
name: "Command Groups Demo"
description: "Example showing reusable command groups"
command_groups:
- name: "quality-checks"
description: "Standard quality assurance commands"
commands:
- type: "shell"
name: "Run Tests"
command_line: "go test ./..."
- type: "shell"
name: "Run Linter"
command_line: "golangci-lint run"
- type: "shell"
name: "Security Scan"
command_line: "gosec ./..."
- name: "build-commands"
description: "Standard build commands"
commands:
- type: "shell"
name: "Build"
command_line: "go build -o app ."
- type: "shell"
name: "Package"
command_line: "tar -czf app.tar.gz app"
commands:
- type: "parallel"
name: "Quality Checks"
command_group: "quality-checks"
- type: "serial"
name: "Build Process"
runs_on_condition: "success"
command_group: "build-commands"
name: "Conditional Execution Demo"
description: "Examples of conditional command execution"
commands:
- type: "shell"
name: "Primary Task"
command_line: "make build"
- type: "shell"
name: "Success Handler"
command_line: "echo 'Build succeeded, deploying...'"
runs_on_condition: "success"
- type: "shell"
name: "Error Handler"
command_line: "echo 'Build failed, cleaning up...'"
runs_on_condition: "error"
- type: "shell"
name: "Warning Handler"
command_line: "echo 'Build completed with warnings'"
runs_on_condition: "exit-codes"
runs_on_exit_codes: [1, 2]
- type: "shell"
name: "Always Cleanup"
command_line: "rm -rf temp/"
runs_on_condition: "always"
porch automatically generates beautiful tree-structured output showing the execution hierarchy:
β Build and Test Workflow
β Setup Environment (0ms)
β Quality Checks (2.1s)
β Run Tests (1.8s)
β Run Linter (1.2s)
β Security Scan (0.5s) - exit code 1
stderr: gosec: 1 issue found
β Build Process (1.5s)
β Build for Linux (0.8s)
β Build for macOS (0.7s)
porch provides sophisticated signal handling for graceful shutdown:
First Signal (SIGINT/SIGTERM): Initiates graceful shutdown
- Running commands receive the signal for cleanup
- No new commands are started
- Context remains active for cleanup operations
Second Signal: Forces immediate termination
- Context is cancelled
- All running processes are killed
- Execution stops immediately
This allows for proper cleanup and prevents data corruption during interruption.
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Go and the amazing Go ecosystem
- CLI powered by urfave/cli
- YAML parsing with goccy/go-yaml
- Beautiful JSON output with TylerBrock/colorjson
porch - Making process orchestration simple, powerful, and beautiful. π