No issue to link – this was a problem we discovered while updating the Registry website ## Description This PR adds (very) basic validation for the GitHub Flavored Markdown alerts that we allow contributors to add to their README files. The errors that get generated should be correct, but the error messages themselves aren't as helpful as they could be. I'm going to be handling that in a separate PR, just so we can get this one in sooner. ### Changes made - Added function for validating the core structure of all GFM alerts - Updated existing README files that were failing the new validation requirements ## Type of Change - [ ] New module - [x] Bug fix - [ ] Feature/enhancement - [ ] Documentation - [ ] Other
123 lines
3.6 KiB
Go
123 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"strings"
|
|
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
func validateCoderTemplateReadmeBody(body string) []error {
|
|
var errs []error
|
|
|
|
trimmed := strings.TrimSpace(body)
|
|
if baseErrs := validateReadmeBody(trimmed); len(baseErrs) != 0 {
|
|
errs = append(errs, baseErrs...)
|
|
}
|
|
|
|
var nextLine string
|
|
foundParagraph := false
|
|
isInsideCodeBlock := false
|
|
lineNum := 0
|
|
|
|
lineScanner := bufio.NewScanner(strings.NewReader(trimmed))
|
|
for lineScanner.Scan() {
|
|
lineNum++
|
|
nextLine = lineScanner.Text()
|
|
|
|
// Code assumes that invalid headers would've already been handled by the base validation function, so we don't
|
|
// need to check deeper if the first line isn't an h1.
|
|
if lineNum == 1 {
|
|
if !strings.HasPrefix(nextLine, "# ") {
|
|
break
|
|
}
|
|
continue
|
|
}
|
|
|
|
if strings.HasPrefix(nextLine, "```") {
|
|
isInsideCodeBlock = !isInsideCodeBlock
|
|
if strings.HasPrefix(nextLine, "```hcl") {
|
|
errs = append(errs, xerrors.New("all .hcl language references must be converted to .tf"))
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Code assumes that we can treat this case as the end of the "h1 section" and don't need to process any further lines.
|
|
if lineNum > 1 && strings.HasPrefix(nextLine, "#") {
|
|
break
|
|
}
|
|
|
|
// Code assumes that if we've reached this point, the only other options are:
|
|
// (1) empty spaces, (2) paragraphs, (3) HTML, and (4) asset references made via [] syntax.
|
|
trimmedLine := strings.TrimSpace(nextLine)
|
|
isParagraph := trimmedLine != "" && !strings.HasPrefix(trimmedLine, "![") && !strings.HasPrefix(trimmedLine, "<")
|
|
foundParagraph = foundParagraph || isParagraph
|
|
}
|
|
|
|
if !foundParagraph {
|
|
errs = append(errs, xerrors.New("did not find paragraph within h1 section"))
|
|
}
|
|
if isInsideCodeBlock {
|
|
errs = append(errs, xerrors.New("code blocks inside h1 section do not all terminate before end of file"))
|
|
}
|
|
|
|
return errs
|
|
}
|
|
|
|
func validateCoderTemplateReadme(rm coderResourceReadme) []error {
|
|
var errs []error
|
|
for _, err := range validateCoderTemplateReadmeBody(rm.body) {
|
|
errs = append(errs, addFilePathToError(rm.filePath, err))
|
|
}
|
|
for _, err := range validateResourceGfmAlerts(rm.body) {
|
|
errs = append(errs, addFilePathToError(rm.filePath, err))
|
|
}
|
|
if fmErrs := validateCoderResourceFrontmatter("templates", rm.filePath, rm.frontmatter); len(fmErrs) != 0 {
|
|
errs = append(errs, fmErrs...)
|
|
}
|
|
return errs
|
|
}
|
|
|
|
func validateAllCoderTemplateReadmes(resources []coderResourceReadme) error {
|
|
var yamlValidationErrors []error
|
|
for _, readme := range resources {
|
|
errs := validateCoderTemplateReadme(readme)
|
|
if len(errs) > 0 {
|
|
yamlValidationErrors = append(yamlValidationErrors, errs...)
|
|
}
|
|
}
|
|
if len(yamlValidationErrors) != 0 {
|
|
return validationPhaseError{
|
|
phase: validationPhaseReadme,
|
|
errors: yamlValidationErrors,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateAllCoderTemplates() error {
|
|
const resourceType = "templates"
|
|
allReadmeFiles, err := aggregateCoderResourceReadmeFiles(resourceType)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
logger.Info(context.Background(), "processing template README files", "resource_type", resourceType, "num_files", len(allReadmeFiles))
|
|
resources, err := parseCoderResourceReadmeFiles(resourceType, allReadmeFiles)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = validateAllCoderTemplateReadmes(resources)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
logger.Info(context.Background(), "processed README files as valid Coder resources", "resource_type", resourceType, "num_files", len(resources))
|
|
|
|
if err := validateCoderResourceRelativeURLs(resources); err != nil {
|
|
return err
|
|
}
|
|
logger.Info(context.Background(), "all relative URLs for READMEs are valid", "resource_type", resourceType)
|
|
return nil
|
|
}
|