Skip to content

Commit

Permalink
Do not fail conversion of non-HCL fenced markdown snippets (#1842)
Browse files Browse the repository at this point in the history
Fix #1839

The documentation pipeline was looking for Markdown code-fenced blocks.
In the case where they specify terraform or hcl
as the language, it is clear that they should be converted. However the
unmarked blocks may be carrying code that needs
to be converted or else they can be carrying text that needs to be
displayed with a fixed font.

This change introduces a regex-based guess that allows non-Terraform
code snippets to pass through as-is.

Fixes the following AWS build warnings (107-118 total, getting numbers a
bit mixed up):

```
   1 warning: failed to convert HCL for #/types/aws:autoscaling/GroupMixedInstancesPolicyLaunchTemplateOverrideInstanceRequirements:GroupMixedInstancesPolicyLaunchTemplateOverrideInstanceRequirements/acceleratorManufacturers to csharp: <nil>: unexpected HCL snippet in Convert;
```
  • Loading branch information
t0yv0 authored Apr 4, 2024
1 parent 0441ef3 commit 88fafd0
Show file tree
Hide file tree
Showing 5 changed files with 663 additions and 4 deletions.
4 changes: 3 additions & 1 deletion pkg/tfgen/convert_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ func (cc *cliConverter) FinishConvertingExamples(p pschema.PackageSpec) pschema.
return result
}

const cliConverterErrUnexpectedHCLSnippet = "unexpected HCL snippet in Convert"

// During FinishConvertingExamples pass, generator calls back into this function to continue
// PCL->lang translation from a pre-computed HCL->PCL translation table cc.pcls.
func (cc *cliConverter) Convert(
Expand All @@ -185,7 +187,7 @@ func (cc *cliConverter) Convert(
// Something skips adding failing conversion diagnostics to cc.pcls when pre-converting. The
// end-user experience is not affected much, the above example does not regress.
if !ok {
return "", hcl.Diagnostics{}, fmt.Errorf("unexpected HCL snippet in Convert")
return "", hcl.Diagnostics{}, fmt.Errorf("%s %q", cliConverterErrUnexpectedHCLSnippet, hclCode)
}
if example.Diagnostics.HasErrors() {
return "", example.Diagnostics, nil
Expand Down
72 changes: 72 additions & 0 deletions pkg/tfgen/convert_cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package tfgen

import (
"bytes"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -285,6 +286,77 @@ resource "azurerm_web_pubsub_custom_certificate" "test" {
err = g.Generate()
require.NoError(t, err)
})

t.Run("regress-1839", func(t *testing.T) {
mdPath := filepath.Join(
"test_data",
"TestConvertViaPulumiCLI",
"launch_template",
"launch_template.html.markdown",
)
md, err := os.ReadFile(mdPath)
require.NoError(t, err)

p := &schema.Provider{
ResourcesMap: map[string]*schema.Resource{
"aws_launch_template": {
Schema: map[string]*schema.Schema{
"instance_requirements": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cpu_manufacturers": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
},
},
},
}
pi := tfbridge.ProviderInfo{
P: shimv2.NewProvider(p),
Name: "aws",
Version: "0.0.1",
Resources: map[string]*tfbridge.ResourceInfo{
"aws_launch_template": {
Tok: "aws:index:LaunchTemplate",
Docs: &tfbridge.DocInfo{Markdown: md},
},
},
}

var stdout bytes.Buffer
var stderr bytes.Buffer

out := t.TempDir()

g, err := NewGenerator(GeneratorOptions{
Package: "aws",
Version: "0.0.1",
PluginHost: &testPluginHost{},
Language: Schema,
ProviderInfo: pi,
Root: afero.NewBasePathFs(afero.NewOsFs(), out),
Sink: diag.DefaultSink(&stdout, &stderr, diag.FormatOptions{
Color: colors.Never,
}),
})
require.NoError(t, err)

err = g.Generate()
require.NoError(t, err)

require.NotContains(t, stdout.String(), cliConverterErrUnexpectedHCLSnippet)
require.NotContains(t, stderr.String(), cliConverterErrUnexpectedHCLSnippet)
})
}

func TestNotYetImplementedErrorHandling(t *testing.T) {
Expand Down
17 changes: 14 additions & 3 deletions pkg/tfgen/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1437,9 +1437,12 @@ func (g *Generator) convertExamplesInner(
fprintf("%s%s", docs[tfBlock.start:tfBlock.end], codeFence)
} else {
fenceLanguage := docs[tfBlock.start : tfBlock.start+nextNewLine+1]
// Only attempt to convert code blocks that are either explicitly marked as Terraform, or unmarked.
if fenceLanguage == "```terraform\n" ||
fenceLanguage == "```hcl\n" || fenceLanguage == "```\n" {
hcl := docs[tfBlock.start+nextNewLine+1 : tfBlock.end]

// Only attempt to convert code blocks that are either explicitly marked as Terraform, or
// unmarked. For unmarked snippets further gate by a regex guess if it is actually Terraform.
if fenceLanguage == "```terraform\n" || fenceLanguage == "```hcl\n" ||
(fenceLanguage == "```\n" && guessIsHCL(hcl)) {

// generate the code block and append
if g.language.shouldConvertExamples() {
Expand Down Expand Up @@ -2108,3 +2111,11 @@ func replaceFooterLinks(text string, footerLinks map[string]string) string {
return link
})
}

var (
guessIsHCLPattern = regexp.MustCompile(`(resource|data)\s+["][^"]+["]\s+["][^"]+["]\s+[{]`)
)

func guessIsHCL(code string) bool {
return guessIsHCLPattern.MatchString(code)
}
55 changes: 55 additions & 0 deletions pkg/tfgen/docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1609,3 +1609,58 @@ func TestFixupImports(t *testing.T) {
})
}
}

func TestGuessIsHCL(t *testing.T) {
type testCase struct {
code string
hcl bool
}
testCases := []testCase{
{
code: `
data "aws_ami_ids" "ubuntu" {
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/ubuntu-*-*-amd64-server-*"]
}
}
`,
hcl: true,
},
{
code: `
resource "aws_ami" "example" {
name = "terraform-example"
virtualization_type = "hvm"
root_device_name = "/dev/xvda"
imds_support = "v2.0" # Enforce usage of IMDSv2.
ebs_block_device {
device_name = "/dev/xvda"
snapshot_id = "snap-xxxxxxxx"
volume_size = 8
}
}
`,
hcl: true,
},
{
code: `
Valid names:
* amazon-web-services
* amd
* nvidia
* xilinx
`,
hcl: false,
},
}
for i, tc := range testCases {
tc := tc
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
actual := guessIsHCL(tc.code)
assert.Equal(t, tc.hcl, actual)
})
}
}
Loading

0 comments on commit 88fafd0

Please sign in to comment.