diff --git a/.changes/0.20.1.md b/.changes/0.20.1.md new file mode 100644 index 00000000..dfe21ccb --- /dev/null +++ b/.changes/0.20.1.md @@ -0,0 +1,8 @@ +## 0.20.1 (November 26, 2024) + +BUG FIXES: + +* validate: Fixed a bug that caused false positive validation errors for resource types that have the same name as the provider. ([#419](https://github.com/hashicorp/terraform-plugin-docs/issues/419)) +* generate: Fixed a bug that caused all generated resource documentation to have the same content when the provider has a resource type with the same name as the provider. ([#419](https://github.com/hashicorp/terraform-plugin-docs/issues/419)) +* generate: Fixed a bug that would return an error when a static file exists in both `templates` and `docs`, which will now be ignored. ([#421](https://github.com/hashicorp/terraform-plugin-docs/issues/421)) + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7520b64c..4899981c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,7 +93,7 @@ jobs: cd .changes sed -e "1{/# /d;}" -e "2{/^$/d;}" ${{ needs.changelog-version.outputs.version }}.md > /tmp/release-notes.txt - - uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0 + - uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.gitignore b/.gitignore index 19e2604d..588539de 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ tfproviderdocsgen # JetBrains IDEs files .idea/ *.iws + +# VSCode files +.vscode \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 8ef33d91..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Launch", - "type": "go", - "request": "launch", - "mode": "auto", - "program": "${workspaceFolder}/cmd/tfplugindocs/", - "env": {}, - "args": [], - // Set this to a directory with the provider source code you - // with which you want to test generation. - "cwd": "${workspaceFolder}/../../hashicorp/terraform-provider-null" - } - ] -} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 52f01b97..2f9b32e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.20.1 (November 26, 2024) + +BUG FIXES: + +* validate: Fixed a bug that caused false positive validation errors for resource types that have the same name as the provider. ([#419](https://github.com/hashicorp/terraform-plugin-docs/issues/419)) +* generate: Fixed a bug that caused all generated resource documentation to have the same content when the provider has a resource type with the same name as the provider. ([#419](https://github.com/hashicorp/terraform-plugin-docs/issues/419)) +* generate: Fixed a bug that would return an error when a static file exists in both `templates` and `docs`, which will now be ignored. ([#421](https://github.com/hashicorp/terraform-plugin-docs/issues/421)) + ## 0.20.0 (November 06, 2024) NOTES: diff --git a/README.md b/README.md index dc1edecc..b1e6d166 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,11 @@ When you run `tfplugindocs`, by default from the root directory of a provider co * Generate function template files, if missing (Requires Terraform v1.8.0+) * Generate ephemeral resource template files, if missing (Requires Terraform v1.10.0+) * Copy all non-template files to the output website directory + +> [!NOTE] +> +> Non-template files that already exist in the output website directory will not be overwritten. + * Process all the remaining templates to generate files for the output website directory For inspiration, you can look at the templates and output of the diff --git a/go.mod b/go.mod index 57fd2989..7c6e43be 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/zclconf/go-cty v1.15.0 go.abhg.dev/goldmark/frontmatter v0.2.0 golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df - golang.org/x/text v0.19.0 + golang.org/x/text v0.20.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 2b362738..701a8f58 100644 --- a/go.sum +++ b/go.sum @@ -155,8 +155,8 @@ golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -174,8 +174,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/internal/check/file_mismatch.go b/internal/check/file_mismatch.go index c53827d7..97d78d70 100644 --- a/internal/check/file_mismatch.go +++ b/internal/check/file_mismatch.go @@ -84,7 +84,7 @@ func (check *FileMismatchCheck) Run() error { return result } -// ResourceFileMismatchCheck checks for mismatched files, either missing or extraneous, against the resource/datasouce schema +// ResourceFileMismatchCheck checks for mismatched files, either missing or extraneous, against the resource/datasource schema func (check *FileMismatchCheck) ResourceFileMismatchCheck(files []os.DirEntry, resourceType string, schemas map[string]*tfjson.Schema) error { if len(files) == 0 { log.Printf("[DEBUG] Skipping %s file mismatch checks due to missing file list", resourceType) @@ -200,7 +200,11 @@ func (check *FileMismatchCheck) FunctionFileMismatchCheck(files []os.DirEntry, f func (check *FileMismatchCheck) IgnoreFileMismatch(file string) bool { for _, ignoreResourceName := range check.Options.IgnoreFileMismatch { - if ignoreResourceName == fileResourceName(check.Options.ProviderShortName, file) { + if ignoreResourceName == fileResourceNameWithProvider(check.Options.ProviderShortName, file) { + return true + } else if ignoreResourceName == TrimFileExtension(file) { + // While uncommon, it is valid for a resource type to be named the same as the provider itself. + // https://github.com/hashicorp/terraform-plugin-docs/issues/419 return true } } @@ -219,7 +223,13 @@ func (check *FileMismatchCheck) IgnoreFileMissing(resourceName string) bool { } func fileHasResource(schemaResources map[string]*tfjson.Schema, providerName, file string) bool { - if _, ok := schemaResources[fileResourceName(providerName, file)]; ok { + if _, ok := schemaResources[fileResourceNameWithProvider(providerName, file)]; ok { + return true + } + + // While uncommon, it is valid for a resource type to be named the same as the provider itself. + // https://github.com/hashicorp/terraform-plugin-docs/issues/419 + if _, ok := schemaResources[TrimFileExtension(file)]; ok { return true } @@ -234,7 +244,7 @@ func fileHasFunction(functions map[string]*tfjson.FunctionSignature, file string return false } -func fileResourceName(providerName, fileName string) string { +func fileResourceNameWithProvider(providerName, fileName string) string { resourceSuffix := TrimFileExtension(fileName) return fmt.Sprintf("%s_%s", providerName, resourceSuffix) @@ -244,7 +254,12 @@ func resourceHasFile(files []os.DirEntry, providerName, resourceName string) boo var found bool for _, file := range files { - if fileResourceName(providerName, file.Name()) == resourceName { + if fileResourceNameWithProvider(providerName, file.Name()) == resourceName { + found = true + break + } else if TrimFileExtension(file.Name()) == resourceName { + // While uncommon, it is valid for a resource type to be named the same as the provider itself. + // https://github.com/hashicorp/terraform-plugin-docs/issues/419 found = true break } diff --git a/internal/check/file_mismatch_test.go b/internal/check/file_mismatch_test.go index 50ef8dfc..67f7103d 100644 --- a/internal/check/file_mismatch_test.go +++ b/internal/check/file_mismatch_test.go @@ -82,7 +82,7 @@ func TestFileResourceName(t *testing.T) { testCase := testCase t.Run(name, func(t *testing.T) { t.Parallel() - got := fileResourceName("test", testCase.File) + got := fileResourceNameWithProvider("test", testCase.File) want := testCase.Expect if got != want { @@ -115,6 +115,19 @@ func TestFileMismatchCheck(t *testing.T) { }, }, }, + "all found - resource with no suffix": { + ResourceFiles: fstest.MapFS{ + "test.md": {}, + }, + Options: &FileMismatchOptions{ + ProviderShortName: "test", + Schema: &tfjson.ProviderSchema{ + ResourceSchemas: map[string]*tfjson.Schema{ + "test": {}, + }, + }, + }, + }, "all found - function": { FunctionFiles: fstest.MapFS{ "function1.md": {}, @@ -181,6 +194,23 @@ func TestFileMismatchCheck(t *testing.T) { }, }, }, + "ignore extra file - resource with no suffix": { + ResourceFiles: fstest.MapFS{ + "resource1.md": {}, + "resource2.md": {}, + "test.md": {}, + }, + Options: &FileMismatchOptions{ + IgnoreFileMismatch: []string{"test"}, + ProviderShortName: "test", + Schema: &tfjson.ProviderSchema{ + ResourceSchemas: map[string]*tfjson.Schema{ + "test_resource1": {}, + "test_resource2": {}, + }, + }, + }, + }, "ignore extra file - function": { FunctionFiles: fstest.MapFS{ "function1.md": {}, @@ -214,6 +244,21 @@ func TestFileMismatchCheck(t *testing.T) { }, ExpectError: true, }, + "missing file - resource with no suffix": { + ResourceFiles: fstest.MapFS{ + "resource1.md": {}, + }, + Options: &FileMismatchOptions{ + ProviderShortName: "test", + Schema: &tfjson.ProviderSchema{ + ResourceSchemas: map[string]*tfjson.Schema{ + "test_resource1": {}, + "test": {}, + }, + }, + }, + ExpectError: true, + }, "missing file - function": { FunctionFiles: fstest.MapFS{ "function1.md": {}, diff --git a/internal/provider/util.go b/internal/provider/util.go index 7a3ec336..9041cf01 100644 --- a/internal/provider/util.go +++ b/internal/provider/util.go @@ -4,6 +4,7 @@ package provider import ( + "errors" "fmt" "io" "log" @@ -45,6 +46,10 @@ func copyFile(srcPath, dstPath string, mode os.FileMode) error { // If the destination file already exists, we shouldn't blow it away dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, mode) if err != nil { + // If the file already exists, we can skip it without returning an error. + if errors.Is(err, os.ErrExist) { + return nil + } return err } defer dstFile.Close() @@ -71,16 +76,15 @@ func removeAllExt(file string) string { // has either the providerShortName or the providerShortName concatenated with the // templateFileName (stripped of file extension. func resourceSchema(schemas map[string]*tfjson.Schema, providerShortName, templateFileName string) (*tfjson.Schema, string) { - if schema, ok := schemas[providerShortName]; ok { - return schema, providerShortName - } - resName := providerShortName + "_" + removeAllExt(templateFileName) - if schema, ok := schemas[resName]; ok { return schema, resName } + if schema, ok := schemas[providerShortName]; ok { + return schema, providerShortName + } + return nil, resName }