Skip to content

Commit

Permalink
Check if graphviz is installed (#290)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickhoefler committed Feb 12, 2023
1 parent 8df58ae commit 618e246
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 30 deletions.
78 changes: 49 additions & 29 deletions internal/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ func (d dfgWriter) Write(p []byte) (n int, err error) {
}

// NewRootCmd creates a new root command.
func NewRootCmd(dfgWriter io.Writer, inputFS afero.Fs) *cobra.Command {
func NewRootCmd(
dfgWriter io.Writer, inputFS afero.Fs, dotCmd string,
) *cobra.Command {
rootCmd := &cobra.Command{
Use: "dockerfilegraph",
Short: "Visualize your multi-stage Dockerfile",
Expand All @@ -54,6 +56,13 @@ It creates a visual graph representation of the build process.`,
return printVersion(dfgWriter)
}

// Make sure that graphviz is installed.
_, err = exec.LookPath(dotCmd)
if err != nil {
return
}

// Load and parse the Dockerfile.
dockerfile, err := dockerfile2dot.LoadAndParseDockerfile(
inputFS,
filenameFlag,
Expand Down Expand Up @@ -90,32 +99,8 @@ It creates a visual graph representation of the build process.`,
return
}

var unflattenFile *os.File
if unflattenFlag > 0 {
unflattenFile, err = os.CreateTemp("", "dockerfile.*.dot")
if err != nil {
return
}
defer os.Remove(unflattenFile.Name())

unflattenCmd := exec.Command(
"unflatten",
"-l", strconv.FormatUint(uint64(unflattenFlag), 10),
"-o", unflattenFile.Name(), dotFile.Name(),
)
unflattenCmd.Stdout = dfgWriter
unflattenCmd.Stderr = dfgWriter
err = unflattenCmd.Run()
if err != nil {
return
}

err = unflattenFile.Close()
if err != nil {
return
}

err = os.Rename(unflattenFile.Name(), dotFile.Name())
err = unflatten(dotFile, dfgWriter)
if err != nil {
return
}
Expand All @@ -141,7 +126,7 @@ It creates a visual graph representation of the build process.`,
}
dotArgs = append(dotArgs, dotFile.Name())

out, err := exec.Command("dot", dotArgs...).CombinedOutput()
out, err := exec.Command(dotCmd, dotArgs...).CombinedOutput()
if err != nil {
fmt.Fprintf(dfgWriter,
`Oh no, something went wrong while generating the graph!
Expand All @@ -154,7 +139,7 @@ It creates a visual graph representation of the build process.`,
%s`,
dotFileContent, string(out),
)
os.Exit(1)
return
}

fmt.Fprintf(dfgWriter, "Successfully created %s\n", filename)
Expand Down Expand Up @@ -260,9 +245,44 @@ It creates a visual graph representation of the build process.`,
return rootCmd
}

func unflatten(dotFile *os.File, dfgWriter io.Writer) (err error) {
var unflattenFile *os.File
unflattenFile, err = os.CreateTemp("", "dockerfile.*.dot")
if err != nil {
return
}
defer os.Remove(unflattenFile.Name())

unflattenCmd := exec.Command(
"unflatten",
"-l", strconv.FormatUint(uint64(unflattenFlag), 10),
"-o", unflattenFile.Name(), dotFile.Name(),
)
unflattenCmd.Stdout = dfgWriter
unflattenCmd.Stderr = dfgWriter
err = unflattenCmd.Run()
if err != nil {
return
}

err = unflattenFile.Close()
if err != nil {
return
}

err = os.Rename(unflattenFile.Name(), dotFile.Name())
if err != nil {
return
}

return
}

// Execute executes the root command.
func Execute() {
err := NewRootCmd(dfgWriter{}, afero.NewOsFs()).Execute()
err := NewRootCmd(
dfgWriter{}, afero.NewOsFs(), "dot",
).Execute()
if err != nil {
// Cobra prints the error message
os.Exit(1)
Expand Down
12 changes: 11 additions & 1 deletion internal/cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type test struct {
name string
cliArgs []string
dockerfileContent string
dotCmd string
wantErr bool
wantOut string
wantOutRegex string
Expand Down Expand Up @@ -95,6 +96,12 @@ It creates a visual graph representation of the build process.
wantErr: true,
wantOut: "Error: file with no instructions\n" + usage + "\n",
},
{
name: "graphviz not installed",
dotCmd: "dot-not-found-in-path",
wantErr: true,
wantOut: "Error: exec: \"dot-not-found-in-path\": executable file not found in $PATH\n" + usage + "\n",
},
{
name: "--max-label-length too small",
cliArgs: []string{"--max-label-length", "3"},
Expand Down Expand Up @@ -496,7 +503,10 @@ It creates a visual graph representation of the build process.
t.Run(tt.name, func(t *testing.T) {
buf := new(bytes.Buffer)

command := cmd.NewRootCmd(buf, inputFS)
if tt.dotCmd == "" {
tt.dotCmd = "dot"
}
command := cmd.NewRootCmd(buf, inputFS, tt.dotCmd)
command.SetArgs(tt.cliArgs)

// Redirect Cobra output
Expand Down

0 comments on commit 618e246

Please sign in to comment.