mirror of
https://github.com/terraform-docs/terraform-docs.git
synced 2026-03-27 12:58:35 +07:00
Merge pull request #570 from khos2ow/recursive-override
Add recursive to config and enable partial config override
This commit is contained in:
@@ -181,6 +181,10 @@ version: ""
|
||||
header-from: main.tf
|
||||
footer-from: ""
|
||||
|
||||
recursive:
|
||||
enabled: false
|
||||
path: modules
|
||||
|
||||
sections:
|
||||
hide: []
|
||||
show: []
|
||||
|
||||
@@ -60,8 +60,8 @@ func NewCommand() *cobra.Command {
|
||||
|
||||
// flags
|
||||
cmd.PersistentFlags().StringVarP(&config.File, "config", "c", ".terraform-docs.yml", "config file name")
|
||||
cmd.PersistentFlags().BoolVar(&config.Recursive, "recursive", false, "update submodules recursively (default false)")
|
||||
cmd.PersistentFlags().StringVar(&config.RecursivePath, "recursive-path", "modules", "submodules path to recursively update")
|
||||
cmd.PersistentFlags().BoolVar(&config.Recursive.Enabled, "recursive", false, "update submodules recursively (default false)")
|
||||
cmd.PersistentFlags().StringVar(&config.Recursive.Path, "recursive-path", "modules", "submodules path to recursively update")
|
||||
|
||||
cmd.PersistentFlags().StringSliceVar(&config.Sections.Show, "show", []string{}, "show section ["+print.AllSections+"]")
|
||||
cmd.PersistentFlags().StringSliceVar(&config.Sections.Hide, "hide", []string{}, "hide section ["+print.AllSections+"]")
|
||||
|
||||
@@ -11,7 +11,7 @@ toc: false
|
||||
Since `v0.15.0`
|
||||
|
||||
Considering the file strucutre below of main module and its submodules, it is
|
||||
possible to generate documentation for them main and all its submodules in one
|
||||
possible to generate documentation for the main and all its submodules in one
|
||||
execution, with `--recursive` flag.
|
||||
|
||||
{{< alert type="warning" >}}
|
||||
@@ -42,4 +42,41 @@ $ tree .
|
||||
├── outputs.tf
|
||||
├── variables.tf
|
||||
└── versions.tf
|
||||
|
||||
$ terraform-docs markdown --recursive --output-file README.md .
|
||||
```
|
||||
|
||||
Alternatively `recursive.enabled` config also can be used instead of CLI flag.
|
||||
|
||||
```bash
|
||||
$ pwd
|
||||
/path/to/module
|
||||
|
||||
$ tree .
|
||||
.
|
||||
├── README.md
|
||||
├── main.tf
|
||||
├── modules
|
||||
│ └── my-sub-module
|
||||
│ ├── README.md
|
||||
│ ├── main.tf
|
||||
│ ├── variables.tf
|
||||
│ └── versions.tf
|
||||
├── outputs.tf
|
||||
├── variables.tf
|
||||
├── versions.tf
|
||||
├── ...
|
||||
└── .terraform-docs.yml
|
||||
|
||||
$ cat .terraform-docs.yml
|
||||
formatter: markdown table
|
||||
|
||||
recursive:
|
||||
enabled: true
|
||||
|
||||
output:
|
||||
file: README.md
|
||||
mode: inject
|
||||
|
||||
$ terraform-docs .
|
||||
```
|
||||
|
||||
@@ -76,6 +76,10 @@ version: ""
|
||||
header-from: main.tf
|
||||
footer-from: ""
|
||||
|
||||
recursive:
|
||||
enabled: false
|
||||
path: modules
|
||||
|
||||
sections:
|
||||
hide: []
|
||||
show: []
|
||||
|
||||
52
docs/user-guide/configuration/recursive.md
Normal file
52
docs/user-guide/configuration/recursive.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "recursive"
|
||||
description: "recursive configuration"
|
||||
menu:
|
||||
docs:
|
||||
parent: "configuration"
|
||||
weight: 127
|
||||
toc: true
|
||||
---
|
||||
|
||||
Since `v0.16.0`
|
||||
|
||||
Documentation for main module and its submodules can be generated all in one
|
||||
execution using `recursive` config. It can be enabled with `recursive.enabled: true`.
|
||||
|
||||
Path to find submodules can be configured with `recursive.path` (defaults to
|
||||
`modules`).
|
||||
|
||||
{{< alert type="warning" >}}
|
||||
Generating documentation recursively is allowed only with `output.file`
|
||||
set.
|
||||
{{< /alert >}}
|
||||
|
||||
Each submodule can also have their own `.terraform-docs.yml` config file, to
|
||||
override configuration from root module.
|
||||
|
||||
## Options
|
||||
|
||||
Available options with their default values.
|
||||
|
||||
```yaml
|
||||
recursive:
|
||||
enabled: false
|
||||
path: modules
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Enable recursive mode for submodules folder.
|
||||
|
||||
```yaml
|
||||
recursive:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
Provide alternative name of submodules folder.
|
||||
|
||||
```yaml
|
||||
recursive:
|
||||
enabled: true
|
||||
path: submodules-folder
|
||||
```
|
||||
@@ -4,7 +4,7 @@ description: "sections configuration"
|
||||
menu:
|
||||
docs:
|
||||
parent: "configuration"
|
||||
weight: 127
|
||||
weight: 128
|
||||
toc: true
|
||||
---
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ description: "settings configuration"
|
||||
menu:
|
||||
docs:
|
||||
parent: "configuration"
|
||||
weight: 128
|
||||
weight: 129
|
||||
toc: true
|
||||
---
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ description: "sort configuration"
|
||||
menu:
|
||||
docs:
|
||||
parent: "configuration"
|
||||
weight: 129
|
||||
weight: 130
|
||||
toc: true
|
||||
---
|
||||
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
# # see: https://terraform-docs.io/user-guide/configuration/#version
|
||||
# # see: https://terraform-docs.io/user-guide/configuration/version
|
||||
# version: ">= 0.10, < 0.12"
|
||||
|
||||
# see: https://terraform-docs.io/user-guide/configuration/#formatters
|
||||
# see: https://terraform-docs.io/user-guide/configuration/formatter
|
||||
formatter: markdown table
|
||||
|
||||
# see: https://terraform-docs.io/user-guide/configuration/header-from
|
||||
header-from: doc.txt
|
||||
|
||||
# see: https://terraform-docs.io/user-guide/configuration/footer-from
|
||||
footer-from: footer.md
|
||||
|
||||
# see: https://terraform-docs.io/user-guide/configuration/#sections
|
||||
# see: https://terraform-docs.io/user-guide/configuration/recursive
|
||||
# recursive:
|
||||
# enabled: false
|
||||
# path: modules
|
||||
|
||||
# see: https://terraform-docs.io/user-guide/configuration/sections
|
||||
sections:
|
||||
show:
|
||||
- header
|
||||
@@ -16,7 +24,7 @@ sections:
|
||||
- modules
|
||||
- footer
|
||||
|
||||
# # see: https://terraform-docs.io/user-guide/configuration/#content
|
||||
# # see: https://terraform-docs.io/user-guide/configuration/content
|
||||
# content: |-
|
||||
# Any arbitrary text can be placed anywhere in the content
|
||||
#
|
||||
@@ -38,7 +46,7 @@ sections:
|
||||
#
|
||||
# {{ .Inputs }}
|
||||
|
||||
# # see: https://terraform-docs.io/user-guide/configuration/#output
|
||||
# # see: https://terraform-docs.io/user-guide/configuration/output
|
||||
# output:
|
||||
# file: README.md
|
||||
# mode: inject
|
||||
@@ -53,11 +61,17 @@ sections:
|
||||
# You can also show something after it!
|
||||
# <!-- END_TF_DOCS -->
|
||||
|
||||
# see: https://terraform-docs.io/user-guide/configuration/#sort
|
||||
# see: https://terraform-docs.io/user-guide/configuration/sort
|
||||
sort:
|
||||
enabled: true
|
||||
by: required
|
||||
|
||||
# # https://terraform-docs.io/user-guide/configuration/output-values/
|
||||
# output-values:
|
||||
# enabled: false
|
||||
# from: ""
|
||||
|
||||
# see: https://terraform-docs.io/user-guide/configuration/settings
|
||||
settings:
|
||||
indent: 4
|
||||
escape: false
|
||||
|
||||
@@ -71,8 +71,15 @@ func (r *Runtime) PreRunEFunc(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("value of '--config' can't be empty")
|
||||
}
|
||||
|
||||
// attempt to read config file and override them with corresponding flags
|
||||
if err := r.readConfig(r.config, ""); err != nil {
|
||||
// read config file and override them with corresponding flags
|
||||
v := viper.New()
|
||||
|
||||
if err := r.readConfig(v, r.config.File, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// and override them with corresponding flags
|
||||
if err := r.unmarshalConfig(v, r.config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -95,7 +102,7 @@ func (r *Runtime) RunEFunc(cmd *cobra.Command, args []string) error {
|
||||
// Generating content recursively is only allowed when `config.Output.File`
|
||||
// is set. Otherwise it would be impossible to distinguish where output of
|
||||
// one module ends and the other begin, if content is outpput to stdout.
|
||||
if r.config.Recursive && r.config.RecursivePath != "" {
|
||||
if r.config.Recursive.Enabled && r.config.Recursive.Path != "" {
|
||||
items, err := r.findSubmodules()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -120,7 +127,7 @@ func (r *Runtime) RunEFunc(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.config.Recursive && cfg.Output.File == "" {
|
||||
if r.config.Recursive.Enabled && cfg.Output.File == "" {
|
||||
return fmt.Errorf("value of '--output-file' cannot be empty with '--recursive'")
|
||||
}
|
||||
|
||||
@@ -135,11 +142,9 @@ func (r *Runtime) RunEFunc(cmd *cobra.Command, args []string) error {
|
||||
// readConfig attempts to read config file, either default `.terraform-docs.yml`
|
||||
// or provided file with `-c, --config` flag. It will then attempt to override
|
||||
// them with corresponding flags (if set).
|
||||
func (r *Runtime) readConfig(config *print.Config, submoduleDir string) error {
|
||||
v := viper.New()
|
||||
|
||||
func (r *Runtime) readConfig(v *viper.Viper, file string, submoduleDir string) error {
|
||||
if r.isFlagChanged("config") {
|
||||
v.SetConfigFile(config.File)
|
||||
v.SetConfigFile(file)
|
||||
} else {
|
||||
v.SetConfigName(".terraform-docs")
|
||||
v.SetConfigType("yml")
|
||||
@@ -159,7 +164,7 @@ func (r *Runtime) readConfig(config *print.Config, submoduleDir string) error {
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
var perr *os.PathError
|
||||
if errors.As(err, &perr) {
|
||||
return fmt.Errorf("config file %s not found", config.File)
|
||||
return fmt.Errorf("config file %s not found", file)
|
||||
}
|
||||
|
||||
var cerr viper.ConfigFileNotFoundError
|
||||
@@ -174,6 +179,10 @@ func (r *Runtime) readConfig(config *print.Config, submoduleDir string) error {
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runtime) unmarshalConfig(v *viper.Viper, config *print.Config) error {
|
||||
r.bindFlags(v)
|
||||
|
||||
if err := v.Unmarshal(config); err != nil {
|
||||
@@ -188,7 +197,6 @@ func (r *Runtime) readConfig(config *print.Config, submoduleDir string) error {
|
||||
config.Formatter = r.formatter
|
||||
}
|
||||
|
||||
// TODO
|
||||
config.Parse()
|
||||
|
||||
return nil
|
||||
@@ -229,11 +237,27 @@ func (r *Runtime) bindFlags(v *viper.Viper) {
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Runtime) mergeConfig(v *viper.Viper) (*print.Config, error) {
|
||||
copy := *r.config
|
||||
merged := ©
|
||||
|
||||
if v.IsSet("sections.show") || v.IsSet("sections.hide") {
|
||||
merged.Sections.Show = []string{}
|
||||
merged.Sections.Hide = []string{}
|
||||
}
|
||||
|
||||
if err := r.unmarshalConfig(v, merged); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return merged, nil
|
||||
}
|
||||
|
||||
// findSubmodules generates list of submodules in `rootDir/RecursivePath` if
|
||||
// `--recursive` flag is set. This keeps track of `.terraform-docs.yml` in any
|
||||
// of the submodules (if exists) to override the root configuration.
|
||||
func (r *Runtime) findSubmodules() ([]module, error) {
|
||||
dir := filepath.Join(r.rootDir, r.config.RecursivePath)
|
||||
dir := filepath.Join(r.rootDir, r.config.Recursive.Path)
|
||||
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return nil, err
|
||||
@@ -257,9 +281,13 @@ func (r *Runtime) findSubmodules() ([]module, error) {
|
||||
cfgfile := filepath.Join(path, r.config.File)
|
||||
|
||||
if _, err := os.Stat(cfgfile); !os.IsNotExist(err) {
|
||||
cfg = print.DefaultConfig()
|
||||
v := viper.New()
|
||||
|
||||
if err := r.readConfig(cfg, path); err != nil {
|
||||
if err = r.readConfig(v, cfgfile, path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg, err = r.mergeConfig(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,18 @@ import (
|
||||
// Config represents all the available config options that can be accessed and
|
||||
// passed through CLI.
|
||||
type Config struct {
|
||||
File string `mapstructure:"-"`
|
||||
Recursive bool `mapstructure:"-"`
|
||||
RecursivePath string `mapstructure:"-"`
|
||||
Formatter string `mapstructure:"formatter"`
|
||||
Version string `mapstructure:"version"`
|
||||
HeaderFrom string `mapstructure:"header-from"`
|
||||
FooterFrom string `mapstructure:"footer-from"`
|
||||
Content string `mapstructure:"content"`
|
||||
Sections sections `mapstructure:"sections"`
|
||||
Output output `mapstructure:"output"`
|
||||
OutputValues outputvalues `mapstructure:"output-values"`
|
||||
Sort sort `mapstructure:"sort"`
|
||||
Settings settings `mapstructure:"settings"`
|
||||
File string `mapstructure:"-"`
|
||||
Formatter string `mapstructure:"formatter"`
|
||||
Version string `mapstructure:"version"`
|
||||
HeaderFrom string `mapstructure:"header-from"`
|
||||
FooterFrom string `mapstructure:"footer-from"`
|
||||
Recursive recursive `mapstructure:"recursive"`
|
||||
Content string `mapstructure:"content"`
|
||||
Sections sections `mapstructure:"sections"`
|
||||
Output output `mapstructure:"output"`
|
||||
OutputValues outputvalues `mapstructure:"output-values"`
|
||||
Sort sort `mapstructure:"sort"`
|
||||
Settings settings `mapstructure:"settings"`
|
||||
|
||||
ModuleRoot string
|
||||
}
|
||||
@@ -39,6 +38,7 @@ type Config struct {
|
||||
func NewConfig() *Config {
|
||||
return &Config{
|
||||
HeaderFrom: "main.tf",
|
||||
Recursive: recursive{},
|
||||
Sections: sections{},
|
||||
Output: output{},
|
||||
OutputValues: outputvalues{},
|
||||
@@ -50,24 +50,42 @@ func NewConfig() *Config {
|
||||
// DefaultConfig returns new instance of Config with default values set.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
File: "",
|
||||
Recursive: false,
|
||||
RecursivePath: "modules",
|
||||
Formatter: "",
|
||||
Version: "",
|
||||
HeaderFrom: "main.tf",
|
||||
FooterFrom: "",
|
||||
Content: "",
|
||||
Sections: defaultSections(),
|
||||
Output: defaultOutput(),
|
||||
OutputValues: defaultOutputValues(),
|
||||
Sort: defaultSort(),
|
||||
Settings: defaultSettings(),
|
||||
File: "",
|
||||
Formatter: "",
|
||||
Version: "",
|
||||
HeaderFrom: "main.tf",
|
||||
FooterFrom: "",
|
||||
Recursive: defaultRecursive(),
|
||||
Content: "",
|
||||
Sections: defaultSections(),
|
||||
Output: defaultOutput(),
|
||||
OutputValues: defaultOutputValues(),
|
||||
Sort: defaultSort(),
|
||||
Settings: defaultSettings(),
|
||||
|
||||
ModuleRoot: "",
|
||||
}
|
||||
}
|
||||
|
||||
type recursive struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
Path string `mapstructure:"path"`
|
||||
}
|
||||
|
||||
func defaultRecursive() recursive {
|
||||
return recursive{
|
||||
Enabled: false,
|
||||
Path: "modules",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *recursive) validate() error {
|
||||
if r.Enabled && r.Path == "" {
|
||||
return fmt.Errorf("value of '--recursive-path' can't be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
sectionAll = "all"
|
||||
sectionDataSources = "data-sources"
|
||||
@@ -419,11 +437,6 @@ func (c *Config) Validate() error {
|
||||
return fmt.Errorf("value of 'formatter' can't be empty")
|
||||
}
|
||||
|
||||
// recursive
|
||||
if c.Recursive && c.RecursivePath == "" {
|
||||
return fmt.Errorf("value of '--recursive-path' can't be empty")
|
||||
}
|
||||
|
||||
// header-from
|
||||
if c.HeaderFrom == "" {
|
||||
return fmt.Errorf("value of '--header-from' can't be empty")
|
||||
@@ -439,6 +452,7 @@ func (c *Config) Validate() error {
|
||||
}
|
||||
|
||||
for _, fn := range [](func() error){
|
||||
c.Recursive.validate,
|
||||
c.Sections.validate,
|
||||
c.Output.validate,
|
||||
c.OutputValues.validate,
|
||||
|
||||
@@ -561,8 +561,8 @@ func TestConfigValidate(t *testing.T) {
|
||||
},
|
||||
"RecursivePathEmpty": {
|
||||
config: func(c *Config) {
|
||||
c.Recursive = true
|
||||
c.RecursivePath = ""
|
||||
c.Recursive.Enabled = true
|
||||
c.Recursive.Path = ""
|
||||
},
|
||||
wantErr: true,
|
||||
errMsg: "value of '--recursive-path' can't be empty",
|
||||
|
||||
Reference in New Issue
Block a user