Repository: terraform-docs/terraform-docs (go)

Issue:
Option to disable getting provider version info from dependency lock file
### What problem are you facing?

Recently, in #443, the way provider version requirements were determined was changed.  It now uses the version specified in the dependency lock file if one exists.  However, in our application, we do not commit lock files and want the documentation to reflect the requirements specified in the `.tf` code.

The GitHub action (once it's updated to use `terraform-docs` 0.14+) will still work for us, because there won't be any lock files.  However, we have also set up our project so we can update the docs manually on our local machine before committing changes.  In that case, there will be local lock files present that we want ignored.

### How could terraform-docs help solve your problem?

We would like an option to configure the way provider versions are handled so that we can decide whether or not to use the lock files.

Pull request description:
Ignore extracting versions from terraform.lock.hcl
<!--
Thank you for helping to improve terraform-docs!

Please read through https://git.io/JtEzg if this is your first time opening a
terraform-docs pull request. Find us in https://slack.terraform-docs.io/messages/dev if
you need any help contributing.
-->

### Description of your changes

<!--
Briefly describe what this pull request does. Be sure to direct your reviewers'
attention to anything that needs special consideration.

We love pull requests that resolve an open terraform-docs issue. If yours does, you
can uncomment the below line to indicate which issue your PR fixes, for example
"Fixes #500":
-->

New flag, `--lockfile`, is added to control whether ignore reading
`.terraform.lock.hcl` file in an attempt to extract the exact version of
provider being used or not. Default is true.

If set to true, exact version of provider available in lock file at the
time of execution will be extracted. If set to false, the version in `.tf`
file will be used (either exact, or a constrained version: >=, ~>, ...)

Fixes #517

I have:

- [x] Read and followed terraform-docs' [contribution process].
- [x] All tests pass when I run `make test`.

### How has this code been tested

<!--
Before reviewers can be confident in the correctness of this pull request, it
needs to tested and shown to be correct. Briefly describe the testing that has
already been done or which is planned for this change.
-->

New test case added to `TestLoadProviders` to read test module with the lockfile.

[contribution process]: https://git.io/JtEzg


Patch:
diff --git a/cmd/root.go b/cmd/root.go
index 8c8f03c..8b062f7 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -73,6 +73,8 @@ func NewCommand() *cobra.Command {
 	cmd.PersistentFlags().StringVar(&config.HeaderFrom, "header-from", "main.tf", "relative path of a file to read header from")
 	cmd.PersistentFlags().StringVar(&config.FooterFrom, "footer-from", "", "relative path of a file to read footer from (default \"\")")
 
+	cmd.PersistentFlags().BoolVar(&config.Settings.LockFile, "lockfile", true, "read .terraform.lock.hcl if exist")
+
 	cmd.PersistentFlags().BoolVar(&config.OutputValues.Enabled, "output-values", false, "inject output values into outputs (default false)")
 	cmd.PersistentFlags().StringVar(&config.OutputValues.From, "output-values-from", "", "inject output values from file into outputs (default \"\")")
 
diff --git a/docs/reference/asciidoc-document.md b/docs/reference/asciidoc-document.md
index 24c2bc0..a2f9dd9 100644
--- a/docs/reference/asciidoc-document.md
+++ b/docs/reference/asciidoc-document.md
@@ -32,6 +32,7 @@ terraform-docs asciidoc document [PATH] [flags]
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
       --indent int                  indention level of AsciiDoc sections [1, 2, 3, 4, 5] (default 2)
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/asciidoc-table.md b/docs/reference/asciidoc-table.md
index 3565e1e..7d3244c 100644
--- a/docs/reference/asciidoc-table.md
+++ b/docs/reference/asciidoc-table.md
@@ -32,6 +32,7 @@ terraform-docs asciidoc table [PATH] [flags]
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
       --indent int                  indention level of AsciiDoc sections [1, 2, 3, 4, 5] (default 2)
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/asciidoc.md b/docs/reference/asciidoc.md
index f8d09ff..b1a9d5f 100644
--- a/docs/reference/asciidoc.md
+++ b/docs/reference/asciidoc.md
@@ -35,6 +35,7 @@ terraform-docs asciidoc [PATH] [flags]
       --footer-from string          relative path of a file to read footer from (default "")
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/json.md b/docs/reference/json.md
index 2b36d32..560acce 100644
--- a/docs/reference/json.md
+++ b/docs/reference/json.md
@@ -30,6 +30,7 @@ terraform-docs json [PATH] [flags]
       --footer-from string          relative path of a file to read footer from (default "")
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/markdown-document.md b/docs/reference/markdown-document.md
index 61010b1..307b894 100644
--- a/docs/reference/markdown-document.md
+++ b/docs/reference/markdown-document.md
@@ -34,6 +34,7 @@ terraform-docs markdown document [PATH] [flags]
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
       --html                        use HTML tags in genereted output (default true)
       --indent int                  indention level of Markdown sections [1, 2, 3, 4, 5] (default 2)
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/markdown-table.md b/docs/reference/markdown-table.md
index 9cdb613..13e7dbc 100644
--- a/docs/reference/markdown-table.md
+++ b/docs/reference/markdown-table.md
@@ -34,6 +34,7 @@ terraform-docs markdown table [PATH] [flags]
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
       --html                        use HTML tags in genereted output (default true)
       --indent int                  indention level of Markdown sections [1, 2, 3, 4, 5] (default 2)
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/markdown.md b/docs/reference/markdown.md
index 242255f..bd80e9a 100644
--- a/docs/reference/markdown.md
+++ b/docs/reference/markdown.md
@@ -37,6 +37,7 @@ terraform-docs markdown [PATH] [flags]
       --footer-from string          relative path of a file to read footer from (default "")
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/pretty.md b/docs/reference/pretty.md
index d18a556..0c74f4b 100644
--- a/docs/reference/pretty.md
+++ b/docs/reference/pretty.md
@@ -30,6 +30,7 @@ terraform-docs pretty [PATH] [flags]
       --footer-from string          relative path of a file to read footer from (default "")
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/terraform-docs.md b/docs/reference/terraform-docs.md
index 9aec99b..b2f4047 100644
--- a/docs/reference/terraform-docs.md
+++ b/docs/reference/terraform-docs.md
@@ -24,6 +24,7 @@ terraform-docs [PATH] [flags]
       --header-from string          relative path of a file to read header from (default "main.tf")
   -h, --help                        help for terraform-docs
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/tfvars-hcl.md b/docs/reference/tfvars-hcl.md
index 04ead66..0160393 100644
--- a/docs/reference/tfvars-hcl.md
+++ b/docs/reference/tfvars-hcl.md
@@ -30,6 +30,7 @@ terraform-docs tfvars hcl [PATH] [flags]
       --footer-from string          relative path of a file to read footer from (default "")
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/tfvars-json.md b/docs/reference/tfvars-json.md
index 41477ef..39a500f 100644
--- a/docs/reference/tfvars-json.md
+++ b/docs/reference/tfvars-json.md
@@ -29,6 +29,7 @@ terraform-docs tfvars json [PATH] [flags]
       --footer-from string          relative path of a file to read footer from (default "")
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/tfvars.md b/docs/reference/tfvars.md
index ba312af..ac31abf 100644
--- a/docs/reference/tfvars.md
+++ b/docs/reference/tfvars.md
@@ -25,6 +25,7 @@ Generate terraform.tfvars of inputs.
       --footer-from string          relative path of a file to read footer from (default "")
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/toml.md b/docs/reference/toml.md
index c4f0edc..84a924a 100644
--- a/docs/reference/toml.md
+++ b/docs/reference/toml.md
@@ -29,6 +29,7 @@ terraform-docs toml [PATH] [flags]
       --footer-from string          relative path of a file to read footer from (default "")
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/xml.md b/docs/reference/xml.md
index 080a40c..52cfd3d 100644
--- a/docs/reference/xml.md
+++ b/docs/reference/xml.md
@@ -29,6 +29,7 @@ terraform-docs xml [PATH] [flags]
       --footer-from string          relative path of a file to read footer from (default "")
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/reference/yaml.md b/docs/reference/yaml.md
index ea2f395..13e4b78 100644
--- a/docs/reference/yaml.md
+++ b/docs/reference/yaml.md
@@ -29,6 +29,7 @@ terraform-docs yaml [PATH] [flags]
       --footer-from string          relative path of a file to read footer from (default "")
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
+      --lockfile                    read .terraform.lock.hcl if exist (default true)
       --output-check                check if content of output file is up to date (default false)
       --output-file string          file path to insert output into (default "")
       --output-mode string          output to file method [inject, replace] (default "inject")
diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md
index c9084c4..89a186c 100644
--- a/docs/user-guide/configuration.md
+++ b/docs/user-guide/configuration.md
@@ -98,6 +98,7 @@ settings:
   escape: true
   html: true
   indent: 2
+  lockfile: true
   required: true
   sensitive: true
   type: true
diff --git a/internal/cli/config.go b/internal/cli/config.go
index 1f3cbc7..31f9c52 100644
--- a/internal/cli/config.go
+++ b/internal/cli/config.go
@@ -373,6 +373,7 @@ type settings struct {
 	Escape      bool `mapstructure:"escape"`
 	HTML        bool `mapstructure:"html"`
 	Indent      int  `mapstructure:"indent"`
+	LockFile    bool `mapstructure:"lockfile"`
 	Required    bool `mapstructure:"required"`
 	Sensitive   bool `mapstructure:"sensitive"`
 	Type        bool `mapstructure:"type"`
@@ -387,6 +388,7 @@ func defaultSettings() settings {
 		Escape:      true,
 		HTML:        true,
 		Indent:      2,
+		LockFile:    true,
 		Required:    true,
 		Sensitive:   true,
 		Type:        true,
@@ -469,6 +471,9 @@ func (c *Config) extract() (*print.Settings, *terraform.Options) {
 	options.ShowFooter = settings.ShowFooter
 	options.FooterFromFile = c.FooterFrom
 
+	// terraform.lock.hcl file
+	options.UseLockFile = c.Settings.LockFile
+
 	// sections
 	settings.ShowDataSources = c.Sections.dataSources
 	settings.ShowInputs = c.Sections.inputs
diff --git a/internal/terraform/module.go b/internal/terraform/module.go
index f820a08..f46fb18 100644
--- a/internal/terraform/module.go
+++ b/internal/terraform/module.go
@@ -411,14 +411,17 @@ func loadProviders(tfmodule *tfconfig.Module, options *Options) []*Provider {
 		Provider []provider `hcl:"provider,block"`
 	}
 	lock := make(map[string]provider)
-	var lf lockfile
-
-	filename := filepath.Join(options.Path, ".terraform.lock.hcl")
-	if err := hclsimple.DecodeFile(filename, nil, &lf); err == nil {
-		for i := range lf.Provider {
-			segments := strings.Split(lf.Provider[i].Name, "/")
-			name := segments[len(segments)-1]
-			lock[name] = lf.Provider[i]
+
+	if options.UseLockFile {
+		var lf lockfile
+
+		filename := filepath.Join(options.Path, ".terraform.lock.hcl")
+		if err := hclsimple.DecodeFile(filename, nil, &lf); err == nil {
+			for i := range lf.Provider {
+				segments := strings.Split(lf.Provider[i].Name, "/")
+				name := segments[len(segments)-1]
+				lock[name] = lf.Provider[i]
+			}
 		}
 	}
 
diff --git a/internal/terraform/options.go b/internal/terraform/options.go
index 40f60b1..8126b3d 100644
--- a/internal/terraform/options.go
+++ b/internal/terraform/options.go
@@ -31,6 +31,7 @@ type Options struct {
 	HeaderFromFile   string
 	ShowFooter       bool
 	FooterFromFile   string
+	UseLockFile      bool
 	SortBy           *SortBy
 	OutputValues     bool
 	OutputValuesPath string
@@ -44,6 +45,7 @@ func NewOptions() *Options {
 		HeaderFromFile:   "main.tf",
 		ShowFooter:       false,
 		FooterFromFile:   "",
+		UseLockFile:      true,
 		SortBy:           &SortBy{Name: false, Required: false, Type: false},
 		OutputValues:     false,
 		OutputValuesPath: "",


Test patch:
diff --git a/internal/terraform/module_test.go b/internal/terraform/module_test.go
index ad56b27..7b8589b 100644
--- a/internal/terraform/module_test.go
+++ b/internal/terraform/module_test.go
@@ -13,6 +13,7 @@ package terraform
 import (
 	"io/ioutil"
 	"path/filepath"
+	"sort"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -665,32 +666,43 @@ func TestLoadOutputsValues(t *testing.T) {
 
 func TestLoadProviders(t *testing.T) {
 	type expected struct {
-		providers int
+		providers []string
 	}
 	tests := []struct {
 		name     string
 		path     string
+		lockfile bool
 		expected expected
 	}{
 		{
-			name: "load module providers from path",
-			path: "full-example",
+			name:     "load module providers from path",
+			path:     "full-example",
+			lockfile: false,
 			expected: expected{
-				providers: 3,
+				providers: []string{"aws->= 2.15.0", "null-", "tls-"},
 			},
 		},
 		{
-			name: "load module providers from path",
-			path: "with-lock-file",
+			name:     "load module providers from path",
+			path:     "with-lock-file",
+			lockfile: true,
+			expected: expected{
+				providers: []string{"aws-3.42.0", "null-3.1.0", "tls-3.1.0"},
+			},
+		},
+		{
+			name:     "load module providers from path",
+			path:     "with-lock-file",
+			lockfile: false,
 			expected: expected{
-				providers: 3,
+				providers: []string{"aws->= 2.15.0", "null-", "tls-"},
 			},
 		},
 		{
 			name: "load module providers from path",
 			path: "no-providers",
 			expected: expected{
-				providers: 0,
+				providers: []string{},
 			},
 		},
 	}
@@ -698,12 +710,21 @@ func TestLoadProviders(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			assert := assert.New(t)
 			options, _ := NewOptions().With(&Options{
-				Path: tt.path,
+				Path: filepath.Join("testdata", tt.path),
 			})
+			options.UseLockFile = tt.lockfile
 			module, _ := loadModule(filepath.Join("testdata", tt.path))
 			providers := loadProviders(module, options)
 
-			assert.Equal(tt.expected.providers, len(providers))
+			actual := []string{}
+
+			for _, p := range providers {
+				actual = append(actual, p.FullName()+"-"+string(p.Version))
+				providers[0].FullName()
+			}
+			sort.Strings(actual)
+
+			assert.Equal(tt.expected.providers, actual)
 		})
 	}
 }