tools/template-check/gotemplate/gotemplate.go (59 lines of code) (raw):
package gotemplate
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"strings"
)
// Note: this is allowlisted to combat other issues like `=` instead of `==`, but it is possible we
// need to add more options to this list in the future, like `private`. The main thing we want to
// prevent is targeting `beta` in version guards, because it mishandles either `ga` or `private`.
// TODO: refactor to be a regex as the permitted formats are more varied under go templates
var allowedGuards = []string{
`{{- if ne $.TargetVersionName "ga" }}`,
`{{ if ne $.TargetVersionName "ga" -}}`,
`{{- if ne $.TargetVersionName "ga" -}}`,
`{{ if ne $.TargetVersionName "ga" }}`,
`{{- if eq $.TargetVersionName "ga" }}`,
`{{ if eq $.TargetVersionName "ga" -}}`,
`{{- if eq $.TargetVersionName "ga" -}}`,
`{{ if eq $.TargetVersionName "ga" }}`,
"{{- if ne $.TargetVersionName `ga` }}",
"{{ if ne $.TargetVersionName `ga` -}}",
"{{- if ne $.TargetVersionName `ga` -}}",
"{{ if ne $.TargetVersionName `ga` }}",
"{{- if eq $.TargetVersionName `ga` }}",
"{{ if eq $.TargetVersionName `ga` -}}",
"{{- if eq $.TargetVersionName `ga` -}}",
"{{ if eq $.TargetVersionName `ga` }}",
}
// Note: this does not account for _every_ possible use of a version guard (for example, those
// starting with `version.nil?`), because the logic would start to get more complicated. Instead,
// the goal is to capture (and validate) all "standard" version guards that would be added for new
// resources/fields.
func isVersionGuard(line string) bool {
re := regexp.MustCompile(`{{-? if (eq|ne) \$\.TargetVersionName`)
return re.MatchString(line)
}
func isValidVersionGuard(line string) bool {
for _, g := range allowedGuards {
if strings.Contains(line, g) {
return true
}
}
return false
}
// CheckVersionGuards scans the input for version guards, and checks that those version guards are
// valid. Invalid version guards are returned along with the line number in which they occurred.
func CheckVersionGuards(r io.Reader) []string {
scanner := bufio.NewScanner(r)
lineNum := 1
var invalidGuards []string
for scanner.Scan() {
if isVersionGuard(scanner.Text()) && !isValidVersionGuard(scanner.Text()) {
invalidGuards = append(invalidGuards, fmt.Sprintf("%d: %s", lineNum, scanner.Text()))
}
lineNum++
}
return invalidGuards
}
// CheckVersionGuardsForFile scans the file for version guards, and checks that those version
// guards are valid. Invalid version guards are returned along with the line number in which they
// occurred.
func CheckVersionGuardsForFile(filename string) ([]string, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
return CheckVersionGuards(file), nil
}