From 1a5c102c2f49bd5cb92115213aef95e1fea7cf86 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 18 Apr 2025 20:49:04 +0000 Subject: [PATCH] refactor: more domain splitting --- cmd/readmevalidation/contributors.go | 37 +++++--- cmd/readmevalidation/main.go | 23 +---- cmd/readmevalidation/repoStructure.go | 116 ++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 32 deletions(-) create mode 100644 cmd/readmevalidation/repoStructure.go diff --git a/cmd/readmevalidation/contributors.go b/cmd/readmevalidation/contributors.go index 4dfdfe47..ad30d086 100644 --- a/cmd/readmevalidation/contributors.go +++ b/cmd/readmevalidation/contributors.go @@ -3,6 +3,7 @@ package main import ( "errors" "fmt" + "log" "net/url" "os" "path" @@ -12,9 +13,7 @@ import ( "gopkg.in/yaml.v3" ) -var ( - validContributorStatuses = []string{"official", "partner", "community"} -) +var validContributorStatuses = []string{"official", "partner", "community"} type contributorProfileFrontmatter struct { DisplayName string `yaml:"display_name"` @@ -48,10 +47,7 @@ func validateContributorGithubUsername(githubUsername string) error { return nil } -func validateContributorEmployerGithubUsername( - employerGithubUsername *string, - githubUsername string, -) []error { +func validateContributorEmployerGithubUsername(employerGithubUsername *string, githubUsername string) []error { if employerGithubUsername == nil { return nil } @@ -337,9 +333,7 @@ func aggregateContributorReadmeFiles() ([]readme, error) { return allReadmeFiles, nil } -func validateContributorRelativeUrls( - contributors map[string]contributorProfile, -) error { +func validateContributorRelativeUrls(contributors map[string]contributorProfile) error { // This function only validates relative avatar URLs for now, but it can be // beefed up to validate more in the future problems := []error{} @@ -376,3 +370,26 @@ func validateContributorRelativeUrls( errors: problems, } } + +func validateAllContributorFiles() error { + allReadmeFiles, err := aggregateContributorReadmeFiles() + if err != nil { + return err + } + + log.Printf("Processing %d README files\n", len(allReadmeFiles)) + contributors, err := parseContributorFiles(allReadmeFiles) + if err != nil { + return err + } + log.Printf("Processed %d README files as valid contributor profiles", len(contributors)) + + err = validateContributorRelativeUrls(contributors) + if err != nil { + return err + } + log.Println("All relative URLs for READMEs are valid") + + log.Printf("Processed all READMEs in the %q directory\n", rootRegistryPath) + return nil +} diff --git a/cmd/readmevalidation/main.go b/cmd/readmevalidation/main.go index bc8209fe..7f275c5f 100644 --- a/cmd/readmevalidation/main.go +++ b/cmd/readmevalidation/main.go @@ -11,29 +11,8 @@ import ( func main() { log.Println("Starting README validation") - allReadmeFiles, err := aggregateContributorReadmeFiles() + err := validateAllContributorFiles() if err != nil { log.Panic(err) } - - log.Printf("Processing %d README files\n", len(allReadmeFiles)) - contributors, err := parseContributorFiles(allReadmeFiles) - log.Printf( - "Processed %d README files as valid contributor profiles", - len(contributors), - ) - if err != nil { - log.Panic(err) - } - - err = validateContributorRelativeUrls(contributors) - if err != nil { - log.Panic(err) - } - log.Println("All relative URLs for READMEs are valid") - - log.Printf( - "Processed all READMEs in the %q directory\n", - rootRegistryPath, - ) } diff --git a/cmd/readmevalidation/repoStructure.go b/cmd/readmevalidation/repoStructure.go new file mode 100644 index 00000000..ed73910e --- /dev/null +++ b/cmd/readmevalidation/repoStructure.go @@ -0,0 +1,116 @@ +package main + +import ( + "errors" + "fmt" + "os" + "path" +) + +var supportedResourceTypes = []string{"modules", "templates"} + +func validateCoderResourceSubdirectory(dirPath string) []error { + errs := []error{} + + dir, err := os.Stat(dirPath) + if err != nil { + // It's valid for a specific resource directory not to exist. It's just + // that if it does exist, it must follow specific rules + if !errors.Is(err, os.ErrNotExist) { + errs = append(errs, addFilePathToError(dirPath, err)) + } + return errs + } + + if !dir.IsDir() { + errs = append(errs, fmt.Errorf("%q: path is not a directory", dirPath)) + return errs + } + + files, err := os.ReadDir(dirPath) + if err != nil { + errs = append(errs, fmt.Errorf("%q: %v", dirPath, err)) + return errs + } + for _, f := range files { + if !f.IsDir() { + continue + } + + resourceReadmePath := path.Join(dirPath, f.Name(), "README.md") + _, err := os.Stat(resourceReadmePath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + errs = append(errs, fmt.Errorf("%q: 'README.md' does not exist", resourceReadmePath)) + } else { + errs = append(errs, addFilePathToError(resourceReadmePath, err)) + } + } + + mainTerraformPath := path.Join(dirPath, f.Name(), "main.tf") + _, err = os.Stat(mainTerraformPath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + errs = append(errs, fmt.Errorf("%q: 'main.tf' file does not exist", mainTerraformPath)) + } else { + errs = append(errs, addFilePathToError(mainTerraformPath, err)) + } + } + + } + + return errs +} + +func validateRegistryDirectory() []error { + dirEntries, err := os.ReadDir(rootRegistryPath) + if err != nil { + return []error{err} + } + + problems := []error{} + for _, e := range dirEntries { + dirPath := path.Join(rootRegistryPath, e.Name()) + if !e.IsDir() { + problems = append(problems, fmt.Errorf("detected non-directory file %q at base of main Registry directory", dirPath)) + continue + } + + readmePath := path.Join(dirPath, "README.md") + _, err := os.Stat(readmePath) + if err != nil { + problems = append(problems, err) + } + + for _, rType := range supportedResourceTypes { + resourcePath := path.Join(dirPath, rType) + if errs := validateCoderResourceSubdirectory(resourcePath); len(errs) != 0 { + problems = append(problems, errs...) + } + } + } + + return problems +} + +func validateRepoStructure() error { + var problems []error + if errs := validateRegistryDirectory(); len(errs) != 0 { + problems = append(problems, errs...) + } + + _, err := os.Stat("./.icons") + if err != nil { + problems = append(problems, err) + } + + // Todo: figure out what other directories we want to make guarantees for + // and add them to this function + if len(problems) != 0 { + return validationPhaseError{ + phase: validationPhaseFileStructureValidation, + errors: problems, + } + } + return nil +}