Skip to content

Commit 7cdae66

Browse files
authored
Merge pull request #65 from RoseSecurity/fix-rendering-and-parsing
Fix rendering and parsing
2 parents cd4c999 + 4b5c57c commit 7cdae66

20 files changed

+412
-284
lines changed

.github/workflows/test.yml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
cache: false
1919
- run: |
2020
make build
21-
build/terramaid -w test/aws
21+
build/terramaid run -w test/aws
2222
cat Terramaid.md
2323
test_gcp:
2424
runs-on: ubuntu-latest
@@ -31,7 +31,7 @@ jobs:
3131
cache: false
3232
- run: |
3333
make build
34-
build/terramaid -w test/gcp
34+
build/terramaid run -w test/gcp
3535
cat Terramaid.md
3636
test_az:
3737
runs-on: ubuntu-latest
@@ -44,5 +44,18 @@ jobs:
4444
cache: false
4545
- run: |
4646
make build
47-
build/terramaid -w test/az
47+
build/terramaid run -w test/az
48+
cat Terramaid.md
49+
test_multi:
50+
runs-on: ubuntu-latest
51+
steps:
52+
- uses: actions/checkout@v4
53+
- uses: hashicorp/setup-terraform@v3
54+
- uses: actions/setup-go@v5
55+
with:
56+
go-version-file: "go.mod"
57+
cache: false
58+
- run: |
59+
make build
60+
build/terramaid run -w test/multi
4861
cat Terramaid.md

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FROM golang:alpine AS builder
22
WORKDIR /usr/src/terramaid
33
# Terraform version
4-
ARG TERRAFORM_VERSION=1.9.2
4+
ARG TERRAFORM_VERSION=1.10.0
55

66
# Install necessary dependencies
77
RUN apk update && apk add --no-cache \

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ make build
112112
The following configuration options are available:
113113

114114
```sh
115-
> terramaid -h
115+
> terramaid run -h
116116
A utility for generating Mermaid diagrams from Terraform
117117

118118
Usage:
@@ -142,7 +142,7 @@ Use "terramaid [command] --help" for more information about a command.
142142
Run the following command to utilize the Terramaid Docker image:
143143

144144
```sh
145-
docker run -it -v $(pwd):/usr/src/terramaid rosesecurity/terramaid:latest
145+
docker run -it -v $(pwd):/usr/src/terramaid rosesecurity/terramaid:latest run
146146
```
147147

148148
## CI/CD Integrations

cmd/docs.go

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,17 @@ import (
66
)
77

88
// Generate documentation for Terramaid commands and output to docs directory
9-
func docsCmd() *cobra.Command {
10-
cmd := &cobra.Command{
11-
Use: "docs",
12-
Short: "Generate documentation for the CLI",
13-
SilenceUsage: true,
14-
Hidden: true,
15-
RunE: func(cmd *cobra.Command, args []string) error {
16-
err := doc.GenMarkdownTree(cmd.Root(), "./docs")
17-
if err != nil {
18-
return err
19-
}
9+
var docsCmd = &cobra.Command{
10+
Use: "docs",
11+
Short: "Generate documentation for the CLI",
12+
SilenceUsage: true,
13+
Hidden: true,
14+
RunE: func(cmd *cobra.Command, args []string) error {
15+
err := doc.GenMarkdownTree(cmd.Root(), "./docs")
16+
if err != nil {
17+
return err
18+
}
2019

21-
return nil
22-
},
23-
}
24-
25-
return cmd
20+
return nil
21+
},
2622
}

cmd/root.go

Lines changed: 26 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -2,131 +2,40 @@ package cmd
22

33
import (
44
"fmt"
5-
"log"
6-
"os"
7-
"os/exec"
85

9-
"github.com/RoseSecurity/terramaid/internal"
10-
"github.com/RoseSecurity/terramaid/pkg/utils"
11-
"github.com/caarlos0/env/v11"
6+
tuiUtils "github.com/RoseSecurity/terramaid/internal/tui/utils"
7+
u "github.com/RoseSecurity/terramaid/pkg/utils"
128
"github.com/spf13/cobra"
139
)
1410

15-
var Version string
16-
17-
type options struct {
18-
WorkingDir string `env:"WORKING_DIR" envDefault:"."`
19-
TFDir string `env:"TF_DIR" envDefault:"."`
20-
TFPlan string `env:"TF_PLAN"`
21-
TFBinary string `env:"TF_BINARY"`
22-
Output string `env:"OUTPUT" envDefault:"Terramaid.md"`
23-
Direction string `env:"DIRECTION" envDefault:"TD"`
24-
SubgraphName string `env:"SUBGRAPH_NAME" envDefault:"Terraform"`
25-
ChartType string `env:"CHART_TYPE" envDefault:"flowchart"`
11+
var rootCmd = &cobra.Command{
12+
Use: "terramaid",
13+
Short: "A utility for generating Mermaid diagrams from Terraform configurations",
14+
SilenceUsage: true,
15+
SilenceErrors: true,
16+
Run: func(cmd *cobra.Command, args []string) {
17+
var err error
18+
fmt.Println()
19+
err = tuiUtils.PrintStyledText("TERRAMAID")
20+
if err != nil {
21+
u.LogErrorAndExit(err)
22+
}
23+
cmd.Help()
24+
},
2625
}
2726

28-
func TerramaidCmd() *cobra.Command {
29-
options := &options{}
30-
31-
// Parse Envs
32-
if err := env.ParseWithOptions(options, env.Options{Prefix: "TERRAMAID_"}); err != nil {
33-
log.Fatalf("error parsing envs: %s", err.Error())
34-
}
35-
36-
cmd := &cobra.Command{
37-
Use: "terramaid",
38-
Short: "A utility for generating Mermaid diagrams from Terraform configurations",
39-
SilenceUsage: true,
40-
SilenceErrors: true,
41-
PreRunE: func(cmd *cobra.Command, args []string) error {
42-
if options.TFDir != "" && !utils.DirExists(options.TFDir) {
43-
return fmt.Errorf("Terraform directory \"%s\" does not exist", options.TFDir)
44-
}
45-
46-
if options.TFDir != "" {
47-
exists, err := utils.TerraformFilesExist(options.TFDir)
48-
if err != nil {
49-
return fmt.Errorf("error checking Terraform files in directory \"%s\": %v", options.TFDir, err)
50-
}
51-
if !exists {
52-
return fmt.Errorf("Terraform files do not exist in directory \"%s\"", options.TFDir)
53-
}
54-
}
55-
56-
if options.WorkingDir != "" && !utils.DirExists(options.WorkingDir) {
57-
return fmt.Errorf("Working directory \"%s\" does not exist", options.WorkingDir)
58-
}
59-
60-
if options.TFPlan != "" && !utils.DirExists(options.TFPlan) {
61-
return fmt.Errorf("Terraform planfile \"%s\" does not exist", options.TFPlan)
62-
}
63-
64-
if options.TFBinary == "" {
65-
tfBinary, err := exec.LookPath("terraform")
66-
if err != nil {
67-
return fmt.Errorf("error finding Terraform binary: %w", err)
68-
}
69-
70-
options.TFBinary = tfBinary
71-
}
72-
73-
return nil
74-
},
75-
76-
RunE: func(cmd *cobra.Command, args []string) error {
77-
sp := utils.NewSpinner("Generating Terramaid Diagrams")
78-
sp.Start()
79-
graph, err := internal.ParseTerraform(options.WorkingDir, options.TFBinary, options.TFPlan)
80-
if err != nil {
81-
return fmt.Errorf("error parsing Terraform: %w", err)
82-
}
27+
func init() {
28+
// Add subcommands
29+
rootCmd.AddCommand(runCmd)
30+
rootCmd.AddCommand(docsCmd)
31+
rootCmd.AddCommand(versionCmd)
8332

84-
// Convert the graph to a Mermaid diagram
85-
var mermaidDiagram string
86-
87-
switch options.ChartType {
88-
case "flowchart":
89-
mermaidDiagram, err = internal.ConvertToMermaidFlowchart(graph, options.Direction, options.SubgraphName)
90-
if err != nil {
91-
return fmt.Errorf("error converting to Mermaid flowchart: %w", err)
92-
}
93-
default:
94-
return fmt.Errorf("unsupported chart type: %s", options.ChartType)
95-
}
96-
97-
// Write the Mermaid diagram to the specified output file
98-
if err := os.WriteFile(options.Output, []byte(mermaidDiagram), 0o644); err != nil {
99-
return fmt.Errorf("error writing to file: %w", err)
100-
}
101-
102-
sp.Stop()
103-
fmt.Printf("Mermaid diagram successfully written to %s\n", options.Output)
104-
105-
return nil
106-
},
107-
}
108-
109-
cmd.Flags().StringVarP(&options.Output, "output", "o", options.Output, "Output file for Mermaid diagram (env: TERRAMAID_OUTPUT)")
110-
cmd.Flags().StringVarP(&options.Direction, "direction", "r", options.Direction, "Specify the direction of the diagram (env: TERRAMAID_DIRECTION)")
111-
cmd.Flags().StringVarP(&options.SubgraphName, "subgraph-name", "s", options.SubgraphName, "Specify the subgraph name of the diagram (env: TERRAMAID_SUBGRAPH_NAME)")
112-
cmd.Flags().StringVarP(&options.ChartType, "chart-type", "c", options.ChartType, "Specify the type of Mermaid chart to generate (env: TERRAMAID_CHART_TYPE)")
113-
cmd.Flags().StringVarP(&options.TFDir, "tf-dir", "d", options.TFDir, "Path to Terraform directory (env: TERRAMAID_TF_DIR)")
114-
cmd.Flags().StringVarP(&options.TFPlan, "tf-plan", "p", options.TFPlan, "Path to Terraform plan file (env: TERRAMAID_TF_PLAN)")
115-
cmd.Flags().StringVarP(&options.TFBinary, "tf-binary", "b", options.TFBinary, "Path to Terraform binary (env: TERRAMAID_TF_BINARY)")
116-
cmd.Flags().StringVarP(&options.WorkingDir, "working-dir", "w", options.WorkingDir, "Working directory for Terraform (env: TERRAMAID_WORKING_DIR)")
117-
118-
cmd.AddCommand(docsCmd(), versionCmd(Version))
119-
120-
// Disable auto generated string from documentation so that documentation is cleanly built and updated
121-
cmd.DisableAutoGenTag = true
122-
123-
return cmd
33+
// Disable auto-generated string from documentation so that documentation is cleanly built and updated
34+
rootCmd.DisableAutoGenTag = true
12435
}
12536

126-
func Execute() error {
127-
if err := TerramaidCmd().Execute(); err != nil {
128-
return err
37+
func Execute() {
38+
if err := rootCmd.Execute(); err != nil {
39+
u.LogErrorAndExit(err)
12940
}
130-
131-
return nil
13241
}

cmd/run.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
8+
"github.com/RoseSecurity/terramaid/internal"
9+
"github.com/RoseSecurity/terramaid/pkg/utils"
10+
"github.com/caarlos0/env/v11"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
type options struct {
15+
WorkingDir string `env:"WORKING_DIR" envDefault:"."`
16+
TFPlan string `env:"TF_PLAN"`
17+
TFBinary string `env:"TF_BINARY"`
18+
Output string `env:"OUTPUT" envDefault:"Terramaid.md"`
19+
Direction string `env:"DIRECTION" envDefault:"TD"`
20+
SubgraphName string `env:"SUBGRAPH_NAME" envDefault:"Terraform"`
21+
ChartType string `env:"CHART_TYPE" envDefault:"flowchart"`
22+
}
23+
24+
var opts options // Global variable for flags and env variables
25+
26+
var runCmd = &cobra.Command{
27+
Use: "run",
28+
Short: "Generate Mermaid diagrams from Terraform configurations",
29+
SilenceUsage: true,
30+
SilenceErrors: true,
31+
RunE: func(cmd *cobra.Command, args []string) error {
32+
// The opts variable is automatically populated with flags here
33+
return generateDiagrams(&opts)
34+
},
35+
}
36+
37+
func generateDiagrams(opts *options) error {
38+
if opts.WorkingDir != "" {
39+
exists, err := utils.TerraformFilesExist(opts.WorkingDir)
40+
if err != nil {
41+
return fmt.Errorf("error checking Terraform files in directory \"%s\": %v", opts.WorkingDir, err)
42+
}
43+
if !exists {
44+
return fmt.Errorf("Terraform files do not exist in directory \"%s\"", opts.WorkingDir)
45+
}
46+
}
47+
48+
// Validate directories and files
49+
if opts.WorkingDir != "" && !utils.DirExists(opts.WorkingDir) {
50+
return fmt.Errorf("terraform directory \"%s\" does not exist", opts.WorkingDir)
51+
}
52+
53+
// Check for Terraform binary
54+
if opts.TFBinary == "" {
55+
tfBinary, err := exec.LookPath("terraform")
56+
if err != nil {
57+
return fmt.Errorf("error finding Terraform binary: %w", err)
58+
}
59+
opts.TFBinary = tfBinary
60+
}
61+
62+
// Spinner initialization and graph parsing
63+
sp := utils.NewSpinner("Generating Terramaid Diagrams")
64+
sp.Start()
65+
66+
graph, err := internal.ParseTerraform(opts.WorkingDir, opts.TFBinary, opts.TFPlan)
67+
if err != nil {
68+
sp.Stop()
69+
return fmt.Errorf("error parsing Terraform: %w", err)
70+
}
71+
72+
// Generate the Mermaid diagram
73+
mermaidDiagram, err := internal.GenerateMermaidFlowchart(graph, opts.Direction, opts.SubgraphName)
74+
if err != nil {
75+
sp.Stop()
76+
return fmt.Errorf("error generating Mermaid diagram: %w", err)
77+
}
78+
79+
// Write the Mermaid diagram to the specified output file
80+
if err := os.WriteFile(opts.Output, []byte(mermaidDiagram), 0o644); err != nil {
81+
sp.Stop()
82+
return fmt.Errorf("error writing to file: %w", err)
83+
}
84+
85+
sp.Stop()
86+
fmt.Printf("Mermaid diagram successfully written to %s\n", opts.Output)
87+
88+
return nil
89+
}
90+
91+
func init() {
92+
// Parse environment variables first, then bind flags to the opts struct
93+
if err := env.ParseWithOptions(&opts, env.Options{Prefix: "TERRAMAID_"}); err != nil {
94+
fmt.Printf("Error parsing environment variables: %s\n", err.Error())
95+
}
96+
97+
// Bind flags to the opts struct
98+
runCmd.Flags().StringVarP(&opts.Output, "output", "o", opts.Output, "Output file for Mermaid diagram (env: TERRAMAID_OUTPUT)")
99+
runCmd.Flags().StringVarP(&opts.Direction, "direction", "r", opts.Direction, "Specify the direction of the diagram (env: TERRAMAID_DIRECTION)")
100+
runCmd.Flags().StringVarP(&opts.SubgraphName, "subgraph-name", "s", opts.SubgraphName, "Specify the subgraph name of the diagram (env: TERRAMAID_SUBGRAPH_NAME)")
101+
runCmd.Flags().StringVarP(&opts.ChartType, "chart-type", "c", opts.ChartType, "Specify the type of Mermaid chart to generate (env: TERRAMAID_CHART_TYPE)")
102+
runCmd.Flags().StringVarP(&opts.TFPlan, "tf-plan", "p", opts.TFPlan, "Path to Terraform plan file (env: TERRAMAID_TF_PLAN)")
103+
runCmd.Flags().StringVarP(&opts.TFBinary, "tf-binary", "b", opts.TFBinary, "Path to Terraform binary (env: TERRAMAID_TF_BINARY)")
104+
runCmd.Flags().StringVarP(&opts.WorkingDir, "working-dir", "w", opts.WorkingDir, "Working directory for Terraform (env: TERRAMAID_WORKING_DIR)")
105+
106+
// Disable auto-generated string from documentation so that documentation is cleanly built and updated
107+
runCmd.DisableAutoGenTag = true
108+
}

0 commit comments

Comments
 (0)