chore: add directory validation in separate file
This commit is contained in:
parent
94ca584b9e
commit
18680d0a15
@ -194,10 +194,6 @@ func validateContributorAvatarURL(avatarURL *string) []error {
|
|||||||
return problems
|
return problems
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFilePathToError(filePath string, err error) error {
|
|
||||||
return fmt.Errorf("%q: %v", filePath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateContributorProfile(yml contributorProfile) []error {
|
func validateContributorProfile(yml contributorProfile) []error {
|
||||||
allProblems := []error{}
|
allProblems := []error{}
|
||||||
|
|
||||||
@ -313,7 +309,6 @@ func aggregateContributorReadmeFiles() ([]readme, error) {
|
|||||||
for _, e := range dirEntries {
|
for _, e := range dirEntries {
|
||||||
dirPath := path.Join(rootRegistryPath, e.Name())
|
dirPath := path.Join(rootRegistryPath, e.Name())
|
||||||
if !e.IsDir() {
|
if !e.IsDir() {
|
||||||
problems = append(problems, fmt.Errorf("detected non-directory file %q at base of main Registry directory", dirPath))
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +326,7 @@ func aggregateContributorReadmeFiles() ([]readme, error) {
|
|||||||
|
|
||||||
if len(problems) != 0 {
|
if len(problems) != 0 {
|
||||||
return nil, validationPhaseError{
|
return nil, validationPhaseError{
|
||||||
phase: validationPhaseFilesystemRead,
|
phase: validationPhaseFileLoad,
|
||||||
errors: problems,
|
errors: problems,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
cmd/readmevalidation/errors.go
Normal file
28
cmd/readmevalidation/errors.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
var _ error = validationPhaseError{}
|
||||||
|
|
||||||
|
// validationPhaseError represents an error that occurred during a specific
|
||||||
|
// phase of README validation. It should be used to collect ALL validation
|
||||||
|
// errors that happened during a specific phase, rather than the first one
|
||||||
|
// encountered.
|
||||||
|
type validationPhaseError struct {
|
||||||
|
phase validationPhase
|
||||||
|
errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vpe validationPhaseError) Error() string {
|
||||||
|
msg := fmt.Sprintf("Error during %q phase of README validation:", vpe.phase.String())
|
||||||
|
for _, e := range vpe.errors {
|
||||||
|
msg += fmt.Sprintf("\n- %v", e)
|
||||||
|
}
|
||||||
|
msg += "\n"
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFilePathToError(filePath string, err error) error {
|
||||||
|
return fmt.Errorf("%q: %v", filePath, err)
|
||||||
|
}
|
||||||
@ -55,10 +55,17 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
log.Println("Provided API token does not belong to a Coder employee. Some README validation steps will be skipped compared to when they run in CI.")
|
log.Println("Provided API token does not belong to a Coder employee. Some README validation steps will be skipped compared to when they run in CI.")
|
||||||
}
|
}
|
||||||
fmt.Printf("actor %q is %s\n", actorUsername, actorOrgStatus.String())
|
fmt.Printf("Script GitHub actor %q has Coder organization status %q\n", actorUsername, actorOrgStatus.String())
|
||||||
|
|
||||||
log.Println("Starting README validation")
|
log.Println("Starting README validation")
|
||||||
|
|
||||||
|
// Validate file structure of main README directory
|
||||||
|
err = validateRepoStructure()
|
||||||
|
if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate contributor README files
|
||||||
allReadmeFiles, err := aggregateContributorReadmeFiles()
|
allReadmeFiles, err := aggregateContributorReadmeFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
@ -75,4 +82,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
log.Println("All relative URLs for READMEs are valid")
|
log.Println("All relative URLs for READMEs are valid")
|
||||||
log.Printf("Processed all READMEs in the %q directory\n", rootRegistryPath)
|
log.Printf("Processed all READMEs in the %q directory\n", rootRegistryPath)
|
||||||
|
|
||||||
|
// Validate modules
|
||||||
|
|
||||||
|
// Validate templates
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -72,12 +71,15 @@ func separateFrontmatter(readmeText string) (string, string, error) {
|
|||||||
type validationPhase int
|
type validationPhase int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// validationPhaseFilesystemRead indicates when a README file is being read
|
//
|
||||||
// from the file system
|
validationPhaseStructureValidation validationPhase = iota
|
||||||
validationPhaseFilesystemRead validationPhase = iota
|
|
||||||
|
|
||||||
// validationPhaseReadmeParsing indicates when a README's frontmatter is being
|
// validationPhaseFileLoad indicates when a README file is being read from
|
||||||
// parsed as YAML. This phase does not include YAML validation.
|
// the file system
|
||||||
|
validationPhaseFileLoad
|
||||||
|
|
||||||
|
// validationPhaseReadmeParsing indicates when a README's frontmatter is
|
||||||
|
// being parsed as YAML. This phase does not include YAML validation.
|
||||||
validationPhaseReadmeParsing
|
validationPhaseReadmeParsing
|
||||||
|
|
||||||
// validationPhaseReadmeValidation indicates when a README's frontmatter is
|
// validationPhaseReadmeValidation indicates when a README's frontmatter is
|
||||||
@ -92,7 +94,7 @@ const (
|
|||||||
|
|
||||||
func (p validationPhase) String() string {
|
func (p validationPhase) String() string {
|
||||||
switch p {
|
switch p {
|
||||||
case validationPhaseFilesystemRead:
|
case validationPhaseFileLoad:
|
||||||
return "Filesystem reading"
|
return "Filesystem reading"
|
||||||
case validationPhaseReadmeParsing:
|
case validationPhaseReadmeParsing:
|
||||||
return "README parsing"
|
return "README parsing"
|
||||||
@ -104,24 +106,3 @@ func (p validationPhase) String() string {
|
|||||||
return "Unknown validation phase"
|
return "Unknown validation phase"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ error = validationPhaseError{}
|
|
||||||
|
|
||||||
// validationPhaseError represents an error that occurred during a specific
|
|
||||||
// phase of README validation. It should be used to collect ALL validation
|
|
||||||
// errors that happened during a specific phase, rather than the first one
|
|
||||||
// encountered.
|
|
||||||
type validationPhaseError struct {
|
|
||||||
phase validationPhase
|
|
||||||
errors []error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vpe validationPhaseError) Error() string {
|
|
||||||
msg := fmt.Sprintf("Error during %q phase of README validation:", vpe.phase.String())
|
|
||||||
for _, e := range vpe.errors {
|
|
||||||
msg += fmt.Sprintf("\n- %v", e)
|
|
||||||
}
|
|
||||||
msg += "\n"
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|||||||
97
cmd/readmevalidation/repostructure.go
Normal file
97
cmd/readmevalidation/repostructure.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateCoderResourceDirectory(directoryPath string) []error {
|
||||||
|
errs := []error{}
|
||||||
|
|
||||||
|
dir, err := os.Stat(directoryPath)
|
||||||
|
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(directoryPath, err))
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
if !dir.IsDir() {
|
||||||
|
errs = append(errs, fmt.Errorf("%q: path is not a directory", directoryPath))
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := os.ReadDir(directoryPath)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("%q: %v", directoryPath, err))
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
for _, f := range files {
|
||||||
|
if !f.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceReadmePath := path.Join(directoryPath, f.Name(), "README.md")
|
||||||
|
_, err := os.Stat(resourceReadmePath)
|
||||||
|
if err == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
errs = append(errs, fmt.Errorf("%q: README file does not exist", resourceReadmePath))
|
||||||
|
} else {
|
||||||
|
errs = append(errs, addFilePathToError(resourceReadmePath, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
modulesPath := path.Join(dirPath, "modules")
|
||||||
|
if errs := validateCoderResourceDirectory(modulesPath); len(errs) != 0 {
|
||||||
|
problems = append(problems, errs...)
|
||||||
|
}
|
||||||
|
templatesPath := path.Join(dirPath, "templates")
|
||||||
|
if errs := validateCoderResourceDirectory(templatesPath); len(errs) != 0 {
|
||||||
|
problems = append(problems, errs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return problems
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateRepoStructure() error {
|
||||||
|
errs := validateRegistryDirectory()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
return validationPhaseError{
|
||||||
|
phase: validationPhaseFileLoad,
|
||||||
|
errors: errs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user