CLI Usage Recipes¶
Practical examples and recipes for using the cel command-line tool in real-world scenarios, from simple evaluations to complex integrations.
Basic Usage Patterns¶
Simple Expressions¶
# Simple arithmetic
cel '1 + 2' # 3
cel '10 * 3.14' # 31.4
# String operations
cel '"Hello " + "World"' # Hello World
cel '"test".size()' # 4
# Boolean logic
cel 'true && false' # false
cel '5 > 3' # true
# Collections
cel '[1, 2, 3]' # [1, 2, 3]
cel '[1, 2, 3].size()' # 3
cel '{"name": "Alice", "age": 30}' # {"name": "Alice", "age": 30}
Working with Context¶
# Inline context
cel 'name + " is " + string(age)' --context '{"name": "Alice", "age": 30}'
# Output: Alice is 30
# Context from file
echo '{"user": {"name": "Bob", "role": "admin"}}' > user.json
cel 'user.name + " (" + user.role + ")"' --context-file user.json
# Output: Bob (admin)
# Complex context
cel 'user.role == "admin" && "write" in permissions' \
--context '{"user": {"role": "admin"}, "permissions": ["read", "write"]}'
# Output: true
Pipeline and Data Processing¶
JSON Processing with jq and CEL¶
# Process JSON with jq and CEL
echo '{"users": [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 17}]}' | \
jq '.users[]' | \
while read -r user; do
cel 'user.age >= 18 ? user.name + " (adult)" : user.name + " (minor)"' \
--context "{\"user\": $user}"
done
# Filter and transform data
curl -s https://api.github.com/users/octocat | \
cel 'login + " has " + string(public_repos) + " repos"' \
--context-file /dev/stdin
Batch Processing¶
# Process multiple files
for config in configs/*.json; do
echo "Validating $config..."
if cel 'has("database.host") && database.host != ""' --context-file "$config" --exit-status; then
echo "✓ $config is valid"
else
echo "✗ $config is invalid"
fi
done
# Transform data files
ls data/*.json | while read -r file; do
cel 'user.name + "," + user.email + "," + string(user.age)' \
--context-file "$file" >> users.csv
done
Validation and Testing¶
Configuration Validation¶
# Database configuration validation
cel 'has("database.host") && database.host != ""' --context-file config.json
cel 'database.port > 0 && database.port < 65536' --context-file config.json
cel 'database.ssl == true' --context-file config.json
# Application configuration validation
cel 'env in ["development", "staging", "production"]' --context-file app.json
cel 'has("api_key") && api_key.size() >= 32' --context-file secrets.json
Policy Testing¶
# Access control policies
cel 'user.role == "admin" || (resource.public && action == "read")' \
--context '{"user": {"role": "user"}, "resource": {"public": true}, "action": "read"}'
# Business rules testing
cel 'user.age >= 18 && user.verified && user.country in ["US", "CA", "UK"]' \
--context-file user_profile.json
# Exit status for scripting
if cel 'user.verified && user.role in ["admin", "moderator"]' \
--context-file user.json --exit-status; then
echo "User has required permissions"
else
echo "Access denied"
fi
Interactive Development¶
Interactive Session Workflow¶
# Start interactive mode
cel -i
# Example session:
CEL> context {"user": {"name": "Alice", "role": "admin", "verified": true}}
Context updated: user
CEL> user.name
Alice
CEL> user.role == "admin"
true
CEL> user.verified && user.role in ["admin", "moderator"]
true
CEL> load permissions.json
Loaded context from permissions.json
CEL> "write" in permissions
true
CEL> history
1: user.name
2: user.role == "admin"
3: user.verified && user.role in ["admin", "moderator"]
4: "write" in permissions
CEL> exit
Rapid Prototyping¶
# Test expressions quickly:
cel -i
CEL> context {"data": {"users": [{"name": "Alice", "active": true}, {"name": "Bob", "active": false}]}}
Context updated: data
CEL> data.users.filter(u, u.active).map(u, u.name)
["Alice"]
CEL> data.users.exists(u, u.name == "Bob")
true
CEL> data.users.all(u, has(u.active))
true
Integration Patterns¶
Shell Functions¶
Add to your .bashrc or .zshrc:
# Quick CEL evaluation
function cq() {
cel "$1" --context-file ~/.cel/default_context.json
}
# CEL with context from environment
function ce() {
local ctx="{\"USER\": \"$USER\", \"HOME\": \"$HOME\", \"PWD\": \"$PWD\"}"
cel "$1" --context "$ctx"
}
# Policy check
function policy_check() {
cel "$1" --context-file ~/.cel/policy_context.json --exit-status
}
# Usage examples:
cq 'user.role == "admin"'
ce 'USER == "alice"'
policy_check 'action == "deploy" && user.role in ["admin", "deployer"]'
Git Hooks¶
Use in git hooks for policy enforcement:
#!/bin/bash
# pre-commit hook
# Check if commit should be allowed
commit_msg=$(git log -1 --pretty=format:"%s")
author=$(git log -1 --pretty=format:"%an")
context="{\"commit\": {\"message\": \"$commit_msg\", \"author\": \"$author\"}}"
if ! cel 'commit.message.size() > 10 && !commit.message.contains("WIP")' \
--context "$context" --exit-status; then
echo "Commit message too short or contains WIP"
exit 1
fi
# Check file patterns
changed_files=$(git diff --cached --name-only)
if echo "$changed_files" | grep -q "\.py$"; then
if ! cel 'commit.message.contains("python") || commit.message.contains("py")' \
--context "$context" --exit-status; then
echo "Python files changed but commit message doesn't mention Python"
exit 1
fi
fi
CI/CD Integration¶
# In GitHub Actions / CI pipelines
#!/bin/bash
# Environment validation
cel 'env.NODE_ENV in ["development", "staging", "production"]' \
--context "{\"env\": {\"NODE_ENV\": \"$NODE_ENV\"}}" \
--exit-status || exit 1
# Deployment conditions
cel 'branch == "main" && tests_passed && security_scan_passed' \
--context "{
\"branch\": \"$GITHUB_REF_NAME\",
\"tests_passed\": $TESTS_PASSED,
\"security_scan_passed\": $SECURITY_SCAN_PASSED
}" \
--exit-status || exit 1
# Feature flag evaluation
cel 'env == "production" && feature_flags.new_ui_enabled' \
--context-file deployment_config.json \
--exit-status && echo "::set-output name=deploy_new_ui::true"
Docker Integration¶
FROM python:3.11-slim
# Install CEL
RUN pip install common-expression-language
# Use in health checks
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD cel 'timestamp() - start_time < duration("5m")' \
--context "{\"start_time\": \"$(cat /app/start_time)\"}" \
--exit-status
# Use in entrypoint scripts
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
# entrypoint.sh
#!/bin/bash
# Configuration validation before startup
if ! cel 'has("database.url") && has("redis.url")' \
--context-file /app/config.json --exit-status; then
echo "Missing required configuration"
exit 1
fi
# Environment-specific startup logic
if cel 'env == "development"' --context-file /app/config.json --exit-status; then
echo "Starting in development mode..."
python -m app --debug
else
echo "Starting in production mode..."
gunicorn app:app
fi
Configuration Management¶
Configuration Files¶
Create ~/.cel/config.json for default settings:
{
"default_context_file": "~/.cel/default_context.json",
"history_size": 1000,
"interactive": {
"syntax_highlighting": true,
"auto_completion": true,
"vi_mode": false
},
"output": {
"format": "auto",
"compact": false,
"color": true
}
}
Context Files¶
Store frequently used context in JSON files:
// ~/.cel/contexts/development.json
{
"env": "development",
"debug": true,
"api_url": "http://localhost:8080",
"user": {
"role": "developer",
"permissions": ["read", "write", "debug"]
}
}
// ~/.cel/contexts/production.json
{
"env": "production",
"debug": false,
"api_url": "https://api.example.com",
"user": {
"role": "service",
"permissions": ["read"]
}
}
Usage:
cel 'env == "development" && debug' --context-file ~/.cel/contexts/development.json
cel 'api_url.startsWith("https")' --context-file ~/.cel/contexts/production.json
Environment Variables¶
# Set up environment for CEL
export CEL_CONFIG_DIR="$HOME/.config/cel"
export CEL_HISTORY_FILE="$HOME/.cel_history"
export CEL_DEFAULT_CONTEXT="$HOME/.cel/default_context.json"
export CEL_DEBUG=1
# Use in scripts
cel 'expression' # Will automatically load default context and run in debug mode
Performance Optimization¶
Optimization Tips¶
- Use context files for complex context instead of inline JSON
- Cache compiled expressions when evaluating repeatedly
- Minimize context size by including only necessary data
- Use specific field access instead of passing large objects
# Faster - focused context
cel 'user.role == "admin"' --context '{"user": {"role": "admin"}}'
# Slower - large context
cel 'user.role == "admin"' --context-file large_user_object.json
Benchmarking¶
# Time expression evaluation
time cel 'complex.expression.here' --context-file large_context.json
# Measure multiple evaluations
echo "Testing expression performance..."
time for i in {1..100}; do
cel 'user.role == "admin"' --context-file user.json >/dev/null
done
# Compare different approaches
echo "Method 1: Inline context"
time for i in {1..50}; do
cel 'user.verified' --context '{"user": {"verified": true}}' >/dev/null
done
echo "Method 2: Context file"
time for i in {1..50}; do
cel 'user.verified' --context-file small_context.json >/dev/null
done
Error Handling and Debugging¶
Common Error Scenarios¶
# Syntax errors
$ cel '1 + + 2'
Error: Syntax error at position 4: unexpected token '+'
$ cel 'invalid syntax here'
Error: Parse error: expected expression
# Runtime errors
$ cel '1 / 0'
Error: Division by zero
$ cel 'unknown_variable + 1' --context '{}'
Error: Variable 'unknown_variable' not found
# Type errors
$ cel '"hello" + 42'
Error: Type mismatch: cannot add string and int
$ cel 'user.name.invalid_method()' --context '{"user": {"name": "Alice"}}'
Error: No such method 'invalid_method' on type string
# File errors
$ cel 'expression' --context-file nonexistent.json
Error: Cannot read context file: nonexistent.json (No such file or directory)
$ cel 'expression' --context-file invalid.json
Error: Invalid JSON in context file: invalid.json
Debugging Techniques¶
# Use debug mode for detailed error information
cel --debug 'problematic.expression' --context-file context.json
# Use verbose mode to see what CEL is doing
cel --verbose 'expression' --context-file context.json
# Validate JSON files before using them
cat context.json | python -m json.tool
# Test expressions step by step in interactive mode:
cel -i
CEL> context {"user": {"name": "Alice"}}
Context updated: user
CEL> has(user)
true
CEL> has(user.name)
true
CEL> user.name
Alice
Troubleshooting Recipes¶
Expression not evaluating as expected¶
Problem: Expression seems correct but doesn't evaluate as expected.
Solution: Check operator precedence and use parentheses:
# May not work as expected
cel 'a + b * c > d && e || f'
# Clearer with parentheses
cel '((a + (b * c)) > d && e) || f'
Context not loading¶
Problem: Context variables not available in expression.
Solutions:
# 1. Verify JSON syntax
cat context.json | python -m json.tool
# 2. Check file permissions
ls -la context.json
# 3. Use absolute paths
cel 'expression' --context-file "$(pwd)/context.json"
# 4. Test context loading in interactive mode
cel -i
CEL> load context.json
Loaded context from context.json
CEL> context
{"user": {"name": "Alice"}}
Type conversion issues¶
Problem: Type mismatches in expressions.
Solution: Use explicit type conversion:
# Instead of: age + "years"
cel 'string(age) + " years"' --context '{"age": 30}'
# Instead of: int_string > 10
cel 'int(int_string) > 10' --context '{"int_string": "15"}'
# Check types in interactive mode:
cel -i
CEL> context {"data": {"value": "123"}}
Context updated: data
CEL> typeof(data.value)
string
CEL> int(data.value)
123
Advanced Recipes¶
Complex Data Transformations¶
# Transform nested data structures
cel 'users.map(u, {
"name": u.name,
"email": u.email,
"is_admin": u.role == "admin",
"permissions_count": u.permissions.size()
})' --context-file users.json
# Filter and extract prices
cel 'products.filter(p, p.price > 100).map(p, p.price)' \
--context-file products.json
# Nested filtering
cel 'departments.map(d, {
"name": d.name,
"active_employees": d.employees.filter(e, e.active).size()
})' --context-file company.json
Policy Composition¶
# Base policies
base_policy='user.verified && user.active'
role_policy='user.role in ["admin", "moderator", "user"]'
time_policy='current_hour >= 9 && current_hour <= 17'
# Combine policies
cel "$base_policy && $role_policy && $time_policy" \
--context-file user_context.json
# Environment-specific policies
if [[ "$ENV" == "production" ]]; then
security_policy='user.mfa_enabled && user.last_login_days < 30'
else
security_policy='true' # Relaxed for dev/staging
fi
cel "$base_policy && $security_policy" --context-file user_context.json
For more advanced usage patterns, see the Python API documentation and other how-to guides in this section.