feat: ignore outputs, providers, resources with comments

Prepend any type of resource, including `resource`, `data`, `module`,
`variable`, and `output` with a comment including "terraform-docs-ignore"
to exclude it from the generated output.

Signed-off-by: Khosrow Moossavi <khos2ow@gmail.com>
This commit is contained in:
Khosrow Moossavi
2024-05-28 11:45:42 -04:00
parent 943489ca41
commit 8f74fd4453
19 changed files with 261 additions and 14 deletions

View File

@@ -189,7 +189,7 @@ func loadInputs(tfmodule *tfconfig.Module, config *print.Config) ([]*Input, []*I
for _, input := range tfmodule.Variables {
comments := loadComments(input.Pos.Filename, input.Pos.Line)
// Skip over inputs that are marked as being ignored
// skip over inputs that are marked as being ignored
if strings.Contains(comments, "terraform-docs-ignore") {
continue
}
@@ -252,13 +252,20 @@ func loadModulecalls(tfmodule *tfconfig.Module, config *print.Config) []*ModuleC
var source, version string
for _, m := range tfmodule.ModuleCalls {
source, version = formatSource(m.Source, m.Version)
comments := loadComments(m.Pos.Filename, m.Pos.Line)
// skip over modules that are marked as being ignored
if strings.Contains(comments, "terraform-docs-ignore") {
continue
}
description := ""
if config.Settings.ReadComments {
description = loadComments(m.Pos.Filename, m.Pos.Line)
description = comments
}
source, version = formatSource(m.Source, m.Version)
modules = append(modules, &ModuleCall{
Name: m.Name,
Source: source,
@@ -284,10 +291,17 @@ func loadOutputs(tfmodule *tfconfig.Module, config *print.Config) ([]*Output, er
}
}
for _, o := range tfmodule.Outputs {
comments := loadComments(o.Pos.Filename, o.Pos.Line)
// skip over outputs that are marked as being ignored
if strings.Contains(comments, "terraform-docs-ignore") {
continue
}
// convert CRLF to LF early on (https://github.com/terraform-docs/terraform-docs/issues/584)
description := strings.ReplaceAll(o.Description, "\r\n", "\n")
if description == "" && config.Settings.ReadComments {
description = loadComments(o.Pos.Filename, o.Pos.Line)
description = comments
}
output := &Output{
@@ -337,7 +351,11 @@ func loadOutputValues(config *print.Config) (map[string]*output, error) {
return terraformOutputs, err
}
func loadProviders(tfmodule *tfconfig.Module, config *print.Config) []*Provider {
func loadProviders(tfmodule *tfconfig.Module, config *print.Config) []*Provider { //nolint:gocyclo
// NOTE(khos2ow): this function is over our cyclomatic complexity goal.
// Be wary when adding branches, and look for functionality that could
// be reasonably moved into an injected dependency.
type provider struct {
Name string `hcl:"name,label"`
Version string `hcl:"version"`
@@ -367,6 +385,13 @@ func loadProviders(tfmodule *tfconfig.Module, config *print.Config) []*Provider
for _, resource := range resources {
for _, r := range resource {
comments := loadComments(r.Pos.Filename, r.Pos.Line)
// skip over resources that are marked as being ignored
if strings.Contains(comments, "terraform-docs-ignore") {
continue
}
var version = ""
if l, ok := lock[r.Provider.Name]; ok {
version = l.Version
@@ -375,6 +400,10 @@ func loadProviders(tfmodule *tfconfig.Module, config *print.Config) []*Provider
}
key := fmt.Sprintf("%s.%s", r.Provider.Name, r.Provider.Alias)
if _, ok := discovered[key]; ok {
continue
}
discovered[key] = &Provider{
Name: r.Provider.Name,
Alias: types.String(r.Provider.Alias),
@@ -391,6 +420,7 @@ func loadProviders(tfmodule *tfconfig.Module, config *print.Config) []*Provider
for _, provider := range discovered {
providers = append(providers, provider)
}
return providers
}
@@ -427,6 +457,13 @@ func loadResources(tfmodule *tfconfig.Module, config *print.Config) []*Resource
for _, resource := range allResources {
for _, r := range resource {
comments := loadComments(r.Pos.Filename, r.Pos.Line)
// skip over resources that are marked as being ignored
if strings.Contains(comments, "terraform-docs-ignore") {
continue
}
var version string
if rv, ok := tfmodule.RequiredProviders[r.Provider.Name]; ok {
version = resourceVersion(rv.VersionConstraints)
@@ -444,7 +481,7 @@ func loadResources(tfmodule *tfconfig.Module, config *print.Config) []*Resource
description := ""
if config.Settings.ReadComments {
description = loadComments(r.Pos.Filename, r.Pos.Line)
description = comments
}
discovered[key] = &Resource{

View File

@@ -11,12 +11,14 @@ the root directory of this source tree.
package terraform
import (
"fmt"
"os"
"path/filepath"
"sort"
"testing"
"github.com/stretchr/testify/assert"
"golang.org/x/exp/slices"
"github.com/terraform-docs/terraform-docs/print"
)
@@ -38,6 +40,7 @@ func TestLoadModuleWithOptions(t *testing.T) {
assert.Equal(true, module.HasModuleCalls())
assert.Equal(true, module.HasProviders())
assert.Equal(true, module.HasRequirements())
assert.Equal(true, module.HasResources())
config.Sections.Header = false
config.Sections.Footer = true
@@ -774,6 +777,87 @@ func TestLoadProviders(t *testing.T) {
}
}
func TestLoadRequirements(t *testing.T) {
type expected struct {
requirements []string
}
tests := []struct {
name string
path string
expected expected
}{
{
name: "load module requirements from path",
path: "full-example",
expected: expected{
requirements: []string{"terraform >= 0.12", "aws >= 2.15.0"},
},
},
{
name: "load module requirements from path",
path: "no-requirements",
expected: expected{
requirements: []string{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
module, _ := loadModule(filepath.Join("testdata", tt.path))
requirements := loadRequirements(module)
assert.Equal(len(tt.expected.requirements), len(requirements))
for i, r := range tt.expected.requirements {
assert.Equal(r, fmt.Sprintf("%s %s", requirements[i].Name, requirements[i].Version))
}
})
}
}
func TestLoadResources(t *testing.T) {
type expected struct {
resources []string
}
tests := []struct {
name string
path string
expected expected
}{
{
name: "load module resources from path",
path: "full-example",
expected: expected{
resources: []string{"tls_private_key.baz", "aws_caller_identity.current", "null_resource.foo"},
},
},
{
name: "load module resources from path",
path: "no-resources",
expected: expected{
resources: []string{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
config := print.NewConfig()
module, _ := loadModule(filepath.Join("testdata", tt.path))
resources := loadResources(module, config)
assert.Equal(len(tt.expected.resources), len(resources))
for _, r := range resources {
assert.True(slices.Contains(tt.expected.resources, fmt.Sprintf("%s_%s.%s", r.ProviderName, r.Type, r.Name)))
}
})
}
}
func TestLoadComments(t *testing.T) {
tests := []struct {
name string

View File

@@ -21,8 +21,16 @@ data "aws_caller_identity" "current" {
provider = "aws"
}
# terraform-docs-ignore
data "aws_caller_identity" "ignored" {
provider = "aws"
}
resource "null_resource" "foo" {}
# terraform-docs-ignore
resource "null_resource" "ignored" {}
module "foo" {
source = "bar"
version = "1.2.3"
@@ -35,3 +43,9 @@ module "foobar" {
locals {
arn = provider::aws::arn_parse("arn:aws:iam::444455556666:role/example")
}
// terraform-docs-ignore
module "ignored" {
source = "baz"
version = "1.2.3"
}

View File

@@ -17,3 +17,8 @@ output "B" {
output "D" {
value = null
}
# terraform-docs-ignore
output "ignored" {
value = "e"
}

View File

@@ -30,7 +30,7 @@ variable "G" {
}
# terraform-docs-ignore
variable "H" {
variable "ignored" {
description = "H description"
default = null
}

View File

View File

View File

@@ -7,3 +7,8 @@ variable "B" {
output "B" {
value = "b"
}
// terraform-docs-ignore
output "ignored" {
value = "c"
}