Files
terraform-docs/internal/cli/run.go
Khosrow Moossavi a3bafd6018 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>
2021-03-09 16:40:22 -05:00

166 lines
4.3 KiB
Go

/*
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 (
"fmt"
"io"
"os"
"path/filepath"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
pluginsdk "github.com/terraform-docs/plugin-sdk/plugin"
"github.com/terraform-docs/terraform-docs/internal/format"
"github.com/terraform-docs/terraform-docs/internal/plugin"
"github.com/terraform-docs/terraform-docs/internal/terraform"
)
// list of flagset items which are explicitly changed from CLI
var changedfs = make(map[string]bool)
// PreRunEFunc returns actual 'cobra.Command#PreRunE' function for 'formatter'
// commands. This functions reads and normalizes flags and arguments passed
// through CLI execution.
func PreRunEFunc(config *Config) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
formatter := cmd.Annotations["command"]
// root command must have an argument, otherwise we're going to show help
if formatter == "root" && len(args) == 0 {
cmd.Help() //nolint:errcheck
os.Exit(0)
}
cmd.Flags().VisitAll(func(f *pflag.Flag) {
changedfs[f.Name] = f.Changed
})
// read config file if provided and/or available
if config.File == "" {
return fmt.Errorf("value of '--config' can't be empty")
}
file := filepath.Join(args[0], config.File)
cfgreader := &cfgreader{
file: file,
config: config,
}
if found, err := cfgreader.exist(); !found {
// config is explicitly provided and file not found, this is an error
if changedfs["config"] {
return err
}
// config is not provided and file not found, only show an error for the root command
if formatter == "root" {
cmd.Help() //nolint:errcheck
os.Exit(0)
}
} else {
// config file is found, we're now going to parse it
if err := cfgreader.parse(); err != nil {
return err
}
}
// explicitly setting formatter to Config for non-root commands this
// will effectively override formattter properties from config file
// if 1) config file exists and 2) formatter is set and 3) explicitly
// a subcommand was executed in the terminal
if formatter != "root" {
config.Formatter = formatter
}
config.process()
if err := config.validate(); err != nil {
return err
}
// set the base moduel directory
config.BaseDir = args[0]
return nil
}
}
// RunEFunc returns actual 'cobra.Command#RunE' function for 'formatter' commands.
// 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, _ []string) error {
settings, options := config.extract()
options.Path = config.BaseDir
module, err := terraform.LoadWithOptions(options)
if err != nil {
return err
}
printer, err := format.Factory(config.Formatter, settings)
if err != nil {
plugins, perr := plugin.Discover()
if perr != nil {
return fmt.Errorf("formatter '%s' not found", config.Formatter)
}
client, found := plugins.Get(config.Formatter)
if !found {
return fmt.Errorf("formatter '%s' not found", config.Formatter)
}
content, cerr := client.Execute(pluginsdk.ExecuteArgs{
Module: module.Convert(),
Settings: settings.Convert(),
})
if cerr != nil {
return cerr
}
return writeContent(config, content)
}
content, err := printer.Print(module, settings)
if err != nil {
return err
}
return writeContent(config, content)
}
}
// 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{}
}
_, err := io.WriteString(w, content)
return err
}