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

Issue:
--output-file is broken for absolute path
### Describe the bug
the leading "/" in the file name seems to be removed

### How can we reproduce it?
```
$ terraform-docs markdown table . --output-file /home/tfoerste/hlag/repos.d/html/modules/grafana/src/../docs/API.md
Error: open home/tfoerste/hlag/repos.d/html/modules/grafana/docs/API.md: no such file or directory

$ ls -l /home/tfoerste/hlag/repos.d/html/modules/grafana/docs/API.md
-rw-r--r-- 1 tfoerste tfoerste 8734 Apr  8 16:51 /home/tfoerste/hlag/repos.d/html/modules/grafana/docs/API.md

$ ls -l /home/tfoerste/hlag/repos.d/html/modules/grafana/src/../docs/API.md
-rw-r--r-- 1 tfoerste tfoerste 8734 Apr  8 16:51 /home/tfoerste/hlag/repos.d/html/modules/grafana/src/../docs/API.md

```

### Environment information
Gentoo Linux
0.12.1


Pull request description:
Support outputing to file for absolute path
<!--
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":
-->

`--output-file` or corresponding `output.file` config can now support
absolute path as well as relative path to root of module foler. For
example all of the followings are valid:

    $ cd /path/to/module
    $ tree .
    .
    ├── docs
    │   └── README.md
    ├── ...
    └── main.tf

    # this works, relative path
    $ terraform-docs markdown table --output-file ../docs/README.md .

    # so does this, absolute path
    $ terraform-docs markdown table --output-file /path/to/module/docs/README.md .

Fixes #462

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.
-->

Manually built and tested all the relative/absolute combination, some specified in #462.

[contribution process]: https://git.io/JtEzg


Patch:
diff --git a/cmd/root.go b/cmd/root.go
index a33800e..7dc3bdd 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -61,7 +61,7 @@ func NewCommand() *cobra.Command {
 	cmd.PersistentFlags().StringSliceVar(&config.Sections.Show, "show", []string{}, "show section ["+cli.AllSections+"]")
 	cmd.PersistentFlags().StringSliceVar(&config.Sections.Hide, "hide", []string{}, "hide section ["+cli.AllSections+"]")
 
-	cmd.PersistentFlags().StringVar(&config.Output.File, "output-file", "", "File in module directory to insert output into (default \"\")")
+	cmd.PersistentFlags().StringVar(&config.Output.File, "output-file", "", "File path to insert output into (default \"\")")
 	cmd.PersistentFlags().StringVar(&config.Output.Mode, "output-mode", "inject", "Output to file method ["+cli.OutputModes+"]")
 	cmd.PersistentFlags().StringVar(&config.Output.Template, "output-template", cli.OutputTemplate, "Output template")
 
diff --git a/docs/reference/asciidoc-document.md b/docs/reference/asciidoc-document.md
index dc0fc21..2e8365a 100644
--- a/docs/reference/asciidoc-document.md
+++ b/docs/reference/asciidoc-document.md
@@ -32,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
       --indent int                  indention level of AsciiDoc sections [1, 2, 3, 4, 5] (default 2)
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/asciidoc-table.md b/docs/reference/asciidoc-table.md
index def87da..a25aac9 100644
--- a/docs/reference/asciidoc-table.md
+++ b/docs/reference/asciidoc-table.md
@@ -32,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
       --indent int                  indention level of AsciiDoc sections [1, 2, 3, 4, 5] (default 2)
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/asciidoc.md b/docs/reference/asciidoc.md
index 7989f91..c2f530e 100644
--- a/docs/reference/asciidoc.md
+++ b/docs/reference/asciidoc.md
@@ -35,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/json.md b/docs/reference/json.md
index 9ad9fef..ec8a71c 100644
--- a/docs/reference/json.md
+++ b/docs/reference/json.md
@@ -30,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/markdown-document.md b/docs/reference/markdown-document.md
index e9e8610..6e7810c 100644
--- a/docs/reference/markdown-document.md
+++ b/docs/reference/markdown-document.md
@@ -33,7 +33,7 @@ terraform-docs markdown document [PATH] [flags]
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
       --indent int                  indention level of Markdown sections [1, 2, 3, 4, 5] (default 2)
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/markdown-table.md b/docs/reference/markdown-table.md
index 4322745..af2f990 100644
--- a/docs/reference/markdown-table.md
+++ b/docs/reference/markdown-table.md
@@ -33,7 +33,7 @@ terraform-docs markdown table [PATH] [flags]
       --header-from string          relative path of a file to read header from (default "main.tf")
       --hide strings                hide section [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
       --indent int                  indention level of Markdown sections [1, 2, 3, 4, 5] (default 2)
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/markdown.md b/docs/reference/markdown.md
index c448e27..98ab0a7 100644
--- a/docs/reference/markdown.md
+++ b/docs/reference/markdown.md
@@ -36,7 +36,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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/pretty.md b/docs/reference/pretty.md
index a42294c..5bccf7f 100644
--- a/docs/reference/pretty.md
+++ b/docs/reference/pretty.md
@@ -30,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/terraform-docs.md b/docs/reference/terraform-docs.md
index 17de9b8..69f3649 100644
--- a/docs/reference/terraform-docs.md
+++ b/docs/reference/terraform-docs.md
@@ -24,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/tfvars-hcl.md b/docs/reference/tfvars-hcl.md
index 8ef0c45..f9e016e 100644
--- a/docs/reference/tfvars-hcl.md
+++ b/docs/reference/tfvars-hcl.md
@@ -30,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/tfvars-json.md b/docs/reference/tfvars-json.md
index a2b29b4..2149b5a 100644
--- a/docs/reference/tfvars-json.md
+++ b/docs/reference/tfvars-json.md
@@ -29,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/tfvars.md b/docs/reference/tfvars.md
index 27a2684..3e40e29 100644
--- a/docs/reference/tfvars.md
+++ b/docs/reference/tfvars.md
@@ -25,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/toml.md b/docs/reference/toml.md
index 655bfe0..96b9237 100644
--- a/docs/reference/toml.md
+++ b/docs/reference/toml.md
@@ -29,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/xml.md b/docs/reference/xml.md
index 039da5f..67c0a11 100644
--- a/docs/reference/xml.md
+++ b/docs/reference/xml.md
@@ -29,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/reference/yaml.md b/docs/reference/yaml.md
index 7a49c06..7c5ed8c 100644
--- a/docs/reference/yaml.md
+++ b/docs/reference/yaml.md
@@ -29,7 +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 [data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
-      --output-file string          File in module directory to insert output into (default "")
+      --output-file string          File path to insert output into (default "")
       --output-mode string          Output to file method [inject, replace] (default "inject")
       --output-template string      Output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
       --output-values               inject output values into outputs (default false)
diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md
index 9ef484f..f7515b9 100644
--- a/docs/user-guide/configuration.md
+++ b/docs/user-guide/configuration.md
@@ -199,6 +199,7 @@ inserted into a template, if provided, before getting saved into the file. This
 template can be customized with `output.template` or `--output-template string`
 CLI flag.
 
+**Note:** `output.file` can be relative to module root or an absolute path.  
 **Note:** `output.template` is optional for mode `replace`.
 
 The default template value is:
diff --git a/docs/user-guide/how-to.md b/docs/user-guide/how-to.md
index c361832..c3fc82b 100644
--- a/docs/user-guide/how-to.md
+++ b/docs/user-guide/how-to.md
@@ -125,10 +125,29 @@ Generated output can be insterted directly into the file. There are two modes of
 insersion: `inject` (default) or `replace`. Take a look at [output] configuration
 for all the details.
 
-```console
+```bash
 terraform-docs markdown table --output-file README.md --output-mode inject /path/to/module
 ```
 
+Note that `--output-file` can be relative to module path or an absolute path in
+filesystem.
+
+```bash
+$ cd /path/to/module
+$ tree .
+.
+├── docs
+│   └── README.md
+├── ...
+└── main.tf
+
+# this works, relative path
+$ terraform-docs markdown table --output-file ../docs/README.md .
+
+# so does this, absolute path
+$ terraform-docs markdown table --output-file /path/to/module/docs/README.md .
+```
+
 ## Generate terraform.tfvars
 
 Since `v0.9.0`
diff --git a/internal/cli/writer.go b/internal/cli/writer.go
index 37de9ef..7e09d59 100644
--- a/internal/cli/writer.go
+++ b/internal/cli/writer.go
@@ -61,7 +61,7 @@ type fileWriter struct {
 }
 
 func (fw *fileWriter) Write(p []byte) (int, error) {
-	filename := filepath.Join(fw.dir, fw.file)
+	filename := fw.fullFilePath()
 
 	var buf bytes.Buffer
 
@@ -124,3 +124,10 @@ func (fw *fileWriter) write(filename string, p []byte) (int, error) {
 	}
 	return len(p), os.WriteFile(filename, p, 0644)
 }
+
+func (fw *fileWriter) fullFilePath() string {
+	if filepath.IsAbs(fw.file) {
+		return fw.file
+	}
+	return filepath.Join(fw.dir, fw.file)
+}


Test patch:
diff --git a/internal/cli/writer_test.go b/internal/cli/writer_test.go
index ac33417..5e4f66d 100644
--- a/internal/cli/writer_test.go
+++ b/internal/cli/writer_test.go
@@ -21,6 +21,38 @@ import (
 	"github.com/terraform-docs/terraform-docs/internal/testutil"
 )
 
+func TestFileWriterFullPath(t *testing.T) {
+	tests := map[string]struct {
+		file     string
+		dir      string
+		expected string
+	}{
+		"Relative": {
+			file:     "file.md",
+			dir:      "/path/to/module",
+			expected: "/path/to/module/file.md",
+		},
+		"Absolute": {
+			file:     "/path/to/module/file.md",
+			dir:      ".",
+			expected: "/path/to/module/file.md",
+		},
+	}
+	for name, tt := range tests {
+		t.Run(name, func(t *testing.T) {
+			assert := assert.New(t)
+
+			writer := &fileWriter{
+				file: tt.file,
+				dir:  tt.dir,
+			}
+
+			actual := writer.fullFilePath()
+			assert.Equal(tt.expected, actual)
+		})
+	}
+}
+
 func TestFileWriter(t *testing.T) {
 	content := "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
 	tests := map[string]struct {