mirror of
https://github.com/terraform-docs/terraform-docs.git
synced 2026-03-27 04:48:33 +07:00
Save generated content directly into a file
There are two modes for saving into file:
- inject: partially replace the target file with generated output
- replace: completely replace the target file with generated output
The output generated by formatters (markdown, asciidoc, etc) will be
inserted into a template before getting saved into the file. This
template can be customized with '--output-template' CLI flag or
corresponding item in '.terraform-docs.yml' config file. Its default
value is:
```
<!-- BEGIN_TF_DOCS -->
{{ .Content }}
<!-- END_TF_DOCS -->
```
This consists of three items, all of them are mandatory:
- begin comment
- content variable
- end comment
You may change the wording of comment as you wish, but the comment must
be present in the template.
Signed-off-by: Khosrow Moossavi <khos2ow@gmail.com>
This commit is contained in:
@@ -57,11 +57,15 @@ func NewCommand() *cobra.Command {
|
||||
// flags
|
||||
cmd.PersistentFlags().StringVarP(&config.File, "config", "c", ".terraform-docs.yml", "config file name")
|
||||
|
||||
cmd.PersistentFlags().StringSliceVar(&config.Sections.Show, "show", []string{}, "show section [header, inputs, modules, outputs, providers, requirements, resources]")
|
||||
cmd.PersistentFlags().StringSliceVar(&config.Sections.Hide, "hide", []string{}, "hide section [header, inputs, modules, outputs, providers, requirements, resources]")
|
||||
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().BoolVar(&config.Sections.ShowAll, "show-all", true, "show all sections")
|
||||
cmd.PersistentFlags().BoolVar(&config.Sections.HideAll, "hide-all", false, "hide all sections (default false)")
|
||||
|
||||
cmd.PersistentFlags().StringVar(&config.Output.File, "output-file", "", "File in module directory 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")
|
||||
|
||||
cmd.PersistentFlags().BoolVar(&config.Sort.Enabled, "sort", true, "sort items")
|
||||
cmd.PersistentFlags().BoolVar(&config.Sort.By.Required, "sort-by-required", false, "sort items by name and print required ones first (default false)")
|
||||
cmd.PersistentFlags().BoolVar(&config.Sort.By.Type, "sort-by-type", false, "sort items by type of them (default false)")
|
||||
|
||||
@@ -32,6 +32,9 @@ terraform-docs asciidoc document [PATH] [flags]
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--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-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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--required show Required column or section (default true)
|
||||
|
||||
@@ -32,6 +32,9 @@ terraform-docs asciidoc table [PATH] [flags]
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--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-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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--required show Required column or section (default true)
|
||||
|
||||
@@ -35,6 +35,9 @@ terraform-docs asciidoc [PATH] [flags]
|
||||
--header-from string relative path of a file to read header from (default "main.tf")
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -30,6 +30,9 @@ terraform-docs json [PATH] [flags]
|
||||
--header-from string relative path of a file to read header from (default "main.tf")
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -33,6 +33,9 @@ terraform-docs markdown document [PATH] [flags]
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--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-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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--required show Required column or section (default true)
|
||||
|
||||
@@ -33,6 +33,9 @@ terraform-docs markdown table [PATH] [flags]
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--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-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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--required show Required column or section (default true)
|
||||
|
||||
@@ -36,6 +36,9 @@ terraform-docs markdown [PATH] [flags]
|
||||
--header-from string relative path of a file to read header from (default "main.tf")
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -30,6 +30,9 @@ terraform-docs pretty [PATH] [flags]
|
||||
--header-from string relative path of a file to read header from (default "main.tf")
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -24,6 +24,9 @@ terraform-docs [PATH] [flags]
|
||||
-h, --help help for terraform-docs
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -29,6 +29,9 @@ terraform-docs tfvars hcl [PATH] [flags]
|
||||
--header-from string relative path of a file to read header from (default "main.tf")
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -29,6 +29,9 @@ terraform-docs tfvars json [PATH] [flags]
|
||||
--header-from string relative path of a file to read header from (default "main.tf")
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -25,6 +25,9 @@ Generate terraform.tfvars of inputs.
|
||||
--header-from string relative path of a file to read header from (default "main.tf")
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -29,6 +29,9 @@ terraform-docs toml [PATH] [flags]
|
||||
--header-from string relative path of a file to read header from (default "main.tf")
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -29,6 +29,9 @@ terraform-docs xml [PATH] [flags]
|
||||
--header-from string relative path of a file to read header from (default "main.tf")
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -29,6 +29,9 @@ terraform-docs yaml [PATH] [flags]
|
||||
--header-from string relative path of a file to read header from (default "main.tf")
|
||||
--hide strings hide section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
--hide-all hide all sections (default false)
|
||||
--output-file string File in module directory 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)
|
||||
--output-values-from string inject output values from file into outputs (default "")
|
||||
--show strings show section [header, inputs, modules, outputs, providers, requirements, resources]
|
||||
|
||||
@@ -52,6 +52,14 @@ sections:
|
||||
show-all: true
|
||||
show: []
|
||||
|
||||
output:
|
||||
file: ""
|
||||
mode: inject
|
||||
template: |-
|
||||
<!-- BEGIN_TF_DOCS -->
|
||||
{{ .Content }}
|
||||
<!-- END_TF_DOCS -->
|
||||
|
||||
output-values:
|
||||
enabled: false
|
||||
from: ""
|
||||
@@ -121,3 +129,40 @@ The following options are supported and can be used for `sections.show` and
|
||||
- `providers`
|
||||
- `requirements`
|
||||
- `resources`
|
||||
|
||||
## Output
|
||||
|
||||
Insert generated output to file if `output.file` (or `--output-file string` CLI
|
||||
flag) is not empty. Insersion behavior can be controlled by `output.mode` (or
|
||||
`--output-mode string` CLI flag):
|
||||
|
||||
- `inject` (default)
|
||||
|
||||
Partially replace the `output-file` with generated output. This will fail if
|
||||
`output-file` doesn't exist.
|
||||
|
||||
- `replace`
|
||||
|
||||
Completely replace the `output-file` with generated output. This will create
|
||||
the `output-file` if it doesn't exist.
|
||||
|
||||
The output generated by formatters (`markdown`, `asciidoc`, etc) will first be
|
||||
inserted into a template before getting saved into the file. This template can
|
||||
be customized with `output.template` or `--output-template string` CLI flag.
|
||||
The default template value is:
|
||||
|
||||
```go
|
||||
<!-- BEGIN_TF_DOCS -->
|
||||
{{ .Content }}
|
||||
<!-- END_TF_DOCS -->
|
||||
```
|
||||
|
||||
This template consists of three items, all of them are mandatory and have to be on
|
||||
separate lines:
|
||||
|
||||
- begin comment
|
||||
- `{{ .Content }}` slug
|
||||
- end comment
|
||||
|
||||
You may change the wording of comment as you wish, but the comment must be present
|
||||
in the template. Also note that `SPACE`s inside `{{ }}` are mandatory.
|
||||
|
||||
@@ -57,6 +57,16 @@ resource "foo" "bar" { ... }
|
||||
**Note:** This comment must start at the immediate first line of the `.tf` file
|
||||
before any `resource`, `variable`, `module`, etc.
|
||||
|
||||
## Insert Output To File
|
||||
|
||||
Since `v0.12.0` 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
|
||||
terraform-docs markdown table --output-file README.md --output-mode inject /path/to/module
|
||||
```
|
||||
|
||||
## Generate terraform.tfvars
|
||||
|
||||
You can generate `terraform.tfvars` in both `hcl` and `json` format by executing
|
||||
@@ -124,6 +134,7 @@ done
|
||||
Please refer to it for complete examples and guides.
|
||||
|
||||
[sections]: {{< ref "configuration/#sections" >}}
|
||||
[output]: {{< ref "configuration/#output" >}}
|
||||
[terraform-docs GitHub Action]: https://github.com/terraform-docs/gh-actions
|
||||
[git hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
|
||||
[pre-commit-terraform]: https://github.com/antonbabenko/pre-commit-terraform
|
||||
|
||||
@@ -7,10 +7,26 @@ sections:
|
||||
- inputs
|
||||
- providers
|
||||
- modules
|
||||
|
||||
# output:
|
||||
# file: README.md
|
||||
# mode: inject
|
||||
# template: |-
|
||||
# <!-- BEGIN_TF_DOCS -->
|
||||
# The template can be customized with aribitrary markdown content.
|
||||
# For example this can be shown before the actual content generated
|
||||
# by formatters.
|
||||
|
||||
# {{ .Content }}
|
||||
|
||||
# You can also show something after it!
|
||||
# <!-- END_TF_DOCS -->
|
||||
|
||||
sort:
|
||||
enabled: true
|
||||
by:
|
||||
- required
|
||||
|
||||
settings:
|
||||
indent: 4
|
||||
escape: false
|
||||
|
||||
@@ -12,11 +12,27 @@ package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/terraform-docs/terraform-docs/internal/print"
|
||||
"github.com/terraform-docs/terraform-docs/internal/terraform"
|
||||
)
|
||||
|
||||
const (
|
||||
sectionHeader = "header"
|
||||
sectionInputs = "inputs"
|
||||
sectionModules = "modules"
|
||||
sectionOutputs = "outputs"
|
||||
sectionProviders = "providers"
|
||||
sectionRequirements = "requirements"
|
||||
sectionResources = "resources"
|
||||
)
|
||||
|
||||
var allSections = []string{sectionHeader, sectionInputs, sectionModules, sectionOutputs, sectionProviders, sectionRequirements, sectionResources}
|
||||
|
||||
// AllSections list.
|
||||
var AllSections = strings.Join(allSections, ", ")
|
||||
|
||||
type sections struct {
|
||||
Show []string `yaml:"show"`
|
||||
Hide []string `yaml:"hide"`
|
||||
@@ -50,17 +66,16 @@ func defaultSections() sections {
|
||||
}
|
||||
|
||||
func (s *sections) validate() error {
|
||||
items := []string{"header", "inputs", "modules", "outputs", "providers", "requirements", "resources"}
|
||||
for _, item := range s.Show {
|
||||
switch item {
|
||||
case items[0], items[1], items[2], items[3], items[4], items[5], items[6]:
|
||||
case allSections[0], allSections[1], allSections[2], allSections[3], allSections[4], allSections[5], allSections[6]:
|
||||
default:
|
||||
return fmt.Errorf("'%s' is not a valid section", item)
|
||||
}
|
||||
}
|
||||
for _, item := range s.Hide {
|
||||
switch item {
|
||||
case items[0], items[1], items[2], items[3], items[4], items[5], items[6]:
|
||||
case allSections[0], allSections[1], allSections[2], allSections[3], allSections[4], allSections[5], allSections[6]:
|
||||
default:
|
||||
return fmt.Errorf("'%s' is not a valid section", item)
|
||||
}
|
||||
@@ -99,6 +114,73 @@ func (s *sections) visibility(section string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
outputModeInject = "inject"
|
||||
outputModeReplace = "replace"
|
||||
|
||||
outputBeginComment = "<!-- BEGIN_TF_DOCS -->"
|
||||
outputContent = "{{ .Content }}"
|
||||
outputEndComment = "<!-- END_TF_DOCS -->"
|
||||
)
|
||||
|
||||
// Output to file template and modes
|
||||
var (
|
||||
OutputTemplate = fmt.Sprintf("%s\n%s\n%s", outputBeginComment, outputContent, outputEndComment)
|
||||
OutputModes = strings.Join([]string{outputModeInject, outputModeReplace}, ", ")
|
||||
)
|
||||
|
||||
type output struct {
|
||||
File string `yaml:"file"`
|
||||
Mode string `yaml:"mode"`
|
||||
Template string `yaml:"template"`
|
||||
|
||||
BeginComment string `yaml:"-"`
|
||||
EndComment string `yaml:"-"`
|
||||
}
|
||||
|
||||
func defaultOutput() output {
|
||||
return output{
|
||||
File: "",
|
||||
Mode: outputModeInject,
|
||||
Template: OutputTemplate,
|
||||
|
||||
BeginComment: outputBeginComment,
|
||||
EndComment: outputEndComment,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *output) validate() error {
|
||||
if o.File != "" {
|
||||
if o.Mode == "" {
|
||||
return fmt.Errorf("value of '--output-mode' can't be empty")
|
||||
}
|
||||
if o.Template == "" {
|
||||
return fmt.Errorf("value of '--output-template' can't be empty")
|
||||
}
|
||||
|
||||
index := strings.Index(o.Template, outputContent)
|
||||
if index < 0 {
|
||||
return fmt.Errorf("value of '--output-template' doesn't have '{{ .Content }}' (note that spaces inside '{{ }}' are mandatory)")
|
||||
}
|
||||
|
||||
lines := strings.Split(o.Template, "\n")
|
||||
if len(lines) < 3 {
|
||||
return fmt.Errorf("value of '--output-template' should contain at least 3 lines (begin comment, {{ .Content }}, and end comment)")
|
||||
}
|
||||
|
||||
if !strings.Contains(lines[0], "<!--") || !strings.Contains(lines[0], "-->") {
|
||||
return fmt.Errorf("value of '--output-template' is missing begin comment")
|
||||
}
|
||||
o.BeginComment = strings.TrimSpace(lines[0])
|
||||
|
||||
if !strings.Contains(lines[len(lines)-1], "<!--") || !strings.Contains(lines[len(lines)-1], "-->") {
|
||||
return fmt.Errorf("value of '--output-template' is missing end comment")
|
||||
}
|
||||
o.EndComment = strings.TrimSpace(lines[len(lines)-1])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type outputvalues struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
From string `yaml:"from"`
|
||||
@@ -187,10 +269,12 @@ func (s *settings) validate() error {
|
||||
|
||||
// Config represents all the available config options that can be accessed and passed through CLI
|
||||
type Config struct {
|
||||
BaseDir string `yaml:"-"`
|
||||
File string `yaml:"-"`
|
||||
Formatter string `yaml:"formatter"`
|
||||
HeaderFrom string `yaml:"header-from"`
|
||||
Sections sections `yaml:"sections"`
|
||||
Output output `yaml:"output"`
|
||||
OutputValues outputvalues `yaml:"output-values"`
|
||||
Sort sort `yaml:"sort"`
|
||||
Settings settings `yaml:"settings"`
|
||||
@@ -199,10 +283,12 @@ type Config struct {
|
||||
// DefaultConfig returns new instance of Config with default values set
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
BaseDir: "",
|
||||
File: "",
|
||||
Formatter: "",
|
||||
HeaderFrom: "main.tf",
|
||||
Sections: defaultSections(),
|
||||
Output: defaultOutput(),
|
||||
OutputValues: defaultOutputValues(),
|
||||
Sort: defaultSort(),
|
||||
Settings: defaultSettings(),
|
||||
@@ -244,6 +330,11 @@ func (c *Config) validate() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// output
|
||||
if err := c.Output.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// output values
|
||||
if err := c.OutputValues.validate(); err != nil {
|
||||
return err
|
||||
|
||||
@@ -88,6 +88,11 @@ func (c *cfgreader) parse() error {
|
||||
if !el.FieldByName(field).Bool() {
|
||||
c.config.Sort.ByList = remove(c.config.Sort.ByList, mapping[flag])
|
||||
}
|
||||
case "output-file", "output-mode", "output-template":
|
||||
mapping := map[string]string{"output-file": "file", "output-mode": "mode", "output-template": "template"}
|
||||
if err := c.overrideValue(mapping[flag], &c.config.Output, &c.overrides.Output); err != nil {
|
||||
return err
|
||||
}
|
||||
case "output-values", "output-values-from":
|
||||
mapping := map[string]string{"output-values": "enabled", "output-values-from": "from"}
|
||||
if err := c.overrideValue(mapping[flag], &c.config.OutputValues, &c.overrides.OutputValues); err != nil {
|
||||
|
||||
@@ -110,6 +110,15 @@ func TestOverrideValue(t *testing.T) {
|
||||
wantErr: false,
|
||||
errMsg: "",
|
||||
},
|
||||
{
|
||||
name: "override values of given field",
|
||||
tag: "mode",
|
||||
to: func() interface{} { return &config.Output },
|
||||
from: func() interface{} { return &override.Output },
|
||||
overrideFn: func() { override.Output.Mode = "replace" },
|
||||
wantErr: false,
|
||||
errMsg: "",
|
||||
},
|
||||
{
|
||||
name: "override values of unkwon field tag",
|
||||
tag: "not-available",
|
||||
|
||||
@@ -12,6 +12,7 @@ package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -86,6 +87,9 @@ func PreRunEFunc(config *Config) func(*cobra.Command, []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// set the base moduel directory
|
||||
config.BaseDir = args[0]
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -94,9 +98,9 @@ func PreRunEFunc(config *Config) func(*cobra.Command, []string) error {
|
||||
// This functions extract print.Settings and terraform.Options from generated and
|
||||
// normalized Config and initializes required print.Format instance and executes it.
|
||||
func RunEFunc(config *Config) func(*cobra.Command, []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
return func(cmd *cobra.Command, _ []string) error {
|
||||
settings, options := config.extract()
|
||||
options.Path = args[0]
|
||||
options.Path = config.BaseDir
|
||||
|
||||
module, err := terraform.LoadWithOptions(options)
|
||||
if err != nil {
|
||||
@@ -115,22 +119,47 @@ func RunEFunc(config *Config) func(*cobra.Command, []string) error {
|
||||
return fmt.Errorf("formatter '%s' not found", config.Formatter)
|
||||
}
|
||||
|
||||
output, cerr := client.Execute(pluginsdk.ExecuteArgs{
|
||||
content, cerr := client.Execute(pluginsdk.ExecuteArgs{
|
||||
Module: module.Convert(),
|
||||
Settings: settings.Convert(),
|
||||
})
|
||||
return printOrDie(output, cerr)
|
||||
if cerr != nil {
|
||||
return cerr
|
||||
}
|
||||
return writeContent(config, content)
|
||||
}
|
||||
|
||||
output, err := printer.Print(module, settings)
|
||||
return printOrDie(output, err)
|
||||
content, err := printer.Print(module, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeContent(config, content)
|
||||
}
|
||||
}
|
||||
|
||||
func printOrDie(output string, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
// writeContent to a Writer. This can either be os.Stdout or specific
|
||||
// file (e.g. README.md) if '--output-file' is provided.
|
||||
func writeContent(config *Config, content string) error {
|
||||
var w io.Writer
|
||||
|
||||
// writing to a file (either inject or replace)
|
||||
if config.Output.File != "" {
|
||||
w = &fileWriter{
|
||||
file: config.Output.File,
|
||||
dir: config.BaseDir,
|
||||
|
||||
mode: config.Output.Mode,
|
||||
|
||||
template: config.Output.Template,
|
||||
begin: config.Output.BeginComment,
|
||||
end: config.Output.EndComment,
|
||||
}
|
||||
} else {
|
||||
// writing to stdout
|
||||
w = &stdoutWriter{}
|
||||
}
|
||||
fmt.Println(output)
|
||||
return nil
|
||||
|
||||
_, err := io.WriteString(w, content)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
19
internal/cli/testdata/writer/begin-comment-missing.md
vendored
Normal file
19
internal/cli/testdata/writer/begin-comment-missing.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Foo
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
## Bar
|
||||
|
||||
sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua.
|
||||
|
||||
- Ut enim ad minim veniam
|
||||
- quis nostrud exercitation
|
||||
|
||||
ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit
|
||||
<!-- END_TF_DOCS -->
|
||||
|
||||
## Baz
|
||||
|
||||
esse cillum dolore eu fugiat nulla pariatur.
|
||||
0
internal/cli/testdata/writer/empty-file.md
vendored
Normal file
0
internal/cli/testdata/writer/empty-file.md
vendored
Normal file
20
internal/cli/testdata/writer/end-comment-before-begin.md
vendored
Normal file
20
internal/cli/testdata/writer/end-comment-before-begin.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# Foo
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
## Bar
|
||||
|
||||
sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua.
|
||||
|
||||
- Ut enim ad minim veniam
|
||||
- quis nostrud exercitation
|
||||
|
||||
<!-- END_TF_DOCS -->
|
||||
ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit
|
||||
<!-- BEGIN_TF_DOCS -->
|
||||
|
||||
## Baz
|
||||
|
||||
esse cillum dolore eu fugiat nulla pariatur.
|
||||
19
internal/cli/testdata/writer/end-comment-missing.md
vendored
Normal file
19
internal/cli/testdata/writer/end-comment-missing.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Foo
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
## Bar
|
||||
|
||||
sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua.
|
||||
|
||||
- Ut enim ad minim veniam
|
||||
- quis nostrud exercitation
|
||||
|
||||
<!-- BEGIN_TF_DOCS -->
|
||||
ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit
|
||||
|
||||
## Baz
|
||||
|
||||
esse cillum dolore eu fugiat nulla pariatur.
|
||||
110
internal/cli/writer.go
Normal file
110
internal/cli/writer.go
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2021 The terraform-docs Authors.
|
||||
|
||||
Licensed under the MIT license (the "License"); you may not
|
||||
use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at the LICENSE file in
|
||||
the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const (
|
||||
errFileEmpty = "file content is empty"
|
||||
errTemplateEmpty = "template is missing"
|
||||
errBeginCommentMissing = "begin comment is missing"
|
||||
errEndCommentMissing = "end comment is missing"
|
||||
errEndCommentBeforeBegin = "end comment is before begin comment"
|
||||
)
|
||||
|
||||
// stdoutWriter writes content to os.Stdout.
|
||||
type stdoutWriter struct{}
|
||||
|
||||
func (sw *stdoutWriter) Write(p []byte) (int, error) {
|
||||
return os.Stdout.Write([]byte(string(p) + "\n"))
|
||||
}
|
||||
|
||||
// fileWriter writes content to file.
|
||||
//
|
||||
// First of all it will process 'content' into provided 'template'.
|
||||
//
|
||||
// If 'mode' is 'replace' it replaces the whole content of 'dir/file'
|
||||
// with output of executed template. Note that this will create 'dir/file'
|
||||
// if it doesn't exist.
|
||||
//
|
||||
// If 'mode' is 'inject' it will attempt to inject the output of executed
|
||||
// template into 'dir/file' between the 'begin' and 'end' comment. Note that
|
||||
// this will fail if 'dir/file' doesn't exist, or doesn't contain 'begin' or
|
||||
// 'end' comment.
|
||||
type fileWriter struct {
|
||||
file string
|
||||
dir string
|
||||
|
||||
mode string
|
||||
|
||||
template string
|
||||
begin string
|
||||
end string
|
||||
}
|
||||
|
||||
func (fw *fileWriter) Write(p []byte) (int, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
if fw.template == "" {
|
||||
return 0, errors.New(errTemplateEmpty)
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.New("content").Parse(fw.template))
|
||||
if err := tmpl.ExecuteTemplate(&buf, "content", struct {
|
||||
Content string
|
||||
}{
|
||||
Content: string(p),
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
content := buf.String()
|
||||
filename := filepath.Join(fw.dir, fw.file)
|
||||
|
||||
if fw.mode == outputModeInject {
|
||||
f, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
fc := string(f)
|
||||
if fc == "" {
|
||||
return 0, errors.New(errFileEmpty)
|
||||
}
|
||||
|
||||
before := strings.Index(fc, fw.begin)
|
||||
if before < 0 {
|
||||
return 0, errors.New(errBeginCommentMissing)
|
||||
}
|
||||
content = fc[:before] + content
|
||||
|
||||
after := strings.Index(fc, fw.end)
|
||||
if after < 0 {
|
||||
return 0, errors.New(errEndCommentMissing)
|
||||
}
|
||||
if after < before {
|
||||
return 0, errors.New(errEndCommentBeforeBegin)
|
||||
}
|
||||
content = content + fc[after+len(fw.end):]
|
||||
}
|
||||
|
||||
n := len(content)
|
||||
err := os.WriteFile(filename, []byte(content), 0644)
|
||||
|
||||
return n, err
|
||||
}
|
||||
101
internal/cli/writer_test.go
Normal file
101
internal/cli/writer_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Copyright 2021 The terraform-docs Authors.
|
||||
|
||||
Licensed under the MIT license (the "License"); you may not
|
||||
use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at the LICENSE file in
|
||||
the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFileWriter(t *testing.T) {
|
||||
content := "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||
tests := map[string]struct {
|
||||
file string
|
||||
mode string
|
||||
template string
|
||||
begin string
|
||||
end string
|
||||
errMsg string
|
||||
}{
|
||||
"ModeInjectNoFile": {
|
||||
file: "file-missing.md",
|
||||
mode: "inject",
|
||||
template: OutputTemplate,
|
||||
begin: outputBeginComment,
|
||||
end: outputEndComment,
|
||||
errMsg: "open testdata/writer/file-missing.md: no such file or directory",
|
||||
},
|
||||
"EmptyTemplate": {
|
||||
file: "not-applicable.md",
|
||||
mode: "inject",
|
||||
template: "",
|
||||
begin: outputBeginComment,
|
||||
end: outputEndComment,
|
||||
errMsg: "template is missing",
|
||||
},
|
||||
"EmptyFile": {
|
||||
file: "empty-file.md",
|
||||
mode: "inject",
|
||||
template: OutputTemplate,
|
||||
begin: outputBeginComment,
|
||||
end: outputEndComment,
|
||||
errMsg: "file content is empty",
|
||||
},
|
||||
"BeginCommentMissing": {
|
||||
file: "begin-comment-missing.md",
|
||||
mode: "inject",
|
||||
template: OutputTemplate,
|
||||
begin: outputBeginComment,
|
||||
end: outputEndComment,
|
||||
errMsg: "begin comment is missing",
|
||||
},
|
||||
"EndCommentMissing": {
|
||||
file: "end-comment-missing.md",
|
||||
mode: "inject",
|
||||
template: OutputTemplate,
|
||||
begin: outputBeginComment,
|
||||
end: outputEndComment,
|
||||
errMsg: "end comment is missing",
|
||||
},
|
||||
"EndCommentBeforeBegin": {
|
||||
file: "end-comment-before-begin.md",
|
||||
mode: "inject",
|
||||
template: OutputTemplate,
|
||||
begin: outputBeginComment,
|
||||
end: outputEndComment,
|
||||
errMsg: "end comment is before begin comment",
|
||||
},
|
||||
}
|
||||
for name, tt := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
writer := &fileWriter{
|
||||
file: tt.file,
|
||||
dir: filepath.Join("testdata", "writer"),
|
||||
|
||||
mode: tt.mode,
|
||||
|
||||
template: tt.template,
|
||||
begin: tt.begin,
|
||||
end: tt.end,
|
||||
}
|
||||
|
||||
_, err := io.WriteString(writer, content)
|
||||
|
||||
assert.NotNil(err)
|
||||
assert.Equal(tt.errMsg, err.Error())
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user