From 17c9667db68079faa10e71f19b3be586d5e81ac1 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Tue, 15 Apr 2025 16:35:33 +0000 Subject: [PATCH] wip: commit progress for module validation --- cmd/readmevalidation/coderResources.go | 100 ++++++++++++++++++ cmd/readmevalidation/readmes.go | 8 +- .../{repostructure.go => repoStructure.go} | 0 3 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 cmd/readmevalidation/coderResources.go rename cmd/readmevalidation/{repostructure.go => repoStructure.go} (100%) diff --git a/cmd/readmevalidation/coderResources.go b/cmd/readmevalidation/coderResources.go new file mode 100644 index 00000000..b1c3928f --- /dev/null +++ b/cmd/readmevalidation/coderResources.go @@ -0,0 +1,100 @@ +package main + +import ( + "errors" + "fmt" + "net/url" + "strings" +) + +type coderResourceFrontmatter struct { + Description string `yaml:"description"` + IconURL string `yaml:"icon"` + DisplayName *string `yaml:"display_name"` + Tags []string `yaml:"tags"` + Verified *bool `yaml:"verified"` +} + +type coderResourceReadme struct { + oldFrontmatter *coderResourceFrontmatter + newFrontmatter *coderResourceFrontmatter + newBody string + moduleName string + filePath string +} + +func validateCoderResourceDisplayName(displayName *string) error { + if displayName == nil { + return nil + } + + if *displayName == "" { + return errors.New("if defined, display_name must not be empty string") + } + + return nil +} + +func validateCoderResourceDescription(description string) error { + if description == "" { + return errors.New("frontmatter description cannot be empty") + } + return nil +} + +func validateCoderResourceIconURL(iconURL string) []error { + problems := []error{} + + if iconURL == "" { + problems = append(problems, errors.New("icon URL cannot be empty")) + return problems + } + + isAbsoluteURL := !strings.HasPrefix(iconURL, ".") && !strings.HasPrefix(iconURL, "/") + if isAbsoluteURL { + if _, err := url.ParseRequestURI(iconURL); err != nil { + problems = append(problems, errors.New("absolute icon URL is not correctly formatted")) + } + if strings.Contains(iconURL, "?") { + problems = append(problems, errors.New("icon URLs cannot contain query parameters")) + } + return problems + } + + // Would normally be skittish about having relative paths like this, but it + // should be safe because we have guarantees about the structure of the + // repo, and where this logic will run + isPermittedRelativeURL := strings.HasPrefix(iconURL, "./") || + strings.HasPrefix(iconURL, "/") || + strings.HasPrefix(iconURL, "../../../.logos") + if !isPermittedRelativeURL { + problems = append(problems, errors.New("relative icon URL must either be scoped to that module's directory, or the top-level /.logos directory")) + } + + return problems +} + +func validateCoderResourceTags(tags []string) error { + if len(tags) == 0 { + return nil + } + + // All of these tags are used for the module/template filter controls in the + // Registry site. Need to make sure they can all be placed in the browser + // URL without issue + invalidTags := []string{} + for _, t := range tags { + if t != url.QueryEscape(t) { + invalidTags = append(invalidTags, t) + } + } + if len(invalidTags) != 0 { + return fmt.Errorf("found invalid tags (tags that cannot be used for filter state in the Registry website): [%s]", strings.Join(invalidTags, ", ")) + } + + return nil +} + +func validateCoderResourceReadmeBody(body string) []error { + return nil +} diff --git a/cmd/readmevalidation/readmes.go b/cmd/readmevalidation/readmes.go index a8d2bd68..00bcca18 100644 --- a/cmd/readmevalidation/readmes.go +++ b/cmd/readmevalidation/readmes.go @@ -71,10 +71,12 @@ func separateFrontmatter(readmeText string) (string, string, error) { type validationPhase int const ( - // - validationPhaseStructureValidation validationPhase = iota + // validationPhaseFileStructureValidation indicates when the entire Registry + // directory is being verified for having all files be placed in the file + // system as expected. + validationPhaseFileStructureValidation validationPhase = iota - // validationPhaseFileLoad indicates when a README file is being read from + // validationPhaseFileLoad indicates when README files are being read from // the file system validationPhaseFileLoad diff --git a/cmd/readmevalidation/repostructure.go b/cmd/readmevalidation/repoStructure.go similarity index 100% rename from cmd/readmevalidation/repostructure.go rename to cmd/readmevalidation/repoStructure.go