Workflow Template Structure

Overview

Workflow templates define the DAG (Directed Acyclic Graph) of steps to execute during deployment, promotion, and other automated processes.

Template Structure

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;
}

Node Types

Step Node

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[];
}

Input Bindings

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

Edge Types

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
}

Built-in Step Types

Control Steps

TypeDescriptionConfig
approvalWait for human approvalpromotionId
waitWait for specified durationdurationSeconds
conditionBranch based on conditionexpression
parallelExecute children in parallelmaxConcurrency

Gate Steps

TypeDescriptionConfig
security-gateEvaluate security policyblockOnCritical, blockOnHigh
custom-gateCustom OPA policy evaluationpolicyName
freeze-checkCheck freeze windows-
approval-checkCheck approval statusrequiredCount

Deploy Steps

TypeDescriptionConfig
deploy-dockerDeploy single containercontainerName, strategy
deploy-composeDeploy Docker Compose stackcomposePath, strategy
deploy-ecsDeploy to AWS ECScluster, service
deploy-nomadDeploy to HashiCorp NomadjobName

Verification Steps

TypeDescriptionConfig
health-checkHTTP/TCP health checktype, path, expectedStatus
smoke-testRun smoke test suitetestSuite, timeout
verify-digestVerify deployed digestexpectedDigest

Integration Steps

TypeDescriptionConfig
webhookCall external webhookurl, method, headers
trigger-ciTrigger CI pipelineintegrationId, pipelineId
wait-ciWait for CI pipelinerunId, timeout

Notification Steps

TypeDescriptionConfig
notifySend notificationchannel, template
slackSend Slack messagechannel, message
emailSend emailrecipients, template

Recovery Steps

TypeDescriptionConfig
rollbackRollback deploymentstrategy, targetReleaseId
execute-scriptRun recovery scriptscriptType, scriptRef

Template Example: Standard Deployment

{
  "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" }
  ]
}

Template Validation

Templates are validated for:

  1. Structural validity: Valid JSON/YAML, required fields present
  2. DAG validity: No cycles, all edges reference valid nodes
  3. Type validity: All step types exist in registry
  4. Schema validity: Step configs match type schemas
  5. Input validity: All required inputs are bindable

References