* refactor: update file structure to reflect new changes * refactor: start splitting up files * refactor: more domain splitting * refactor: remove directory validation from contributors file * fix: update repo structure checks * fix: improve check for user namespace subdirectories * docs: add missing words to comment * docs: update typo * refactor: make code easier to read * fix: update README files * fix: remove employer field entirely * fix: make Github field optional * refactor: rename files
146 lines
3.7 KiB
Go
146 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
supportedResourceTypes = []string{"modules", "templates"}
|
|
supportedUserNameSpaceDirectories = append(supportedResourceTypes[:], ".icons", ".images")
|
|
)
|
|
|
|
func validateCoderResourceSubdirectory(dirPath string) []error {
|
|
errs := []error{}
|
|
|
|
subDir, 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 !subDir.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, addFilePathToError(dirPath, err))
|
|
return errs
|
|
}
|
|
for _, f := range files {
|
|
// The .coder subdirectories are sometimes generated as part of Bun
|
|
// tests. These subdirectories will never be committed to the repo, but
|
|
// in the off chance that they don't get cleaned up properly, we want to
|
|
// skip over them
|
|
if !f.IsDir() || f.Name() == ".coder" {
|
|
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 {
|
|
userDirs, err := os.ReadDir(rootRegistryPath)
|
|
if err != nil {
|
|
return []error{err}
|
|
}
|
|
|
|
allErrs := []error{}
|
|
for _, d := range userDirs {
|
|
dirPath := path.Join(rootRegistryPath, d.Name())
|
|
if !d.IsDir() {
|
|
allErrs = append(allErrs, fmt.Errorf("detected non-directory file %q at base of main Registry directory", dirPath))
|
|
continue
|
|
}
|
|
|
|
contributorReadmePath := path.Join(dirPath, "README.md")
|
|
_, err := os.Stat(contributorReadmePath)
|
|
if err != nil {
|
|
allErrs = append(allErrs, err)
|
|
}
|
|
|
|
files, err := os.ReadDir(dirPath)
|
|
if err != nil {
|
|
allErrs = append(allErrs, err)
|
|
continue
|
|
}
|
|
|
|
for _, f := range files {
|
|
// Todo: Decide if there's anything more formal that we want to
|
|
// ensure about non-directories scoped to user namespaces
|
|
if !f.IsDir() {
|
|
continue
|
|
}
|
|
|
|
segment := f.Name()
|
|
filePath := path.Join(dirPath, segment)
|
|
|
|
if !slices.Contains(supportedUserNameSpaceDirectories, segment) {
|
|
allErrs = append(allErrs, fmt.Errorf("%q: only these sub-directories are allowed at top of user namespace: [%s]", filePath, strings.Join(supportedUserNameSpaceDirectories, ", ")))
|
|
continue
|
|
}
|
|
|
|
if slices.Contains(supportedResourceTypes, segment) {
|
|
errs := validateCoderResourceSubdirectory(filePath)
|
|
if len(errs) != 0 {
|
|
allErrs = append(allErrs, errs...)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return allErrs
|
|
}
|
|
|
|
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, errors.New("missing top-level .icons directory (used for storing reusable Coder resource icons)"))
|
|
}
|
|
|
|
if len(problems) != 0 {
|
|
return validationPhaseError{
|
|
phase: validationPhaseFileStructureValidation,
|
|
errors: problems,
|
|
}
|
|
}
|
|
return nil
|
|
}
|