Sub-operations
Operations can call other operations as steps, enabling composability and code reuse.
Basic Usage
Use the sub: key to call another operation:
dyngle:
operations:
greet:
- echo "Hello!"
greet-twice:
steps:
- sub: greet
- sub: greet
Passing Arguments
Sub-operations can accept arguments using the args: key:
dyngle:
operations:
greet-person:
expressions:
person: "args[0]"
steps:
- echo "Hello, {{person}}!"
greet-team:
steps:
- sub: greet-person
args: ['Alice']
- sub: greet-person
args: ['Bob']
Scoping Rules
Sub-operations follow clear scoping rules that separate declared values from live data:
Declared Values (Locally Scoped)
Values and expressions declared via values: or expressions: keys are local to each operation:
- A parent operation's declared values are NOT visible to child sub-operations
- A child sub-operation's declared values do NOT leak to the parent operation
- Each operation only sees its own declared values plus global declared values
Live Data (Globally Shared)
Data assigned via the => operator persists across all operations:
- Live data populated by a sub-operation IS available to the parent after the sub-operation completes
- This allows operations to communicate results through shared mutable state
Example
dyngle:
values:
declared-val: global
operations:
child:
values:
declared-val: child-local
steps:
- echo {{declared-val}} # Outputs "child-local"
- echo "result" => live-data
parent:
steps:
- echo {{declared-val}} # Outputs "global"
- sub: child
- echo {{declared-val}} # Still outputs "global"
- echo {{live-data}} # Outputs "result" (persisted from child)
Return Values from Sub-operations
When a sub-operation specifies a return: key, the parent operation can capture the return value:
dyngle:
operations:
get-temperature:
return: temp
steps:
- curl -s "https://api.example.com/weather" => data
- data -> jq -r '.temperature' => temp
weather-report:
steps:
- sub: get-temperature
=> temperature
- echo "Current temperature: {{temperature}} degrees"
Composition Patterns
Build Pipeline
dyngle:
operations:
install-deps:
- npm install
compile:
- npm run build
test:
- npm test
build:
description: Full build pipeline
steps:
- sub: install-deps
- sub: compile
- sub: test
Reusable Components
dyngle:
operations:
setup-env:
access: private
steps:
- echo "Setting up environment..."
- export NODE_ENV=production
deploy-frontend:
steps:
- sub: setup-env
- npm run deploy:frontend
deploy-backend:
steps:
- sub: setup-env
- npm run deploy:backend
Data Processing Chain
dyngle:
operations:
fetch-data:
return: raw-data
steps:
- curl -s "https://api.example.com/data" => raw-data
transform-data:
expressions:
input: "args[0]"
return: transformed
steps:
- input -> jq '.items' => transformed
process-all:
return: result
steps:
- sub: fetch-data
=> data
- sub: transform-data
args: [data]
=> result
Best Practices
Use Private Operations for Helpers
Mark helper operations as private to prevent direct execution:
dyngle:
operations:
deploy:
steps:
- sub: validate
- sub: build
- sub: upload
validate:
access: private
steps:
- echo "Validating configuration..."
build:
access: private
steps:
- npm run build
upload:
access: private
steps:
- aws s3 sync ./dist s3://my-bucket/
Use Private Operations for Secrets
Private operations are particularly useful for operations that generate or fetch secrets:
dyngle:
operations:
get-api-token:
access: private
return: token
steps:
- aws secretsmanager get-secret-value --secret-id api-token => secret
- secret -> jq -r '.SecretString' => token
call-api:
description: Make authenticated API call
steps:
- sub: get-api-token
=> token
- curl -H "Authorization: Bearer {{token}}" https://api.example.com/data
This prevents accidental exposure of the token operation via dyngle run or the MCP server.
Share Data via Return Values
Instead of relying on implicit live data sharing, prefer explicit return values:
Good:
dyngle:
operations:
get-version:
return: version
steps:
- cat package.json => pkg
- pkg -> jq -r '.version' => version
tag-release:
steps:
- sub: get-version
=> ver
- git tag "v{{ver}}"
Less clear:
dyngle:
operations:
get-version:
steps:
- cat package.json => pkg
- pkg -> jq -r '.version' => version
tag-release:
steps:
- sub: get-version
- git tag "v{{version}}" # Implicit dependency