diff --git a/.changes/unreleased/BREAKING CHANGES-20231206-140932.yaml b/.changes/unreleased/BREAKING CHANGES-20231206-140932.yaml new file mode 100644 index 00000000..e2519d9a --- /dev/null +++ b/.changes/unreleased/BREAKING CHANGES-20231206-140932.yaml @@ -0,0 +1,15 @@ +kind: BREAKING CHANGES +body: 'generate: templates using `printf` with either `codefile` or `tffile` to render code examples in markdown will need to switch to using those +functions directly. + +For example, switch the following template code: + +`{{printf "{{codefile \"shell\" %q}}" .ImportFile}}` + +to + +`{{codefile "shell" .ImportFile}}` +' +time: 2023-12-06T14:09:32.757057-05:00 +custom: + Issue: "300" diff --git a/.changes/unreleased/BUG FIXES-20231121-150034.yaml b/.changes/unreleased/BUG FIXES-20231121-150034.yaml new file mode 100644 index 00000000..69a080a8 --- /dev/null +++ b/.changes/unreleased/BUG FIXES-20231121-150034.yaml @@ -0,0 +1,6 @@ +kind: BUG FIXES +body: 'generate: fix incorrect rendering of example and import files for providers + with no docs templates or with generic fallback templates.' +time: 2023-11-21T15:00:34.216261-05:00 +custom: + Issue: "300" diff --git a/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_generic_templates.txtar b/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_generic_templates.txtar index 5a93b719..6738e4a4 100644 --- a/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_generic_templates.txtar +++ b/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_generic_templates.txtar @@ -20,13 +20,11 @@ compiling provider "scaffolding" using Terraform CLI binary from PATH if available, otherwise downloading latest Terraform CLI binary running terraform init getting provider schema -rendering missing docs +generating missing templates generating missing resource content -resource "scaffolding_example" fallback template exists -generating template for "scaffolding_example" +resource "scaffolding_example" fallback template exists, creating template generating missing data source content -resource "scaffolding_example" fallback template exists -generating template for "scaffolding_example" +data-source "scaffolding_example" fallback template exists, creating template generating missing provider content provider "terraform-provider-scaffolding" template exists, skipping rendering static website @@ -78,11 +76,7 @@ printf codefile: printf tffile: ## Example Usage -```terraform -data "scaffolding_example" "example" { - configurable_attribute = "some-value" -} -``` +{{tffile "$WORK/examples/data-sources/scaffolding_example/data-source.tf"}} codefile: @@ -182,18 +176,12 @@ printf codefile: Import is supported using the following syntax: -```shell -terraform import scaffolding_example.example -``` +{{codefile "shell" "$WORK/examples/resources/scaffolding_example/import.sh"}} printf tffile: ## Example Usage -```terraform -resource "scaffolding_example" "example" { - configurable_attribute = "some-value" -} -``` +{{tffile "$WORK/examples/resources/scaffolding_example/resource.tf"}} codefile: ## Import diff --git a/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_named_templates.txtar b/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_named_templates.txtar index 381ba095..35e77b7f 100644 --- a/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_named_templates.txtar +++ b/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_named_templates.txtar @@ -20,11 +20,11 @@ compiling provider "scaffolding" using Terraform CLI binary from PATH if available, otherwise downloading latest Terraform CLI binary running terraform init getting provider schema -rendering missing docs +generating missing templates generating missing resource content resource "scaffolding_example" template exists, skipping generating missing data source content -resource "scaffolding_example" template exists, skipping +data-source "scaffolding_example" template exists, skipping generating missing provider content provider "terraform-provider-scaffolding" template exists, skipping rendering static website diff --git a/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_no_templates.txtar b/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_no_templates.txtar index 07ae876f..0460419c 100644 --- a/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_no_templates.txtar +++ b/cmd/tfplugindocs/testdata/scripts/provider-build/generate/framework_provider_success_no_templates.txtar @@ -18,13 +18,13 @@ compiling provider "scaffolding" using Terraform CLI binary from PATH if available, otherwise downloading latest Terraform CLI binary running terraform init getting provider schema -rendering missing docs +generating missing templates generating missing resource content -generating template for "scaffolding_example" +generating new template for "scaffolding_example" generating missing data source content -generating template for "scaffolding_example" +generating new template for data-source "scaffolding_example" generating missing provider content -generating template for "terraform-provider-scaffolding" +generating new template for "terraform-provider-scaffolding" rendering static website cleaning rendered website dir rendering templated website to static markdown diff --git a/cmd/tfplugindocs/testdata/scripts/provider-build/generate/null_provider_success.txtar b/cmd/tfplugindocs/testdata/scripts/provider-build/generate/null_provider_success.txtar index c776a3e2..5caa782e 100644 --- a/cmd/tfplugindocs/testdata/scripts/provider-build/generate/null_provider_success.txtar +++ b/cmd/tfplugindocs/testdata/scripts/provider-build/generate/null_provider_success.txtar @@ -17,13 +17,11 @@ compiling provider "null" using Terraform CLI binary from PATH if available, otherwise downloading latest Terraform CLI binary running terraform init getting provider schema -rendering missing docs +generating missing templates generating missing resource content -resource "null_resource" fallback template exists -generating template for "null_resource" +resource "null_resource" fallback template exists, creating template generating missing data source content -resource "null_data_source" fallback template exists -generating template for "null_data_source" +data-source "null_data_source" fallback template exists, creating template generating missing provider content provider "terraform-provider-null" template exists, skipping rendering static website diff --git a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_generic_templates.txtar b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_generic_templates.txtar index 996b6ef0..156b7442 100644 --- a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_generic_templates.txtar +++ b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_generic_templates.txtar @@ -15,13 +15,11 @@ rendering website for provider "terraform-provider-scaffolding" (as "terraform-p copying any existing content to tmp dir exporting schema from JSON file getting provider schema -rendering missing docs +generating missing templates generating missing resource content -resource "scaffolding_example" fallback template exists -generating template for "scaffolding_example" +resource "scaffolding_example" fallback template exists, creating template generating missing data source content -resource "scaffolding_example" fallback template exists -generating template for "scaffolding_example" +data-source "scaffolding_example" fallback template exists, creating template generating missing provider content provider "terraform-provider-scaffolding" template exists, skipping rendering static website @@ -73,11 +71,7 @@ printf codefile: printf tffile: ## Example Usage -```terraform -data "scaffolding_example" "example" { - configurable_attribute = "some-value" -} -``` +{{tffile "$WORK/examples/data-sources/scaffolding_example/data-source.tf"}} codefile: @@ -177,18 +171,12 @@ printf codefile: Import is supported using the following syntax: -```shell -terraform import scaffolding_example.example -``` +{{codefile "shell" "$WORK/examples/resources/scaffolding_example/import.sh"}} printf tffile: ## Example Usage -```terraform -resource "scaffolding_example" "example" { - configurable_attribute = "some-value" -} -``` +{{tffile "$WORK/examples/resources/scaffolding_example/resource.tf"}} codefile: ## Import diff --git a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_legacy_docs.txtar b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_legacy_docs.txtar new file mode 100644 index 00000000..374dd8b6 --- /dev/null +++ b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_legacy_docs.txtar @@ -0,0 +1,210 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# Successful run of tfplugindocs on a Framework provider with docs in the legacy directory structure (i.e. r/.md.tmpl) +[!unix] skip +exec tfplugindocs --provider-name=terraform-provider-scaffolding --providers-schema=schema.json +cmp stdout expected-output.txt + +# Check that static files copied successfully to rendered docs directory +cmp templates/r/example.md docs/r/example.md +cmp templates/r/example.markdown docs/r/example.markdown +cmp templates/r/example.html.markdown docs/r/example.html.markdown +cmp templates/r/example.html.md docs/r/example.html.md + +cmp templates/d/example.md docs/d/example.md +cmp templates/d/example.markdown docs/d/example.markdown +cmp templates/d/example.html.markdown docs/d/example.html.markdown +cmp templates/d/example.html.md docs/d/example.html.md + +cmp templates/index.md docs/index.md +cmp templates/index.markdown docs/index.markdown +cmp templates/index.html.markdown docs/index.html.markdown +cmp templates/index.html.md docs/index.html.md +-- expected-output.txt -- +rendering website for provider "terraform-provider-scaffolding" (as "terraform-provider-scaffolding") +copying any existing content to tmp dir +exporting schema from JSON file +getting provider schema +generating missing templates +generating missing resource content +resource "scaffolding_example" static file exists, skipping +generating missing data source content +data-source "scaffolding_example" static file exists, skipping +generating missing provider content +provider "terraform-provider-scaffolding" static file exists, skipping +rendering static website +cleaning rendered website dir +rendering templated website to static markdown +copying non-template file: "d/example.html.markdown" +copying non-template file: "d/example.html.md" +copying non-template file: "d/example.markdown" +copying non-template file: "d/example.md" +copying non-template file: "index.html.markdown" +copying non-template file: "index.html.md" +copying non-template file: "index.markdown" +copying non-template file: "index.md" +copying non-template file: "r/example.html.markdown" +copying non-template file: "r/example.html.md" +copying non-template file: "r/example.markdown" +copying non-template file: "r/example.md" +-- templates/r/example.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/r/example.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/r/example.html.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/r/example.html.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/d/example.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/d/example.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/d/example.html.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/d/example.html.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/index.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/index.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/index.html.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/index.html.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- examples/README.md -- +# Examples + +This directory contains examples that are mostly used for documentation, but can also be run/tested manually via the Terraform CLI. + +The document generation tool looks for files in the following locations by default. All other *.tf files besides the ones mentioned below are ignored by the documentation tool. This is useful for creating examples that can run and/or ar testable even if some parts are not relevant for the documentation. + +* **provider/provider.tf** example file for the provider index page +* **data-sources/`full data source name`/data-source.tf** example file for the named data source page +* **resources/`full resource name`/resource.tf** example file for the named data source page +-- examples/data-sources/scaffolding_example/data-source.tf -- +data "scaffolding_example" "example" { + configurable_attribute = "some-value" +} +-- examples/provider/provider.tf -- +provider "scaffolding" { + # example configuration here +} +-- examples/resources/scaffolding_example/resource.tf -- +resource "scaffolding_example" "example" { + configurable_attribute = "some-value" +} +-- examples/resources/scaffolding_example/import.sh -- +terraform import scaffolding_example.example +-- schema.json -- +{ + "format_version": "1.0", + "provider_schemas": { + "registry.terraform.io/hashicorp/scaffolding": { + "provider": { + "version": 0, + "block": { + "attributes": { + "endpoint": { + "type": "string", + "description": "Example provider attribute", + "description_kind": "markdown", + "optional": true + } + }, + "description": "Example provider", + "description_kind": "markdown" + } + }, + "resource_schemas": { + "scaffolding_example": { + "version": 0, + "block": { + "attributes": { + "configurable_attribute": { + "type": "string", + "description": "Example configurable attribute", + "description_kind": "markdown", + "optional": true + }, + "defaulted": { + "type": "string", + "description": "Example configurable attribute with default value", + "description_kind": "markdown", + "optional": true, + "computed": true + }, + "id": { + "type": "string", + "description": "Example identifier", + "description_kind": "markdown", + "computed": true + } + }, + "description": "Example resource", + "description_kind": "markdown" + } + } + }, + "data_source_schemas": { + "scaffolding_example": { + "version": 0, + "block": { + "attributes": { + "configurable_attribute": { + "type": "string", + "description": "Example configurable attribute", + "description_kind": "markdown", + "optional": true + }, + "id": { + "type": "string", + "description": "Example identifier", + "description_kind": "markdown", + "computed": true + } + }, + "description": "Example data source", + "description_kind": "markdown" + } + } + } + } + } +} \ No newline at end of file diff --git a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_named_templates.txtar b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_named_templates.txtar index dc1722c2..1bae5cd3 100644 --- a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_named_templates.txtar +++ b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_named_templates.txtar @@ -15,11 +15,11 @@ rendering website for provider "terraform-provider-scaffolding" (as "terraform-p copying any existing content to tmp dir exporting schema from JSON file getting provider schema -rendering missing docs +generating missing templates generating missing resource content resource "scaffolding_example" template exists, skipping generating missing data source content -resource "scaffolding_example" template exists, skipping +data-source "scaffolding_example" template exists, skipping generating missing provider content provider "terraform-provider-scaffolding" template exists, skipping rendering static website diff --git a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_no_templates.txtar b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_no_templates.txtar index 00f55847..b8fe1392 100644 --- a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_no_templates.txtar +++ b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_no_templates.txtar @@ -13,13 +13,13 @@ cmp docs/resources/example.md expected-resource.md rendering website for provider "terraform-provider-scaffolding" (as "terraform-provider-scaffolding") exporting schema from JSON file getting provider schema -rendering missing docs +generating missing templates generating missing resource content -generating template for "scaffolding_example" +generating new template for "scaffolding_example" generating missing data source content -generating template for "scaffolding_example" +generating new template for data-source "scaffolding_example" generating missing provider content -generating template for "terraform-provider-scaffolding" +generating new template for "terraform-provider-scaffolding" rendering static website cleaning rendered website dir rendering templated website to static markdown diff --git a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_static_files.txtar b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_static_files.txtar new file mode 100644 index 00000000..a01fd1e4 --- /dev/null +++ b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/framework_provider_success_static_files.txtar @@ -0,0 +1,210 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# Successful run of tfplugindocs on a Framework provider with static files +[!unix] skip +exec tfplugindocs --provider-name=terraform-provider-scaffolding --providers-schema=schema.json +cmp stdout expected-output.txt + +# Check that static files copied successfully to rendered docs directory +cmp templates/resources/example.md docs/resources/example.md +cmp templates/resources/example.markdown docs/resources/example.markdown +cmp templates/resources/example.html.markdown docs/resources/example.html.markdown +cmp templates/resources/example.html.md docs/resources/example.html.md + +cmp templates/data-sources/example.md docs/data-sources/example.md +cmp templates/data-sources/example.markdown docs/data-sources/example.markdown +cmp templates/data-sources/example.html.markdown docs/data-sources/example.html.markdown +cmp templates/data-sources/example.html.md docs/data-sources/example.html.md + +cmp templates/index.md docs/index.md +cmp templates/index.markdown docs/index.markdown +cmp templates/index.html.markdown docs/index.html.markdown +cmp templates/index.html.md docs/index.html.md +-- expected-output.txt -- +rendering website for provider "terraform-provider-scaffolding" (as "terraform-provider-scaffolding") +copying any existing content to tmp dir +exporting schema from JSON file +getting provider schema +generating missing templates +generating missing resource content +resource "scaffolding_example" static file exists, skipping +generating missing data source content +data-source "scaffolding_example" static file exists, skipping +generating missing provider content +provider "terraform-provider-scaffolding" static file exists, skipping +rendering static website +cleaning rendered website dir +rendering templated website to static markdown +copying non-template file: "data-sources/example.html.markdown" +copying non-template file: "data-sources/example.html.md" +copying non-template file: "data-sources/example.markdown" +copying non-template file: "data-sources/example.md" +copying non-template file: "index.html.markdown" +copying non-template file: "index.html.md" +copying non-template file: "index.markdown" +copying non-template file: "index.md" +copying non-template file: "resources/example.html.markdown" +copying non-template file: "resources/example.html.md" +copying non-template file: "resources/example.markdown" +copying non-template file: "resources/example.md" +-- templates/resources/example.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/resources/example.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/resources/example.html.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/resources/example.html.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/data-sources/example.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/data-sources/example.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/data-sources/example.html.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/data-sources/example.html.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/index.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/index.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/index.html.markdown -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- templates/index.html.md -- +# Data Fields + +Name: {{.Name}} +Type: {{.Type}} +-- examples/README.md -- +# Examples + +This directory contains examples that are mostly used for documentation, but can also be run/tested manually via the Terraform CLI. + +The document generation tool looks for files in the following locations by default. All other *.tf files besides the ones mentioned below are ignored by the documentation tool. This is useful for creating examples that can run and/or ar testable even if some parts are not relevant for the documentation. + +* **provider/provider.tf** example file for the provider index page +* **data-sources/`full data source name`/data-source.tf** example file for the named data source page +* **resources/`full resource name`/resource.tf** example file for the named data source page +-- examples/data-sources/scaffolding_example/data-source.tf -- +data "scaffolding_example" "example" { + configurable_attribute = "some-value" +} +-- examples/provider/provider.tf -- +provider "scaffolding" { + # example configuration here +} +-- examples/resources/scaffolding_example/resource.tf -- +resource "scaffolding_example" "example" { + configurable_attribute = "some-value" +} +-- examples/resources/scaffolding_example/import.sh -- +terraform import scaffolding_example.example +-- schema.json -- +{ + "format_version": "1.0", + "provider_schemas": { + "registry.terraform.io/hashicorp/scaffolding": { + "provider": { + "version": 0, + "block": { + "attributes": { + "endpoint": { + "type": "string", + "description": "Example provider attribute", + "description_kind": "markdown", + "optional": true + } + }, + "description": "Example provider", + "description_kind": "markdown" + } + }, + "resource_schemas": { + "scaffolding_example": { + "version": 0, + "block": { + "attributes": { + "configurable_attribute": { + "type": "string", + "description": "Example configurable attribute", + "description_kind": "markdown", + "optional": true + }, + "defaulted": { + "type": "string", + "description": "Example configurable attribute with default value", + "description_kind": "markdown", + "optional": true, + "computed": true + }, + "id": { + "type": "string", + "description": "Example identifier", + "description_kind": "markdown", + "computed": true + } + }, + "description": "Example resource", + "description_kind": "markdown" + } + } + }, + "data_source_schemas": { + "scaffolding_example": { + "version": 0, + "block": { + "attributes": { + "configurable_attribute": { + "type": "string", + "description": "Example configurable attribute", + "description_kind": "markdown", + "optional": true + }, + "id": { + "type": "string", + "description": "Example identifier", + "description_kind": "markdown", + "computed": true + } + }, + "description": "Example data source", + "description_kind": "markdown" + } + } + } + } + } +} \ No newline at end of file diff --git a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/null_provider_success.txtar b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/null_provider_success.txtar index 9ffa5d4b..71e98aae 100644 --- a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/null_provider_success.txtar +++ b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/null_provider_success.txtar @@ -12,13 +12,11 @@ rendering website for provider "terraform-provider-null" (as "terraform-provider copying any existing content to tmp dir exporting schema from JSON file getting provider schema -rendering missing docs +generating missing templates generating missing resource content -resource "null_resource" fallback template exists -generating template for "null_resource" +resource "null_resource" fallback template exists, creating template generating missing data source content -resource "null_data_source" fallback template exists -generating template for "null_data_source" +data-source "null_data_source" fallback template exists, creating template generating missing provider content provider "terraform-provider-null" template exists, skipping rendering static website diff --git a/internal/provider/generate.go b/internal/provider/generate.go index a303a57a..13dd3d18 100644 --- a/internal/provider/generate.go +++ b/internal/provider/generate.go @@ -27,43 +27,36 @@ import ( ) var ( - examplesResourceFileTemplate = resourceFileTemplate("resources/{{.Name}}/resource.tf") - examplesResourceImportTemplate = resourceFileTemplate("resources/{{.Name}}/import.sh") - examplesDataSourceFileTemplate = resourceFileTemplate("data-sources/{{ .Name }}/data-source.tf") - examplesProviderFileTemplate = providerFileTemplate("provider/provider.tf") - - websiteResourceFileTemplate = resourceFileTemplate("resources/{{ .ShortName }}.md.tmpl") - websiteResourceFallbackFileTemplate = resourceFileTemplate("resources.md.tmpl") - websiteResourceFileStatic = []resourceFileTemplate{ - resourceFileTemplate("resources/{{ .ShortName }}.md"), - // TODO: warn for all of these, as they won't render? massage them to the proper output file name? - resourceFileTemplate("resources/{{ .ShortName }}.markdown"), - resourceFileTemplate("resources/{{ .ShortName }}.html.markdown"), - resourceFileTemplate("resources/{{ .ShortName }}.html.md"), - resourceFileTemplate("r/{{ .ShortName }}.markdown"), - resourceFileTemplate("r/{{ .ShortName }}.md"), - resourceFileTemplate("r/{{ .ShortName }}.html.markdown"), - resourceFileTemplate("r/{{ .ShortName }}.html.md"), - } - websiteDataSourceFileTemplate = resourceFileTemplate("data-sources/{{ .ShortName }}.md.tmpl") - websiteDataSourceFallbackFileTemplate = resourceFileTemplate("data-sources.md.tmpl") - websiteDataSourceFileStatic = []resourceFileTemplate{ - resourceFileTemplate("data-sources/{{ .ShortName }}.md"), - // TODO: warn for all of these, as they won't render? massage them to the proper output file name? - resourceFileTemplate("data-sources/{{ .ShortName }}.markdown"), - resourceFileTemplate("data-sources/{{ .ShortName }}.html.markdown"), - resourceFileTemplate("data-sources/{{ .ShortName }}.html.md"), - resourceFileTemplate("d/{{ .ShortName }}.markdown"), - resourceFileTemplate("d/{{ .ShortName }}.md"), - resourceFileTemplate("d/{{ .ShortName }}.html.markdown"), - resourceFileTemplate("d/{{ .ShortName }}.html.md"), - } - websiteProviderFileTemplate = providerFileTemplate("index.md.tmpl") - websiteProviderFileStatic = []providerFileTemplate{ - providerFileTemplate("index.markdown"), - providerFileTemplate("index.md"), - providerFileTemplate("index.html.markdown"), - providerFileTemplate("index.html.md"), + websiteResourceFile = "resources/%s.md.tmpl" + websiteResourceFallbackFile = "resources.md.tmpl" + websiteResourceFileStaticCandidates = []string{ + "resources/%s.md", + "resources/%s.markdown", + "resources/%s.html.markdown", + "resources/%s.html.md", + "r/%s.markdown", + "r/%s.md", + "r/%s.html.markdown", + "r/%s.html.md", + } + websiteDataSourceFile = "data-sources/%s.md.tmpl" + websiteDataSourceFallbackFile = "data-sources.md.tmpl" + websiteDataSourceFileStaticCandidates = []string{ + "data-sources/%s.md", + "data-sources/%s.markdown", + "data-sources/%s.html.markdown", + "data-sources/%s.html.md", + "d/%s.markdown", + "d/%s.md", + "d/%s.html.markdown", + "d/%s.html.md", + } + websiteProviderFile = "index.md.tmpl" + websiteProviderFileStaticCandidates = []string{ + "index.markdown", + "index.md", + "index.html.markdown", + "index.html.md", } managedWebsiteSubDirectories = []string{ @@ -223,10 +216,10 @@ func (g *generator) Generate(ctx context.Context) error { } } - g.infof("rendering missing docs") - err = g.renderMissingDocs(providerSchema) + g.infof("generating missing templates") + err = g.generateMissingTemplates(providerSchema) if err != nil { - return fmt.Errorf("error rendering missing docs: %w", err) + return fmt.Errorf("error generating missing templates: %w", err) } g.infof("rendering static website") @@ -263,22 +256,26 @@ func (g *generator) TempTemplatesDir() string { return filepath.Join(g.websiteTmpDir, "templates") } -func (g *generator) renderMissingResourceDoc(resourceName, typeName string, schema *tfjson.Schema, websiteFileTemplate resourceFileTemplate, fallbackWebsiteFileTemplate resourceFileTemplate, websiteStaticCandidateTemplates []resourceFileTemplate, examplesFileTemplate resourceFileTemplate, examplesImportTemplate *resourceFileTemplate) error { - tmplPath, err := websiteFileTemplate.Render(g.providerDir, resourceName, g.providerName) - if err != nil { - return fmt.Errorf("unable to render path for resource %q: %w", resourceName, err) - } - tmplPath = filepath.Join(g.TempTemplatesDir(), tmplPath) - if fileExists(tmplPath) { +func (g *generator) generateMissingResourceTemplate(resourceName string) error { + templatePath := fmt.Sprintf(websiteResourceFile, resourceShortName(resourceName, g.providerName)) + templatePath = filepath.Join(g.TempTemplatesDir(), templatePath) + if fileExists(templatePath) { g.infof("resource %q template exists, skipping", resourceName) return nil } - for _, candidate := range websiteStaticCandidateTemplates { - candidatePath, err := candidate.Render(g.providerDir, resourceName, g.providerName) + fallbackTemplatePath := filepath.Join(g.TempTemplatesDir(), websiteResourceFallbackFile) + if fileExists(fallbackTemplatePath) { + g.infof("resource %q fallback template exists, creating template", resourceName) + err := cp(fallbackTemplatePath, templatePath) if err != nil { - return fmt.Errorf("unable to render path for resource %q: %w", resourceName, err) + return fmt.Errorf("unable to copy fallback template for %q: %w", resourceName, err) } + return nil + } + + for _, candidate := range websiteResourceFileStaticCandidates { + candidatePath := fmt.Sprintf(candidate, resourceShortName(resourceName, g.providerName)) candidatePath = filepath.Join(g.TempTemplatesDir(), candidatePath) if fileExists(candidatePath) { g.infof("resource %q static file exists, skipping", resourceName) @@ -286,124 +283,85 @@ func (g *generator) renderMissingResourceDoc(resourceName, typeName string, sche } } - examplePath, err := examplesFileTemplate.Render(g.providerDir, resourceName, g.providerName) + g.infof("generating new template for %q", resourceName) + err := writeFile(templatePath, string(defaultResourceTemplate)) if err != nil { - return fmt.Errorf("unable to render example file path for %q: %w", resourceName, err) - } - if examplePath != "" { - examplePath = filepath.Join(g.ProviderExamplesDir(), examplePath) - } - if !fileExists(examplePath) { - examplePath = "" + return fmt.Errorf("unable to write template for %q: %w", resourceName, err) } - importPath := "" - if examplesImportTemplate != nil { - importPath, err = examplesImportTemplate.Render(g.providerDir, resourceName, g.providerName) - if err != nil { - return fmt.Errorf("unable to render example import file path for %q: %w", resourceName, err) - } - if importPath != "" { - importPath = filepath.Join(g.ProviderExamplesDir(), importPath) - } - if !fileExists(importPath) { - importPath = "" - } - } - - targetResourceTemplate := defaultResourceTemplate + return nil +} - fallbackTmplPath, err := fallbackWebsiteFileTemplate.Render(g.providerDir, resourceName, g.providerName) - if err != nil { - return fmt.Errorf("unable to render path for resource %q: %w", resourceName, err) +func (g *generator) generateMissingDataSourceTemplate(datasourceName string) error { + templatePath := fmt.Sprintf(websiteDataSourceFile, resourceShortName(datasourceName, g.providerName)) + templatePath = filepath.Join(g.TempTemplatesDir(), templatePath) + if fileExists(templatePath) { + g.infof("data-source %q template exists, skipping", datasourceName) + return nil } - fallbackTmplPath = filepath.Join(g.TempTemplatesDir(), fallbackTmplPath) - if fileExists(fallbackTmplPath) { - g.infof("resource %q fallback template exists", resourceName) - tmplData, err := os.ReadFile(fallbackTmplPath) + + fallbackTemplatePath := filepath.Join(g.TempTemplatesDir(), websiteDataSourceFallbackFile) + if fileExists(fallbackTemplatePath) { + g.infof("data-source %q fallback template exists, creating template", datasourceName) + err := cp(fallbackTemplatePath, templatePath) if err != nil { - return fmt.Errorf("unable to read file %q: %w", fallbackTmplPath, err) + return fmt.Errorf("unable to copy fallback template for %q: %w", datasourceName, err) } - targetResourceTemplate = resourceTemplate(tmplData) + return nil } - g.infof("generating template for %q", resourceName) - md, err := targetResourceTemplate.Render(g.providerDir, resourceName, g.providerName, g.renderedProviderName, typeName, examplePath, importPath, schema) - if err != nil { - return fmt.Errorf("unable to render template for %q: %w", resourceName, err) + for _, candidate := range websiteDataSourceFileStaticCandidates { + candidatePath := fmt.Sprintf(candidate, resourceShortName(datasourceName, g.providerName)) + candidatePath = filepath.Join(g.TempTemplatesDir(), candidatePath) + if fileExists(candidatePath) { + g.infof("data-source %q static file exists, skipping", datasourceName) + return nil + } } - err = writeFile(tmplPath, md) + g.infof("generating new template for data-source %q", datasourceName) + err := writeFile(templatePath, string(defaultResourceTemplate)) if err != nil { - return fmt.Errorf("unable to write file %q: %w", tmplPath, err) + return fmt.Errorf("unable to write template for %q: %w", datasourceName, err) } return nil } -func (g *generator) renderMissingProviderDoc(schema *tfjson.Schema, websiteFileTemplate providerFileTemplate, websiteStaticCandidateTemplates []providerFileTemplate, examplesFileTemplate providerFileTemplate) error { - tmplPath, err := websiteFileTemplate.Render(g.providerDir, g.providerName) - if err != nil { - return fmt.Errorf("unable to render path for provider %q: %w", g.providerName, err) - } - tmplPath = filepath.Join(g.TempTemplatesDir(), tmplPath) - if fileExists(tmplPath) { +func (g *generator) generateMissingProviderTemplate() error { + templatePath := filepath.Join(g.TempTemplatesDir(), websiteProviderFile) + if fileExists(templatePath) { g.infof("provider %q template exists, skipping", g.providerName) return nil } - for _, candidate := range websiteStaticCandidateTemplates { - candidatePath, err := candidate.Render(g.providerDir, g.providerName) - if err != nil { - return fmt.Errorf("unable to render path for provider %q: %w", g.providerName, err) - } - candidatePath = filepath.Join(g.TempTemplatesDir(), candidatePath) + for _, candidate := range websiteProviderFileStaticCandidates { + candidatePath := filepath.Join(g.TempTemplatesDir(), candidate) if fileExists(candidatePath) { g.infof("provider %q static file exists, skipping", g.providerName) return nil } } - examplePath, err := examplesFileTemplate.Render(g.providerDir, g.providerName) - if err != nil { - return fmt.Errorf("unable to render example file path for %q: %w", g.providerName, err) - } - if examplePath != "" { - examplePath = filepath.Join(g.ProviderExamplesDir(), examplePath) - } - if !fileExists(examplePath) { - examplePath = "" - } - - g.infof("generating template for %q", g.providerName) - md, err := defaultProviderTemplate.Render(g.providerDir, g.providerName, g.renderedProviderName, examplePath, schema) - if err != nil { - return fmt.Errorf("unable to render template for %q: %w", g.providerName, err) - } - - err = writeFile(tmplPath, md) + g.infof("generating new template for %q", g.providerName) + err := writeFile(templatePath, string(defaultProviderTemplate)) if err != nil { - return fmt.Errorf("unable to write file %q: %w", tmplPath, err) + return fmt.Errorf("unable to write template for %q: %w", g.providerName, err) } return nil } -func (g *generator) renderMissingDocs(providerSchema *tfjson.ProviderSchema) error { +func (g *generator) generateMissingTemplates(providerSchema *tfjson.ProviderSchema) error { g.infof("generating missing resource content") for name, schema := range providerSchema.ResourceSchemas { if g.ignoreDeprecated && schema.Block.Deprecated { continue } - err := g.renderMissingResourceDoc(name, "Resource", schema, - websiteResourceFileTemplate, - websiteResourceFallbackFileTemplate, - websiteResourceFileStatic, - examplesResourceFileTemplate, - &examplesResourceImportTemplate) + err := g.generateMissingResourceTemplate(name) if err != nil { - return fmt.Errorf("unable to render doc %q: %w", name, err) + return fmt.Errorf("unable to generate template for resource %q: %w", name, err) } } @@ -413,25 +371,16 @@ func (g *generator) renderMissingDocs(providerSchema *tfjson.ProviderSchema) err continue } - err := g.renderMissingResourceDoc(name, "Data Source", schema, - websiteDataSourceFileTemplate, - websiteDataSourceFallbackFileTemplate, - websiteDataSourceFileStatic, - examplesDataSourceFileTemplate, - nil) + err := g.generateMissingDataSourceTemplate(name) if err != nil { - return fmt.Errorf("unable to render doc %q: %w", name, err) + return fmt.Errorf("unable to generate template for data-source %q: %w", name, err) } } g.infof("generating missing provider content") - err := g.renderMissingProviderDoc(providerSchema.ConfigSchema, - websiteProviderFileTemplate, - websiteProviderFileStatic, - examplesProviderFileTemplate, - ) + err := g.generateMissingProviderTemplate() if err != nil { - return fmt.Errorf("unable to render provider doc: %w", err) + return fmt.Errorf("unable to generate template for provider: %w", err) } return nil diff --git a/internal/provider/template.go b/internal/provider/template.go index 3d71b419..f2a24cbc 100644 --- a/internal/provider/template.go +++ b/internal/provider/template.go @@ -30,9 +30,6 @@ type ( resourceTemplate string providerTemplate string - resourceFileTemplate string - providerFileTemplate string - docTemplate string ) @@ -116,37 +113,6 @@ func (t docTemplate) Render(providerDir string, out io.Writer) error { return renderTemplate(providerDir, "docTemplate", s, out, nil) } -func (t resourceFileTemplate) Render(providerDir, name, providerName string) (string, error) { - s := string(t) - if s == "" { - return "", nil - } - return renderStringTemplate(providerDir, "resourceFileTemplate", s, struct { - Name string - ShortName string - - ProviderName string - ProviderShortName string - }{ - Name: name, - ShortName: resourceShortName(name, providerName), - - ProviderName: providerName, - ProviderShortName: providerShortName(providerName), - }) -} - -func (t providerFileTemplate) Render(providerDir, name string) (string, error) { - s := string(t) - if s == "" { - return "", nil - } - return renderStringTemplate(providerDir, "providerFileTemplate", s, struct { - Name string - ShortName string - }{name, providerShortName(name)}) -} - func (t providerTemplate) Render(providerDir, providerName, renderedProviderName, exampleFile string, schema *tfjson.Schema) (string, error) { schemaBuffer := bytes.NewBuffer(nil) err := schemamd.Render(schema, schemaBuffer) @@ -167,8 +133,7 @@ func (t providerTemplate) Render(providerDir, providerName, renderedProviderName ProviderName string ProviderShortName string - - SchemaMarkdown string + SchemaMarkdown string RenderedProviderName string }{ @@ -250,7 +215,7 @@ description: |- {{ if .HasExample -}} ## Example Usage -{{ printf "{{tffile %q}}" .ExampleFile }} +{{tffile .ExampleFile }} {{- end }} {{ .SchemaMarkdown | trimspace }} @@ -260,7 +225,7 @@ description: |- Import is supported using the following syntax: -{{ printf "{{codefile \"shell\" %q}}" .ImportFile }} +{{codefile "shell" .ImportFile }} {{- end }} ` @@ -279,7 +244,7 @@ description: |- {{ if .HasExample -}} ## Example Usage -{{ printf "{{tffile %q}}" .ExampleFile }} +{{tffile .ExampleFile }} {{- end }} {{ .SchemaMarkdown | trimspace }} diff --git a/internal/provider/util.go b/internal/provider/util.go index ce9acf76..bb4f50e4 100644 --- a/internal/provider/util.go +++ b/internal/provider/util.go @@ -31,6 +31,12 @@ func copyFile(srcPath, dstPath string, mode os.FileMode) error { } defer srcFile.Close() + // Ensure destination path exists for file creation + err = os.MkdirAll(filepath.Dir(dstPath), 0755) + if err != nil { + return err + } + // 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 {