This tutorial covers comparing two solution files structurally to understand what changed between versions. The soldiff package detects additions, removals, and modifications across metadata, resolvers, actions, and testing sections.

Overview#

Solution diffing answers the question: “What structurally changed between two versions of a solution?” Unlike git diff which shows text changes, soldiff understands the solution schema and reports meaningful structural differences.

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│ solution-v1  │     │   soldiff    │     │    Result    │
│   .yaml      │ ──► │  .Compare()  │ ──► │  (changes,   │
│              │     │              │     │   summary)   │
│ solution-v2  │ ──► │              │     │              │
│   .yaml      │     │              │     │              │
└──────────────┘     └──────────────┘     └──────────────┘

When to Use Solution Diff#

Use CaseScenario
Code reviewReview structural impact of solution YAML changes before merging
Configuration driftDetect when a deployed solution has drifted from the expected baseline
Refactoring validationConfirm a refactor didn’t accidentally add/remove resolvers or actions
Version comparisonCompare v1 and v2 of a solution to document what changed

CLI Usage#

Basic Comparison#

scafctl solution diff -f solution-v1.yaml -f solution-v2.yaml
scafctl solution diff -f solution-v1.yaml -f solution-v2.yaml

Output:

Solution Diff: solution-v1.yaml ↔ solution-v2.yaml

Changes (5):
  changed  metadata.version: "1.0.0" → "2.0.0"
  changed  metadata.description: "Baseline version" → "Updated version"
  changed  spec.resolvers.replicas.description: "Number of replicas" → "Number of replicas (scaled up)"
  added    spec.resolvers.health_check_url
  added    spec.workflow.actions.verify

Summary: 5 total | 2 added | 0 removed | 3 changed

JSON Output#

scafctl solution diff -f solution-v1.yaml -f solution-v2.yaml -o json
scafctl solution diff -f solution-v1.yaml -f solution-v2.yaml -o json

Returns structured JSON with changes array and summary object — useful for CI pipelines and programmatic processing.

Example Walkthrough#

Step 1: Create Two Solution Versions#

Use the provided examples:

# View baseline solution
cat examples/soldiff/solution-v1.yaml

# View updated solution
cat examples/soldiff/solution-v2.yaml

Step 2: Compare Them#

scafctl solution diff -f examples/soldiff/solution-v1.yaml -f examples/soldiff/solution-v2.yaml
scafctl solution diff -f examples/soldiff/solution-v1.yaml -f examples/soldiff/solution-v2.yaml

The diff shows:

  • metadata.version changed from 1.0.0 to 2.0.0
  • metadata.description changed
  • spec.resolvers.replicas.description changed
  • spec.resolvers.health_check_url was added (new resolver)
  • spec.workflow.actions.verify was added (new action)

Step 3: Use in CI#

In a CI pipeline, use JSON output to assert no unexpected changes:

# Fail if any resolvers were removed
diff_output=$(scafctl solution diff -f baseline.yaml -f current.yaml -o json)
removed=$(echo "$diff_output" | jq '.summary.removed')
if [ "$removed" -gt 0 ]; then
  echo "ERROR: Resolvers were removed!"
  exit 1
fi
# Fail if any resolvers were removed
$diff_output = scafctl solution diff -f baseline.yaml -f current.yaml -o json
$parsed = $diff_output | ConvertFrom-Json
if ($parsed.summary.removed -gt 0) {
  Write-Output "ERROR: Resolvers were removed!"
  exit 1
}

What Gets Compared#

The diff compares these structural elements:

SectionWhat’s Compared
metadataname, description, version
spec.resolversResolver existence, type, description, primary provider
spec.workflow.actionsAction existence, provider, description
spec.workflow.finallyAction existence
spec.workflowAdded/removed as a whole
spec.testing.casesTest case existence
spec.testingAdded/removed as a whole

Programmatic Usage#

import "github.com/oakwood-commons/scafctl/pkg/soldiff"

// Compare from files
result, err := soldiff.CompareFiles(ctx, "v1.yaml", "v2.yaml")
if err != nil {
    return err
}

// Or compare in-memory solutions
result := soldiff.Compare(solutionA, solutionB)

// Inspect results
fmt.Printf("Changes: %d\n", result.Summary.Total)
for _, c := range result.Changes {
    fmt.Printf("  %s %s\n", c.Type, c.Field)
}

Combining with Snapshots#

Solution diff compares structure (YAML schema), while snapshot diff compares runtime output (resolver values, timing). Use both for comprehensive change analysis:

# 1. Compare structure
scafctl solution diff -f v1.yaml -f v2.yaml

# 2. Compare runtime behavior
scafctl run resolver -f v1.yaml --snapshot --snapshot-file=before.json
scafctl run resolver -f v2.yaml --snapshot --snapshot-file=after.json
scafctl snapshot diff before.json after.json
# 1. Compare structure
scafctl solution diff -f v1.yaml -f v2.yaml

# 2. Compare runtime behavior
scafctl run resolver -f v1.yaml --snapshot --snapshot-file=before.json
scafctl run resolver -f v2.yaml --snapshot --snapshot-file=after.json
scafctl snapshot diff before.json after.json

See Also#