Repository: patrickhoefler/dockerfilegraph
Pull request #170 (2022-10-07 16:15:10)

Add an option to specify filename
Adds an option to specify the Dockerfile to use, just like `docker build` supports.

patrickhoefler: Hey @iamthad, thanks for your contribution 🙂

According to the failing lint and test, something seems to be not quite right yet.

Could you please also add a unit test for the new functionality?

Once again, thank you 🙂
patrickhoefler: One final request: Could you please also update the usage output in the readme? Thank you!

Patch:
diff --git a/README.md b/README.md
index 32e68d0..2cfc4e5 100644
--- a/README.md
+++ b/README.md
@@ -103,12 +103,13 @@ Usage:
   dockerfilegraph [flags]
 
 Flags:
-  -d, --dpi int   dots per inch of the PNG export (default 96)
-  -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 (default pdf)
-      --version   display the version of dockerfilegraph
+  -d, --dpi int           dots per inch of the PNG export (default 96)
+  -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 (default pdf)
+      --version           display the version of dockerfilegraph
 ```
 
 ## License
diff --git a/internal/cmd/root.go b/internal/cmd/root.go
index eac9810..1173964 100644
--- a/internal/cmd/root.go
+++ b/internal/cmd/root.go
@@ -14,11 +14,12 @@ import (
 )
 
 var (
-	dpiFlag     int
-	legendFlag  bool
-	layersFlag  bool
-	outputFlag  enum
-	versionFlag bool
+	dpiFlag      int
+	filenameFlag string
+	layersFlag   bool
+	legendFlag   bool
+	outputFlag   enum
+	versionFlag  bool
 )
 
 // dfgWriter is a writer that prints to stdout. When testing, we
@@ -43,7 +44,7 @@ It outputs a graph representation of the build process.`,
 				return printVersion(dfgWriter)
 			}
 
-			dockerfile, err := dockerfile2dot.LoadAndParseDockerfile(inputFS)
+			dockerfile, err := dockerfile2dot.LoadAndParseDockerfile(inputFS, filenameFlag)
 			if err != nil {
 				return
 			}
@@ -108,6 +109,14 @@ It outputs a graph representation of the build process.`,
 		"dots per inch of the PNG export",
 	)
 
+	rootCmd.Flags().StringVarP(
+		&filenameFlag,
+		"filename",
+		"f",
+		"Dockerfile",
+		"name of the Dockerfile",
+	)
+
 	rootCmd.Flags().BoolVar(
 		&layersFlag,
 		"layers",
diff --git a/internal/dockerfile2dot/load.go b/internal/dockerfile2dot/load.go
index a6b9ebf..48a6fdd 100644
--- a/internal/dockerfile2dot/load.go
+++ b/internal/dockerfile2dot/load.go
@@ -11,11 +11,11 @@ import (
 
 // LoadAndParseDockerfile looks for the Dockerfile and returns a
 // SimplifiedDockerfile.
-func LoadAndParseDockerfile(inputFS afero.Fs) (SimplifiedDockerfile, error) {
-	content, err := afero.ReadFile(inputFS, "Dockerfile")
+func LoadAndParseDockerfile(inputFS afero.Fs, filename string) (SimplifiedDockerfile, error) {
+	content, err := afero.ReadFile(inputFS, filename)
 	if err != nil {
 		if errors.Is(err, fs.ErrNotExist) {
-			return SimplifiedDockerfile{}, errors.New("could not find any Dockerfile in the current working directory")
+			return SimplifiedDockerfile{}, errors.New("could not find a Dockerfile at " + filename)
 		}
 		panic(err)
 	}


Test patch:
diff --git a/internal/cmd/root_test.go b/internal/cmd/root_test.go
index e4d7b65..c1bb2fb 100644
--- a/internal/cmd/root_test.go
+++ b/internal/cmd/root_test.go
@@ -26,12 +26,13 @@ var usage = `Usage:
   dockerfilegraph [flags]
 
 Flags:
-  -d, --dpi int   dots per inch of the PNG export (default 96)
-  -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 (default pdf)
-      --version   display the version of dockerfilegraph
+  -d, --dpi int           dots per inch of the PNG export (default 96)
+  -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 (default pdf)
+      --version           display the version of dockerfilegraph
 `
 
 // Taken from example/Dockerfile.
diff --git a/internal/dockerfile2dot/load_test.go b/internal/dockerfile2dot/load_test.go
index 7796715..60618a4 100644
--- a/internal/dockerfile2dot/load_test.go
+++ b/internal/dockerfile2dot/load_test.go
@@ -9,12 +9,16 @@ import (
 
 func TestLoadAndParseDockerfile(t *testing.T) {
 	type args struct {
-		inputFS afero.Fs
+		inputFS  afero.Fs
+		filename string
 	}
 
 	dockerfileFS := afero.NewMemMapFs()
 	_ = afero.WriteFile(dockerfileFS, "Dockerfile", []byte(`FROM scratch`), 0644)
 
+	dockerfile2FS := afero.NewMemMapFs()
+	_ = afero.WriteFile(dockerfile2FS, "Dockerfile2", []byte(`FROM docker`), 0644)
+
 	tests := []struct {
 		name    string
 		args    args
@@ -24,14 +28,24 @@ func TestLoadAndParseDockerfile(t *testing.T) {
 		{
 			name: "no Dockerfile found",
 			args: args{
-				inputFS: afero.NewMemMapFs(),
+				inputFS:  afero.NewMemMapFs(),
+				filename: "Dockerfile",
+			},
+			wantErr: true,
+		},
+		{
+			name: "wrong filename",
+			args: args{
+				inputFS:  dockerfileFS,
+				filename: "Dockerfile2",
 			},
 			wantErr: true,
 		},
 		{
 			name: "Dockerfile found",
 			args: args{
-				inputFS: dockerfileFS,
+				inputFS:  dockerfileFS,
+				filename: "Dockerfile",
 			},
 			want: SimplifiedDockerfile{
 				ExternalImages: []ExternalImage{{Name: "scratch"}},
@@ -46,11 +60,30 @@ func TestLoadAndParseDockerfile(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "custom Dockerfile found",
+			args: args{
+				inputFS:  dockerfile2FS,
+				filename: "Dockerfile2",
+			},
+			want: SimplifiedDockerfile{
+				ExternalImages: []ExternalImage{{Name: "docker"}},
+				Stages: []Stage{
+					{
+						Layers: []Layer{
+							{
+								Label:   "FROM docker",
+								WaitFor: WaitFor{Name: "docker", Type: waitForType(from)}},
+						},
+					},
+				},
+			},
+		},
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := LoadAndParseDockerfile(tt.args.inputFS)
+			got, err := LoadAndParseDockerfile(tt.args.inputFS, tt.args.filename)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("LoadAndParseDockerfile() error = %v, wantErr %v", err, tt.wantErr)
 				return