this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(controller): implement graph shaking

+141 -1
+55
controller/activities/graph.go
··· 1 + package activities 2 + 3 + import ( 4 + "fmt" 5 + "strings" 6 + 7 + "github.com/awalterschulze/gographviz" 8 + ) 9 + 10 + func pruneGraph(dot string, changed []string) (string, error) { 11 + ast, err := gographviz.ParseString(dot) 12 + if err != nil { 13 + return "", fmt.Errorf("Parse DOT failed: %w", err) 14 + } 15 + graph := gographviz.NewGraph() 16 + if err := gographviz.Analyse(ast, graph); err != nil { 17 + return "", fmt.Errorf("Graph analysis failed: %w", err) 18 + } 19 + 20 + // Build reverse dependency map: target -> dependents 21 + dependents := map[string][]string{} 22 + for _, edge := range graph.Edges.Edges { 23 + dependents[edge.Dst] = append(dependents[edge.Dst], edge.Src) 24 + } 25 + 26 + // Collect all nodes to keep (changed + all that depend on them) 27 + keep := map[string]bool{} 28 + var visit func(string) 29 + visit = func(node string) { 30 + if keep[node] { 31 + return 32 + } 33 + keep[node] = true 34 + for _, dep := range dependents[node] { 35 + visit(dep) 36 + } 37 + } 38 + for _, node := range changed { 39 + visit(node) 40 + } 41 + 42 + // Reconstruct pruned DOT graph 43 + var b strings.Builder 44 + b.WriteString("digraph {\n") 45 + for _, edge := range graph.Edges.Edges { 46 + if keep[edge.Src] && keep[edge.Dst] { 47 + b.WriteString(fmt.Sprintf(" %s -> %s;\n", edge.Src, edge.Dst)) 48 + } 49 + } 50 + for node := range keep { 51 + b.WriteString(fmt.Sprintf(" %s ;\n", node)) 52 + } 53 + b.WriteString("}") 54 + return b.String(), nil 55 + }
+61
controller/activities/graph_test.go
··· 1 + package activities 2 + 3 + import ( 4 + "strings" 5 + "testing" 6 + ) 7 + 8 + func TestPruneGraph(t *testing.T) { 9 + input := ` 10 + digraph { 11 + "azure-policies" ; 12 + "azure-vm-disk-backups/backup-policies/daily-30-day-retention" ; 13 + "azure-vm-disk-backups/backup-policies/daily-30-day-retention" -> "cloud"; 14 + "azure-vm-disk-backups/backup-policies/daily-30-day-retention" -> "azure-vm-disk-backups/backup-vault"; 15 + "azure-vm-disk-backups/backup-vault" ; 16 + "azure-vm-disk-backups/backup-vault" -> "cloud"; 17 + "cloud" ; 18 + "ecomnet-vng" ; 19 + "generated-secrets" ; 20 + "generated-secrets" -> "topology"; 21 + "il4/generated-secrets" ; 22 + "il4/generated-secrets" -> "topology"; 23 + "legacy-bridge" ; 24 + "legacy-bridge" -> "topology"; 25 + "legacy-bridge" -> "network"; 26 + "local-distribution" ; 27 + "network" ; 28 + "secrets" ; 29 + "secrets" -> "topology"; 30 + "topology" ; 31 + }` 32 + 33 + expectedContains := []string{ 34 + `"azure-vm-disk-backups/backup-policies/daily-30-day-retention"`, 35 + `"azure-vm-disk-backups/backup-policies/daily-30-day-retention" -> "azure-vm-disk-backups/backup-vault"`, 36 + `"azure-vm-disk-backups/backup-vault"`, 37 + `"topology"`, 38 + `"generated-secrets" -> "topology"`, 39 + `"il4/generated-secrets" -> "topology"`, 40 + `"legacy-bridge" -> "topology"`, 41 + `"secrets" -> "topology"`, 42 + `"local-distribution"`, 43 + } 44 + 45 + changed := []string{ 46 + `"azure-vm-disk-backups/backup-vault"`, 47 + `"topology"`, 48 + `"local-distribution"`, 49 + } 50 + 51 + pruned, err := pruneGraph(input, changed) 52 + if err != nil { 53 + t.Fatalf("unexpected error: %v", err) 54 + } 55 + 56 + for _, expected := range expectedContains { 57 + if !strings.Contains(pruned, expected) { 58 + t.Errorf("expected pruned output to contain: %s", expected) 59 + } 60 + } 61 + }
+13
controller/activities/terragrunt.go
··· 21 21 22 22 return string(output), nil 23 23 } 24 + 25 + func TerragruntGraphShaking(ctx context.Context, dotGraph string, changedFiles []string) (string, error) { 26 + logger := activity.GetLogger(ctx) 27 + 28 + logger.Info("Parsing Terragrunt DAG graph") 29 + 30 + pruned, err := pruneGraph(dotGraph, changedFiles) 31 + if err != nil { 32 + return "", fmt.Errorf("failed to prune dependency graph: %w", err) 33 + } 34 + 35 + return pruned, nil 36 + }
+1
controller/go.mod
··· 3 3 go 1.24.3 4 4 5 5 require ( 6 + github.com/awalterschulze/gographviz v2.0.3+incompatible 6 7 github.com/go-git/go-git/v5 v5.16.0 7 8 go.temporal.io/sdk v1.34.0 8 9 )
+2
controller/go.sum
··· 11 11 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= 12 12 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 13 13 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 14 + github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= 15 + github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= 14 16 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 15 17 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 16 18 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+1
controller/worker/main.go
··· 25 25 w.RegisterWorkflow(workflows.Infra) 26 26 w.RegisterActivity(activities.Clone) 27 27 w.RegisterActivity(activities.TerragruntGraph) 28 + w.RegisterActivity(activities.TerragruntTreeShaking) 28 29 29 30 err = w.Run(worker.InterruptCh()) 30 31 if err != nil {
+8 -1
controller/workflows/infra.go
··· 34 34 return "", err 35 35 } 36 36 37 + var dotGraph string 38 + err = workflow.ExecuteActivity(ctx, activities.TerragruntGraph, path+"/infra/"+input.Stack).Get(ctx, &dotGraph) 39 + if err != nil { 40 + logger.Error("Activity failed.", "Error", err) 41 + return "", err 42 + } 43 + 37 44 var result string 38 - err = workflow.ExecuteActivity(ctx, activities.TerragruntGraph, path+"/infra/"+input.Stack).Get(ctx, &result) 45 + err = workflow.ExecuteActivity(ctx, activities.TerragruntTreeShaking, dotGraph, []string{"cluster"}).Get(ctx, &result) 39 46 if err != nil { 40 47 logger.Error("Activity failed.", "Error", err) 41 48 return "", err