Update README validation and Go version
- Downgrade Go version in CI to 1.24 for consistency. - Fix naming and path issues in `readmevalidation` code. - Improve regex validation for module and namespace names. - Correct typos and improve comments for clarity.
This commit is contained in:
parent
5b6d878fd7
commit
3b6b1ba4b9
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@ -63,8 +63,8 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.25.0"
|
go-version: "1.24"
|
||||||
- name: Validate Reademde
|
- name: Validate README
|
||||||
run: go build ./cmd/readmevalidation && ./readmevalidation
|
run: go build ./cmd/readmevalidation && ./readmevalidation
|
||||||
- name: Remove build file artifact
|
- name: Remove build file artifact
|
||||||
run: rm ./readmevalidation
|
run: rm ./readmevalidation
|
||||||
|
|||||||
@ -14,20 +14,14 @@ var (
|
|||||||
terraformSourceRe = regexp.MustCompile(`^\s*source\s*=\s*"([^"]+)"`)
|
terraformSourceRe = regexp.MustCompile(`^\s*source\s*=\s*"([^"]+)"`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func normalizeModuleName(name string) string {
|
func extractNamespaceAndModuleFromPath(filePath string) (namespace string, moduleName string, err error) {
|
||||||
// Normalize module names by replacing hyphens with underscores for comparison
|
// Expected path format: registry/<namespace>/modules/<module-name>/README.md.
|
||||||
// since Terraform allows both but directory names typically use hyphens
|
|
||||||
return strings.ReplaceAll(name, "-", "_")
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractNamespaceAndModuleFromPath(filePath string) (string, string, error) {
|
|
||||||
// Expected path format: registry/<namespace>/modules/<module-name>/README.md
|
|
||||||
parts := strings.Split(filepath.Clean(filePath), string(filepath.Separator))
|
parts := strings.Split(filepath.Clean(filePath), string(filepath.Separator))
|
||||||
if len(parts) < 5 || parts[0] != "registry" || parts[2] != "modules" || parts[4] != "README.md" {
|
if len(parts) < 5 || parts[0] != "registry" || parts[2] != "modules" || parts[4] != "README.md" {
|
||||||
return "", "", xerrors.Errorf("invalid module path format: %s", filePath)
|
return "", "", xerrors.Errorf("invalid module path format: %s", filePath)
|
||||||
}
|
}
|
||||||
namespace := parts[1]
|
namespace = parts[1]
|
||||||
moduleName := parts[3]
|
moduleName = parts[3]
|
||||||
return namespace, moduleName, nil
|
return namespace, moduleName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,21 +49,21 @@ func validateModuleSourceURL(body string, filePath string) []error {
|
|||||||
isInsideTerraform = true
|
isInsideTerraform = true
|
||||||
firstTerraformBlock = false
|
firstTerraformBlock = false
|
||||||
} else if isInsideTerraform {
|
} else if isInsideTerraform {
|
||||||
// End of first terraform block
|
// End of first terraform block.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if isInsideTerraform {
|
if isInsideTerraform {
|
||||||
// Check for any source line in the first terraform block
|
// Check for any source line in the first terraform block.
|
||||||
if matches := terraformSourceRe.FindStringSubmatch(nextLine); matches != nil {
|
if matches := terraformSourceRe.FindStringSubmatch(nextLine); matches != nil {
|
||||||
actualSource := matches[1]
|
actualSource := matches[1]
|
||||||
if actualSource == expectedSource {
|
if actualSource == expectedSource {
|
||||||
foundCorrectSource = true
|
foundCorrectSource = true
|
||||||
break
|
break
|
||||||
} else if strings.HasPrefix(actualSource, "registry.coder.com/") && strings.Contains(actualSource, "/"+moduleName+"/coder") {
|
} else if strings.HasPrefix(actualSource, "registry.coder.com/") && strings.Contains(actualSource, "/"+moduleName+"/coder") {
|
||||||
// Found source for this module but with wrong namespace/format
|
// Found source for this module but with wrong namespace/format.
|
||||||
errs = append(errs, xerrors.Errorf("incorrect source URL format: found %q, expected %q", actualSource, expectedSource))
|
errs = append(errs, xerrors.Errorf("incorrect source URL format: found %q, expected %q", actualSource, expectedSource))
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ var (
|
|||||||
terraformVersionRe = regexp.MustCompile(`^\s*\bversion\s+=`)
|
terraformVersionRe = regexp.MustCompile(`^\s*\bversion\s+=`)
|
||||||
|
|
||||||
// Matches the format "> [!INFO]". Deliberately using a broad pattern to catch formatting issues that can mess up
|
// Matches the format "> [!INFO]". Deliberately using a broad pattern to catch formatting issues that can mess up
|
||||||
// the renderer for the Registry website
|
// the renderer for the Registry website.
|
||||||
gfmAlertRegex = regexp.MustCompile(`^>(\s*)\[!(\w+)\](\s*)(.*)`)
|
gfmAlertRegex = regexp.MustCompile(`^>(\s*)\[!(\w+)\](\s*)(.*)`)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ type coderResourceFrontmatter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A slice version of the struct tags from coderResourceFrontmatter. Might be worth using reflection to generate this
|
// A slice version of the struct tags from coderResourceFrontmatter. Might be worth using reflection to generate this
|
||||||
// list at runtime in the future, but this should be okay for now
|
// list at runtime in the future, but this should be okay for now.
|
||||||
var supportedCoderResourceStructKeys = []string{
|
var supportedCoderResourceStructKeys = []string{
|
||||||
"description", "icon", "display_name", "verified", "tags", "supported_os",
|
"description", "icon", "display_name", "verified", "tags", "supported_os",
|
||||||
// TODO: This is an old, officially deprecated key from the archived coder/modules repo. We can remove this once we
|
// TODO: This is an old, officially deprecated key from the archived coder/modules repo. We can remove this once we
|
||||||
@ -315,15 +315,15 @@ func validateResourceGfmAlerts(readmeBody string) []error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nested GFM alerts is such a weird mistake that it's probably not really safe to keep trying to process the
|
// Nested GFM alerts is such a weird mistake that it's probably not really safe to keep trying to process the
|
||||||
// rest of the content, so this will prevent any other validations from happening for the given line
|
// rest of the content, so this will prevent any other validations from happening for the given line.
|
||||||
if isInsideGfmQuotes {
|
if isInsideGfmQuotes {
|
||||||
errs = append(errs, errors.New("registry does not support nested GFM alerts"))
|
errs = append(errs, xerrors.New("registry does not support nested GFM alerts"))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
leadingWhitespace := currentMatch[1]
|
leadingWhitespace := currentMatch[1]
|
||||||
if len(leadingWhitespace) != 1 {
|
if len(leadingWhitespace) != 1 {
|
||||||
errs = append(errs, errors.New("GFM alerts must have one space between the '>' and the start of the GFM brackets"))
|
errs = append(errs, xerrors.New("GFM alerts must have one space between the '>' and the start of the GFM brackets"))
|
||||||
}
|
}
|
||||||
isInsideGfmQuotes = true
|
isInsideGfmQuotes = true
|
||||||
|
|
||||||
@ -347,7 +347,7 @@ func validateResourceGfmAlerts(readmeBody string) []error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if gfmAlertRegex.Match([]byte(sourceLine)) {
|
if gfmAlertRegex.MatchString(sourceLine) {
|
||||||
errs = append(errs, xerrors.Errorf("README has an incomplete GFM alert at the end of the file"))
|
errs = append(errs, xerrors.Errorf("README has an incomplete GFM alert at the end of the file"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ type contributorProfileFrontmatter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A slice version of the struct tags from contributorProfileFrontmatter. Might be worth using reflection to generate
|
// A slice version of the struct tags from contributorProfileFrontmatter. Might be worth using reflection to generate
|
||||||
// this list at runtime in the future, but this should be okay for now
|
// this list at runtime in the future, but this should be okay for now.
|
||||||
var supportedContributorProfileStructKeys = []string{"display_name", "bio", "status", "avatar", "linkedin", "github", "website", "support_email"}
|
var supportedContributorProfileStructKeys = []string{"display_name", "bio", "status", "avatar", "linkedin", "github", "website", "support_email"}
|
||||||
|
|
||||||
type contributorProfileReadme struct {
|
type contributorProfileReadme struct {
|
||||||
|
|||||||
@ -13,12 +13,11 @@ import (
|
|||||||
|
|
||||||
var supportedUserNameSpaceDirectories = append(supportedResourceTypes, ".images")
|
var supportedUserNameSpaceDirectories = append(supportedResourceTypes, ".images")
|
||||||
|
|
||||||
// validNameRe validates that names contain only alphanumeric characters and hyphens
|
// validNameRe validates that names contain only alphanumeric characters and hyphens.
|
||||||
var validNameRe = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$`)
|
var validNameRe = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$`)
|
||||||
|
|
||||||
|
|
||||||
// validateCoderResourceSubdirectory validates that the structure of a module or template within a namespace follows all
|
// validateCoderResourceSubdirectory validates that the structure of a module or template within a namespace follows all
|
||||||
// expected file conventions
|
// expected file conventions.
|
||||||
func validateCoderResourceSubdirectory(dirPath string) []error {
|
func validateCoderResourceSubdirectory(dirPath string) []error {
|
||||||
resourceDir, err := os.Stat(dirPath)
|
resourceDir, err := os.Stat(dirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -47,7 +46,7 @@ func validateCoderResourceSubdirectory(dirPath string) []error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate module/template name
|
// Validate module/template name.
|
||||||
if !validNameRe.MatchString(f.Name()) {
|
if !validNameRe.MatchString(f.Name()) {
|
||||||
errs = append(errs, xerrors.Errorf("%q: name contains invalid characters (only alphanumeric characters and hyphens are allowed)", path.Join(dirPath, f.Name())))
|
errs = append(errs, xerrors.Errorf("%q: name contains invalid characters (only alphanumeric characters and hyphens are allowed)", path.Join(dirPath, f.Name())))
|
||||||
continue
|
continue
|
||||||
@ -90,7 +89,7 @@ func validateRegistryDirectory() []error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate namespace name
|
// Validate namespace name.
|
||||||
if !validNameRe.MatchString(nDir.Name()) {
|
if !validNameRe.MatchString(nDir.Name()) {
|
||||||
allErrs = append(allErrs, xerrors.Errorf("%q: namespace name contains invalid characters (only alphanumeric characters and hyphens are allowed)", namespacePath))
|
allErrs = append(allErrs, xerrors.Errorf("%q: namespace name contains invalid characters (only alphanumeric characters and hyphens are allowed)", namespacePath))
|
||||||
continue
|
continue
|
||||||
@ -136,7 +135,7 @@ func validateRegistryDirectory() []error {
|
|||||||
|
|
||||||
// validateRepoStructure validates that the structure of the repo is "correct enough" to do all necessary validation
|
// validateRepoStructure validates that the structure of the repo is "correct enough" to do all necessary validation
|
||||||
// checks. It is NOT an exhaustive validation of the entire repo structure – it only checks the parts of the repo that
|
// checks. It is NOT an exhaustive validation of the entire repo structure – it only checks the parts of the repo that
|
||||||
// are relevant for the main validation steps
|
// are relevant for the main validation steps.
|
||||||
func validateRepoStructure() error {
|
func validateRepoStructure() error {
|
||||||
var errs []error
|
var errs []error
|
||||||
if vrdErrs := validateRegistryDirectory(); len(vrdErrs) != 0 {
|
if vrdErrs := validateRegistryDirectory(); len(vrdErrs) != 0 {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user