-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathflow.go
More file actions
105 lines (97 loc) · 3.24 KB
/
flow.go
File metadata and controls
105 lines (97 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package cli
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/randomcodespace/codeiq/internal/flow"
"github.com/randomcodespace/codeiq/internal/graph"
"github.com/spf13/cobra"
)
func init() {
registerSubcommand(newFlowCommand)
}
// newFlowCommand assembles `codeiq flow` — generates an architecture flow
// diagram for one of the five canonical views.
func newFlowCommand() *cobra.Command {
var (
graphDir string
format string
outPath string
queryTimeout time.Duration
)
cmd := &cobra.Command{
Use: "flow <view> [path]",
Short: "Generate an architecture flow diagram (overview / ci / deploy / runtime / auth).",
Long: `Generate an architecture flow diagram for the analyzed codebase.
Five views ship out of the box:
overview The high-level system view (CI + Infra + App + Security).
ci CI/CD pipeline detail (workflows, jobs, triggers).
deploy Deployment topology (K8s, Docker, Terraform).
runtime Runtime architecture grouped by layer.
auth Auth / security view with protection coverage.
Output formats: json (default), mermaid, dot, yaml. Use --out to write to
a file instead of stdout. The renderer is deterministic — nodes within
each subgraph and edges are sorted by ID before emission.`,
Example: ` codeiq flow overview
codeiq flow runtime --format mermaid > runtime.mmd
codeiq flow auth --format dot --out auth.dot
codeiq flow deploy --format yaml /repo`,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
view := args[0]
if !flow.IsKnownView(view) {
return newUsageError(
"unknown view %q; valid: overview, ci, deploy, runtime, auth", view)
}
format = strings.ToLower(strings.TrimSpace(format))
root, err := resolvePath(args[1:])
if err != nil {
return err
}
gdir := graphDir
if gdir == "" {
gdir = filepath.Join(root, ".codeiq", "graph", "codeiq.kuzu")
}
store, err := graph.OpenReadOnly(gdir, queryTimeout)
if err != nil {
return fmt.Errorf("open graph %s: %w", gdir, err)
}
defer store.Close()
engine := flow.NewEngine(store)
diag, err := engine.Generate(context.Background(), flow.View(view))
if err != nil {
return err
}
rendered, err := flow.Render(diag, format)
if err != nil {
return err
}
return writeFlowOutput(cmd.OutOrStdout(), rendered, outPath)
},
}
cmd.Flags().StringVar(&graphDir, "graph-dir", "",
"Path to the Kuzu graph store (default: <path>/.codeiq/graph/codeiq.kuzu).")
cmd.Flags().StringVar(&format, "format", "json",
"Output format: json, mermaid, dot, yaml.")
cmd.Flags().StringVar(&outPath, "out", "",
"Write the rendered diagram to this file instead of stdout.")
cmd.Flags().DurationVar(&queryTimeout, "query-timeout", graph.DefaultQueryTimeout,
"Per-query wall-clock timeout (default: 30s).")
return cmd
}
// writeFlowOutput emits content to outPath (when non-empty) or to w.
// Always terminates with a trailing newline if the content lacks one.
func writeFlowOutput(w io.Writer, content, outPath string) error {
if !strings.HasSuffix(content, "\n") {
content += "\n"
}
if outPath == "" {
_, err := io.WriteString(w, content)
return err
}
return os.WriteFile(outPath, []byte(content), 0o644)
}