Eval Tutorial#
This tutorial covers using the scafctl eval command group to test CEL expressions, render Go templates, and validate solution files — all without running a full solution.
Overview#
The eval commands are developer tools for quick iteration:
eval cel— Evaluate CEL expressions with inline or file-based dataeval template— Render Go templates against JSON/YAML data
For solution file validation, use the separate scafctl lint command (covered in Section 3
below).
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Expression │ ────► │ scafctl │ ────► │ Result │
│ or Template │ │ eval │ │ (stdout) │
└──────────────┘ └──────────────┘ └──────────────┘These commands are especially useful when writing or debugging resolver expressions, action templates, and solution configurations.
1. Evaluating CEL Expressions#
Basic Usage#
Evaluate a simple expression:
scafctl eval cel "1 + 2"
# 3scafctl eval cel "1 + 2"
# 3String operations:
scafctl eval cel '"hello".upperAscii()'
# HELLOscafctl eval cel '"hello".upperAscii()'
# HELLOUsing Inline Data#
Pass JSON data to the expression via --data:
scafctl eval cel '_.name + " is " + string(_.age)' \
--data '{"name": "Alice", "age": 30}'
# Alice is 30scafctl eval cel '_.name + " is " + string(_.age)' `
--data '{"name": "Alice", "age": 30}'
# Alice is 30The data is available under _, just like in solution resolvers.
Using Data Files#
For larger datasets, use --data-file:
# Create a data file
cat > data.json << 'EOF'
{
"items": [
{"name": "item-a", "active": true},
{"name": "item-b", "active": false},
{"name": "item-c", "active": true}
]
}
EOF
# Filter active items
scafctl eval cel '_.items.filter(i, i.active).map(i, i.name)' --data-file data.json
# ["item-a", "item-c"]# Create a data file
@'
{
"items": [
{"name": "item-a", "active": true},
{"name": "item-b", "active": false},
{"name": "item-c", "active": true}
]
}
'@ | Set-Content -Path data.json
# Filter active items
scafctl eval cel '_.items.filter(i, i.active).map(i, i.name)' --data-file data.json
# ["item-a", "item-c"]Output Formats#
Use -o to control output format:
# JSON output
scafctl eval cel '{"greeting": "hello", "count": 42}' -o json
# YAML output
scafctl eval cel '{"greeting": "hello", "count": 42}' -o yaml# JSON output
scafctl eval cel '{"greeting": "hello", "count": 42}' -o json
# YAML output
scafctl eval cel '{"greeting": "hello", "count": 42}' -o yamlBuilt-in Functions#
Test scafctl’s custom CEL extension functions:
# String functions
scafctl eval cel '"hello-world".toCamelCase()'
# helloWorld
scafctl eval cel '"hello-world".toPascalCase()'
# HelloWorld
# List the available CEL functions
scafctl eval cel 'true' --list-functions# String functions
scafctl eval cel '"hello-world".toCamelCase()'
# helloWorld
scafctl eval cel '"hello-world".toPascalCase()'
# HelloWorld
# List the available CEL functions
scafctl eval cel 'true' --list-functionsTip: Use
eval celto prototype resolver expressions before adding them to a solution. This is much faster than running the entire solution each time.
2. Evaluating Go Templates#
Basic Usage#
Render a template with inline data:
scafctl eval template '{{.name}} has {{.count}} items' \
--data '{"name": "project", "count": 5}'
# project has 5 itemsscafctl eval template '{{.name}} has {{.count}} items' `
--data '{"name": "project", "count": 5}'
# project has 5 itemsTemplate Files#
For multi-line templates, use --template-file:
cat > greeting.tmpl << 'EOF'
Hello, {{.name}}!
You have {{len .items}} items:
{{range .items}}- {{.}}
{{end}}
EOF
scafctl eval template --template-file greeting.tmpl \
--data '{"name": "Alice", "items": ["task-1", "task-2", "task-3"]}'@'
Hello, {{.name}}!
You have {{len .items}} items:
{{range .items}}- {{.}}
{{end}}
'@ | Set-Content -Path greeting.tmpl
scafctl eval template --template-file greeting.tmpl `
--data '{"name": "Alice", "items": ["task-1", "task-2", "task-3"]}'Data Files#
Combine template files with data files:
scafctl eval template --template-file config.tmpl --data-file values.jsonscafctl eval template --template-file config.tmpl --data-file values.jsonWriting Output to File#
Save rendered output to a file:
scafctl eval template --template-file config.tmpl \
--data-file values.json \
--output generated-config.yamlscafctl eval template --template-file config.tmpl `
--data-file values.json `
--output generated-config.yamlTip: Use
eval templateto preview scaffold/render actions before running the full solution workflow.
3. Validating Solution Files with scafctl lint#
While eval cel and eval template test individual expressions and templates, use the scafctl lint command to validate entire solution files against the schema and lint rules.
Note:
scafctl lintis a separate top-level command, not part ofeval. It’s included here because validation is a natural part of the expression development workflow.
Basic Validation#
Check a solution file for schema errors and lint violations:
scafctl lint -f solution.yamlscafctl lint -f solution.yamlGet structured validation results:
scafctl lint -f solution.yaml -o jsonscafctl lint -f solution.yaml -o jsonFilter by Severity#
Only show findings at or above a minimum severity level:
# Show only warnings and errors (skip info)
scafctl lint -f solution.yaml --severity warning# Show only warnings and errors (skip info)
scafctl lint -f solution.yaml --severity warningQuick Validation in Scripts#
Use quiet mode for CI/CD pipelines — returns exit code only (0 = clean, non-zero = errors):
if scafctl lint -f solution.yaml -o quiet; then
echo "Valid!"
else
echo "Validation failed"
exit 1
fiscafctl lint -f solution.yaml -o quiet
if ($LASTEXITCODE -eq 0) { Write-Host "Valid!" } else { Write-Host "Validation failed"; exit 1 }Exploring Lint Rules#
List all available lint rules:
scafctl lint rulesscafctl lint rulesGet a detailed explanation of a specific rule:
scafctl lint explain <rule-id>scafctl lint explain <rule-id>See the Linting Tutorial for a deep dive into lint rules and custom validation workflows.
4. Common Workflows#
Prototyping Resolver Expressions#
When building a new resolver, iterate quickly with eval cel:
# 1. Start with simple expression
scafctl eval cel '"Hello, " + _.name' --data '{"name": "World"}'
# 2. Add complexity
scafctl eval cel '_.items.filter(i, i.enabled).map(i, i.name).join(", ")' \
--data-file real-data.json
# 3. Once working, copy to solution YAML# 1. Start with simple expression
scafctl eval cel '"Hello, " + _.name' --data '{"name": "World"}'
# 2. Add complexity
scafctl eval cel '_.items.filter(i, i.enabled).map(i, i.name).join(", ")' `
--data-file real-data.json
# 3. Once working, copy to solution YAMLTesting Template Rendering#
Preview template output before integrating into actions:
# Test with known data
scafctl eval template --template-file deploy.tmpl --data '{"env": "staging", "replicas": 3}'
# Test with real resolver output (from a snapshot)
scafctl eval template --template-file deploy.tmpl --data-file snapshot.json# Test with known data
scafctl eval template --template-file deploy.tmpl --data '{"env": "staging", "replicas": 3}'
# Test with real resolver output (from a snapshot)
scafctl eval template --template-file deploy.tmpl --data-file snapshot.jsonPre-commit Validation#
Add to your pre-commit hooks or CI pipeline:
# Validate all solution files in a directory
find . -name 'solution.yaml' -exec scafctl lint -f {} -o quiet \;Get-ChildItem -Recurse -Filter 'solution.yaml' | ForEach-Object { scafctl lint -f $_.FullName -o quiet }Next Steps#
- CEL Expressions Tutorial — Deep dive into CEL expression syntax and custom functions
- Go Templates Tutorial — Go template syntax and scafctl template functions
- Resolver Tutorial — Use expressions in resolvers
- Functional Testing Tutorial — Automated solution testing