Skip to content

Commit

Permalink
Add the concentrate flag (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickhoefler authored Jan 28, 2023
1 parent 2c0505d commit d047abd
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 129 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ dockerfilegraph

# dockerfilegraph
Dockerfile.*
!Dockerfile.alpine
!/Dockerfile.alpine
!example/Dockerfile.large
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,14 @@ Usage:
dockerfilegraph [flags]
Flags:
-c, --concentrate concentrate the edges (default false)
-d, --dpi int dots per inch of the PNG export (default 96)
-e, --edgestyle style of the graph edges, one of: default, solid (default default)
-f, --filename string name of the Dockerfile (default "Dockerfile")
-h, --help help for dockerfilegraph
--layers display all layers (default false)
--legend add a legend (default false)
-o, --output output file format, one of: canon, dot, pdf, png, svg (default pdf)
-o, --output output file format, one of: canon, dot, pdf, png, raw, svg (default pdf)
--version display the version of dockerfilegraph
```

Expand Down
16 changes: 16 additions & 0 deletions example/Dockerfile.large
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM scratch AS base
COPY --from=external_0 file_0_0 /
COPY --from=external_1 file_1_0 /
COPY --from=external_2 file_2_0 /
COPY --from=external_3 file_3_0 /
COPY --from=external_4 file_4_0 /
COPY --from=external_5 file_5_0 /
COPY --from=external_6 file_6_0 /

FROM base AS build
RUN --mount=type=cache,from=buildcache,source=/build,target=/build make build

FROM build AS final
COPY --from=supplemental file_supplemental_0 /
COPY --from=supplemental file_supplemental_1 /
COPY --from=supplemental file_supplemental_2 /
42 changes: 33 additions & 9 deletions internal/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import (
)

var (
dpiFlag int
edgeStyleFlag enum
filenameFlag string
layersFlag bool
legendFlag bool
outputFlag enum
versionFlag bool
concentrateFlag bool
dpiFlag int
edgeStyleFlag enum
filenameFlag string
layersFlag bool
legendFlag bool
outputFlag enum
versionFlag bool
)

// dfgWriter is a writer that prints to stdout. When testing, we
Expand Down Expand Up @@ -57,7 +58,11 @@ It outputs a graph representation of the build process.`,
defer os.Remove(dotFile.Name())

dotFileContent := dockerfile2dot.BuildDotFile(
dockerfile, legendFlag, layersFlag, edgeStyleFlag.String(),
dockerfile,
concentrateFlag,
edgeStyleFlag.String(),
layersFlag,
legendFlag,
)

_, err = dotFile.Write([]byte(dotFileContent))
Expand All @@ -72,6 +77,17 @@ It outputs a graph representation of the build process.`,

filename := "Dockerfile." + outputFlag.String()

if outputFlag.String() == "raw" {
err = os.Rename(dotFile.Name(), filename)
if err != nil {
return
}

fmt.Fprintf(dfgWriter, "Successfully created %s\n", filename)

return
}

dotArgs := []string{
"-T" + outputFlag.String(),
"-o" + filename,
Expand Down Expand Up @@ -104,6 +120,14 @@ It outputs a graph representation of the build process.`,
}

// Flags
rootCmd.Flags().BoolVarP(
&concentrateFlag,
"concentrate",
"c",
false,
"concentrate the edges (default false)",
)

rootCmd.Flags().IntVarP(
&dpiFlag,
"dpi",
Expand Down Expand Up @@ -142,7 +166,7 @@ It outputs a graph representation of the build process.`,
"add a legend (default false)",
)

outputFlag = newEnum("pdf", "canon", "dot", "png", "svg")
outputFlag = newEnum("pdf", "canon", "dot", "png", "raw", "svg")
rootCmd.Flags().VarP(
&outputFlag,
"output",
Expand Down
170 changes: 55 additions & 115 deletions internal/cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ var usage = `Usage:
dockerfilegraph [flags]
Flags:
-c, --concentrate concentrate the edges (default false)
-d, --dpi int dots per inch of the PNG export (default 96)
-e, --edgestyle style of the graph edges, one of: default, solid (default default)
-f, --filename string name of the Dockerfile (default "Dockerfile")
-h, --help help for dockerfilegraph
--layers display all layers (default false)
--legend add a legend (default false)
-o, --output output file format, one of: canon, dot, pdf, png, svg (default pdf)
-o, --output output file format, one of: canon, dot, pdf, png, raw, svg (default pdf)
--version display the version of dockerfilegraph
`

Expand Down Expand Up @@ -141,126 +142,64 @@ It outputs a graph representation of the build process.
},
{
name: "layers flag",
cliArgs: []string{"--layers", "-o", "canon"},
wantOut: "Successfully created Dockerfile.canon\n",
wantOutFile: "Dockerfile.canon",
cliArgs: []string{"--layers", "-o", "raw"},
wantOut: "Successfully created Dockerfile.raw\n",
wantOutFile: "Dockerfile.raw",
//nolint:lll
wantOutFileContent: `digraph G {
graph [compound=true,
nodesep=1,
rankdir=LR
];
node [label="\N"];
compound=true;
nodesep=1;
rankdir=LR;
stage_0_layer_0->stage_0_layer_1;
external_image_0->stage_0_layer_0;
stage_1_layer_0->stage_1_layer_1;
external_image_1->stage_1_layer_0;
external_image_2->stage_1_layer_1[ arrowhead=ediamond, style=dotted ];
stage_2_layer_0->stage_2_layer_1;
stage_2_layer_1->stage_2_layer_2;
stage_2_layer_2->stage_2_layer_3;
external_image_3->stage_2_layer_0;
stage_0_layer_1->stage_2_layer_1[ arrowhead=empty, ltail=cluster_stage_0, style=dashed ];
stage_1_layer_1->stage_2_layer_2[ arrowhead=empty, ltail=cluster_stage_1, style=dashed ];
subgraph cluster_stage_0 {
graph [label=ubuntu,
margin=16
];
stage_0_layer_0 [fillcolor=white,
label="FROM ubuntu:lates...",
penwidth=0.5,
shape=box,
style="filled,rounded",
width=2];
stage_0_layer_1 [fillcolor=white,
label="RUN apt-get upd...",
penwidth=0.5,
shape=box,
style="filled,rounded",
width=2];
stage_0_layer_0 -> stage_0_layer_1;
}
label=ubuntu;
margin=16;
stage_0_layer_0 [ fillcolor=white, label="FROM ubuntu:lates...", penwidth=0.5, shape=box, style="filled,rounded", width=2 ];
stage_0_layer_1 [ fillcolor=white, label="RUN apt-get upd...", penwidth=0.5, shape=box, style="filled,rounded", width=2 ];
}
;
subgraph cluster_stage_1 {
graph [label=build,
margin=16
];
stage_1_layer_0 [fillcolor=white,
label="FROM golang:1.19 ...",
penwidth=0.5,
shape=box,
style="filled,rounded",
width=2];
stage_1_layer_1 [fillcolor=white,
label="RUN --mount=type=...",
penwidth=0.5,
shape=box,
style="filled,rounded",
width=2];
stage_1_layer_0 -> stage_1_layer_1;
}
label=build;
margin=16;
stage_1_layer_0 [ fillcolor=white, label="FROM golang:1.19 ...", penwidth=0.5, shape=box, style="filled,rounded", width=2 ];
stage_1_layer_1 [ fillcolor=white, label="RUN --mount=type=...", penwidth=0.5, shape=box, style="filled,rounded", width=2 ];
}
;
subgraph cluster_stage_2 {
graph [fillcolor=grey90,
label=release,
margin=16,
style=filled
];
stage_2_layer_0 [fillcolor=white,
label="FROM scratch AS r...",
penwidth=0.5,
shape=box,
style="filled,rounded",
width=2];
stage_2_layer_1 [fillcolor=white,
label="COPY --from=ubunt...",
penwidth=0.5,
shape=box,
style="filled,rounded",
width=2];
stage_2_layer_0 -> stage_2_layer_1;
stage_2_layer_2 [fillcolor=white,
label="COPY --from=build...",
penwidth=0.5,
shape=box,
style="filled,rounded",
width=2];
stage_2_layer_1 -> stage_2_layer_2;
stage_2_layer_3 [fillcolor=white,
label="ENTRYPOINT ['/exa...",
penwidth=0.5,
shape=box,
style="filled,rounded",
width=2];
stage_2_layer_2 -> stage_2_layer_3;
}
stage_0_layer_1 -> stage_2_layer_1 [arrowhead=empty,
ltail=cluster_stage_0,
style=dashed];
external_image_0 [color=grey20,
fontcolor=grey20,
label="ubuntu:latest",
shape=box,
style="dashed,rounded",
width=2];
external_image_0 -> stage_0_layer_0;
stage_1_layer_1 -> stage_2_layer_2 [arrowhead=empty,
ltail=cluster_stage_1,
style=dashed];
external_image_1 [color=grey20,
fontcolor=grey20,
label="golang:1.19",
shape=box,
style="dashed,rounded",
width=2];
external_image_1 -> stage_1_layer_0;
external_image_2 [color=grey20,
fontcolor=grey20,
label=buildcache,
shape=box,
style="dashed,rounded",
width=2];
external_image_2 -> stage_1_layer_1 [arrowhead=ediamond,
style=dotted];
external_image_3 [color=grey20,
fontcolor=grey20,
label=scratch,
shape=box,
style="dashed,rounded",
width=2];
external_image_3 -> stage_2_layer_0;
fillcolor=grey90;
label=release;
margin=16;
style=filled;
stage_2_layer_0 [ fillcolor=white, label="FROM scratch AS r...", penwidth=0.5, shape=box, style="filled,rounded", width=2 ];
stage_2_layer_1 [ fillcolor=white, label="COPY --from=ubunt...", penwidth=0.5, shape=box, style="filled,rounded", width=2 ];
stage_2_layer_2 [ fillcolor=white, label="COPY --from=build...", penwidth=0.5, shape=box, style="filled,rounded", width=2 ];
stage_2_layer_3 [ fillcolor=white, label="ENTRYPOINT ['/exa...", penwidth=0.5, shape=box, style="filled,rounded", width=2 ];
}
;
external_image_0 [ color=grey20, fontcolor=grey20, label="ubuntu:latest", shape=box, style="dashed,rounded", width=2 ];
external_image_1 [ color=grey20, fontcolor=grey20, label="golang:1.19", shape=box, style="dashed,rounded", width=2 ];
external_image_2 [ color=grey20, fontcolor=grey20, label="buildcache", shape=box, style="dashed,rounded", width=2 ];
external_image_3 [ color=grey20, fontcolor=grey20, label="scratch", shape=box, style="dashed,rounded", width=2 ];
}
`,
},
{
name: "layers flag with solid edges",
cliArgs: []string{"--layers", "-o", "canon", "-e", "solid"},
cliArgs: []string{"--layers", "-e", "solid", "-o", "canon"},
wantOut: "Successfully created Dockerfile.canon\n",
wantOutFile: "Dockerfile.canon",
wantOutFileContent: `digraph G {
Expand Down Expand Up @@ -375,12 +314,13 @@ It outputs a graph representation of the build process.
`,
},
{
name: "legend flag",
cliArgs: []string{"--legend", "-o", "canon"},
name: "legend flag with concentrated edges",
cliArgs: []string{"--legend", "--concentrate", "-o", "canon"},
wantOut: "Successfully created Dockerfile.canon\n",
wantOutFile: "Dockerfile.canon",
wantOutFileContent: `digraph G {
graph [compound=true,
concentrate=true,
nodesep=1,
rankdir=LR
];
Expand Down Expand Up @@ -459,7 +399,7 @@ It outputs a graph representation of the build process.
},
{
name: "legend flag with solid edges",
cliArgs: []string{"--legend", "-o", "canon", "-e", "solid"},
cliArgs: []string{"--legend", "-e", "solid", "-o", "canon"},
wantOut: "Successfully created Dockerfile.canon\n",
wantOutFile: "Dockerfile.canon",
wantOutFileContent: `digraph G {
Expand Down
8 changes: 7 additions & 1 deletion internal/dockerfile2dot/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import (

// BuildDotFile builds a GraphViz .dot file from a simplified Dockerfile
func BuildDotFile(
simplifiedDockerfile SimplifiedDockerfile, legend bool, layers bool,
simplifiedDockerfile SimplifiedDockerfile,
concentrate bool,
edgeStyle string,
layers bool,
legend bool,
) string {
// Create a new graph
graph := gographviz.NewEscape()
Expand All @@ -20,6 +23,9 @@ func BuildDotFile(
_ = graph.AddAttr("G", "compound", "true") // allow edges between clusters
_ = graph.AddAttr("G", "nodesep", "1")
_ = graph.AddAttr("G", "rankdir", "LR")
if concentrate {
_ = graph.AddAttr("G", "concentrate", "true")
}

// Add the legend if requested
if legend {
Expand Down
8 changes: 6 additions & 2 deletions internal/dockerfile2dot/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import (

func TestBuildDotFile(t *testing.T) {
type args struct {
simplifiedDockerfile SimplifiedDockerfile
concentrate bool
edgeStyle string
layers bool
legend bool
simplifiedDockerfile SimplifiedDockerfile
}
tests := []struct {
name string
Expand Down Expand Up @@ -85,8 +86,11 @@ func TestBuildDotFile(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := BuildDotFile(
tt.args.simplifiedDockerfile, tt.args.legend, tt.args.layers,
tt.args.simplifiedDockerfile,
tt.args.concentrate,
tt.args.edgeStyle,
tt.args.layers,
tt.args.legend,
); !strings.Contains(got, tt.wantContains) {
t.Errorf(
"BuildDotFile() = %v, did not contain %v", got, tt.wantContains,
Expand Down

0 comments on commit d047abd

Please sign in to comment.