Workflow templates define the DAG (Directed Acyclic Graph) of steps to execute during deployment, promotion, and other automated processes.
interface WorkflowTemplate {
id: UUID;
tenantId: UUID;
name: string; // "standard-deploy"
displayName: string; // "Standard Deployment"
description: string;
version: number; // Auto-incremented
// DAG structure
nodes: StepNode[];
edges: StepEdge[];
// I/O definitions
inputs: InputDefinition[];
outputs: OutputDefinition[];
// Metadata
tags: string[];
isBuiltin: boolean;
createdAt: DateTime;
createdBy: UUID;
}
interface StepNode {
id: string; // Unique within template (e.g., "deploy-api")
type: string; // Step type from registry
name: string; // Display name
config: Record<string, any>; // Step-specific configuration
inputs: InputBinding[]; // Input value bindings
outputs: OutputBinding[]; // Output declarations
position: { x: number; y: number }; // UI position
// Execution settings
timeout: number; // Seconds (default from step type)
retryPolicy: RetryPolicy;
onFailure: FailureAction;
condition?: string; // JS expression for conditional execution
// Documentation
description?: string;
documentation?: string;
}
type FailureAction = "fail" | "continue" | "rollback" | "goto:{nodeId}";
interface RetryPolicy {
maxRetries: number;
backoffType: "fixed" | "exponential";
backoffSeconds: number;
retryableErrors: string[];
}
interface InputBinding {
name: string; // Input parameter name
source: InputSource;
}
type InputSource =
| { type: "literal"; value: any }
| { type: "context"; path: string } // e.g., "release.name"
| { type: "output"; nodeId: string; outputName: string }
| { type: "secret"; secretName: string }
| { type: "expression"; expression: string }; // JS expression
interface StepEdge {
id: string;
from: string; // Source node ID
to: string; // Target node ID
condition?: string; // Optional condition expression
label?: string; // Display label for conditional edges
}
| Type | Description | Config |
|---|
approval | Wait for human approval | promotionId |
wait | Wait for specified duration | durationSeconds |
condition | Branch based on condition | expression |
parallel | Execute children in parallel | maxConcurrency |
| Type | Description | Config |
|---|
security-gate | Evaluate security policy | blockOnCritical, blockOnHigh |
custom-gate | Custom OPA policy evaluation | policyName |
freeze-check | Check freeze windows | - |
approval-check | Check approval status | requiredCount |
| Type | Description | Config |
|---|
deploy-docker | Deploy single container | containerName, strategy |
deploy-compose | Deploy Docker Compose stack | composePath, strategy |
deploy-ecs | Deploy to AWS ECS | cluster, service |
deploy-nomad | Deploy to HashiCorp Nomad | jobName |
| Type | Description | Config |
|---|
health-check | HTTP/TCP health check | type, path, expectedStatus |
smoke-test | Run smoke test suite | testSuite, timeout |
verify-digest | Verify deployed digest | expectedDigest |
| Type | Description | Config |
|---|
webhook | Call external webhook | url, method, headers |
trigger-ci | Trigger CI pipeline | integrationId, pipelineId |
wait-ci | Wait for CI pipeline | runId, timeout |
| Type | Description | Config |
|---|
notify | Send notification | channel, template |
slack | Send Slack message | channel, message |
email | Send email | recipients, template |
| Type | Description | Config |
|---|
rollback | Rollback deployment | strategy, targetReleaseId |
execute-script | Run recovery script | scriptType, scriptRef |
{
"id": "template-standard-deploy",
"name": "standard-deploy",
"displayName": "Standard Deployment",
"version": 1,
"inputs": [
{ "name": "releaseId", "type": "uuid", "required": true },
{ "name": "environmentId", "type": "uuid", "required": true },
{ "name": "promotionId", "type": "uuid", "required": true }
],
"nodes": [
{
"id": "approval",
"type": "approval",
"name": "Approval Gate",
"config": {},
"inputs": [
{ "name": "promotionId", "source": { "type": "context", "path": "promotionId" } }
],
"position": { "x": 100, "y": 100 }
},
{
"id": "security-gate",
"type": "security-gate",
"name": "Security Verification",
"config": {
"blockOnCritical": true,
"blockOnHigh": true
},
"inputs": [
{ "name": "releaseId", "source": { "type": "context", "path": "releaseId" } }
],
"position": { "x": 100, "y": 200 }
},
{
"id": "pre-deploy-hook",
"type": "execute-script",
"name": "Pre-Deploy Hook",
"config": {
"scriptType": "csharp",
"scriptRef": "hooks/pre-deploy.csx"
},
"inputs": [
{ "name": "release", "source": { "type": "context", "path": "release" } },
{ "name": "environment", "source": { "type": "context", "path": "environment" } }
],
"timeout": 300,
"onFailure": "fail",
"position": { "x": 100, "y": 300 }
},
{
"id": "deploy-targets",
"type": "deploy-compose",
"name": "Deploy to Targets",
"config": {
"strategy": "rolling",
"parallelism": 2
},
"inputs": [
{ "name": "releaseId", "source": { "type": "context", "path": "releaseId" } },
{ "name": "environmentId", "source": { "type": "context", "path": "environmentId" } }
],
"timeout": 600,
"retryPolicy": {
"maxRetries": 2,
"backoffType": "exponential",
"backoffSeconds": 30
},
"onFailure": "rollback",
"position": { "x": 100, "y": 400 }
},
{
"id": "health-check",
"type": "health-check",
"name": "Health Verification",
"config": {
"type": "http",
"path": "/health",
"expectedStatus": 200,
"timeout": 30,
"retries": 5
},
"inputs": [
{ "name": "targets", "source": { "type": "output", "nodeId": "deploy-targets", "outputName": "deployedTargets" } }
],
"onFailure": "rollback",
"position": { "x": 100, "y": 500 }
},
{
"id": "post-deploy-hook",
"type": "execute-script",
"name": "Post-Deploy Hook",
"config": {
"scriptType": "bash",
"inline": "echo 'Deployment complete'"
},
"timeout": 300,
"onFailure": "continue",
"position": { "x": 100, "y": 600 }
},
{
"id": "notify-success",
"type": "notify",
"name": "Success Notification",
"config": {
"channel": "slack",
"template": "deployment-success"
},
"inputs": [
{ "name": "release", "source": { "type": "context", "path": "release" } },
{ "name": "environment", "source": { "type": "context", "path": "environment" } }
],
"onFailure": "continue",
"position": { "x": 100, "y": 700 }
},
{
"id": "rollback-handler",
"type": "rollback",
"name": "Rollback Handler",
"config": {
"strategy": "to-previous"
},
"inputs": [
{ "name": "deploymentJobId", "source": { "type": "output", "nodeId": "deploy-targets", "outputName": "jobId" } }
],
"position": { "x": 300, "y": 450 }
},
{
"id": "notify-failure",
"type": "notify",
"name": "Failure Notification",
"config": {
"channel": "slack",
"template": "deployment-failure"
},
"onFailure": "continue",
"position": { "x": 300, "y": 550 }
}
],
"edges": [
{ "id": "e1", "from": "approval", "to": "security-gate" },
{ "id": "e2", "from": "security-gate", "to": "pre-deploy-hook" },
{ "id": "e3", "from": "pre-deploy-hook", "to": "deploy-targets" },
{ "id": "e4", "from": "deploy-targets", "to": "health-check" },
{ "id": "e5", "from": "health-check", "to": "post-deploy-hook" },
{ "id": "e6", "from": "post-deploy-hook", "to": "notify-success" },
{ "id": "e7", "from": "deploy-targets", "to": "rollback-handler", "condition": "status === 'failed'" },
{ "id": "e8", "from": "health-check", "to": "rollback-handler", "condition": "status === 'failed'" },
{ "id": "e9", "from": "rollback-handler", "to": "notify-failure" }
]
}
Templates are validated for:
- Structural validity: Valid JSON/YAML, required fields present
- DAG validity: No cycles, all edges reference valid nodes
- Type validity: All step types exist in registry
- Schema validity: Step configs match type schemas
- Input validity: All required inputs are bindable