Skip to content

Commit 18680d0

Browse files
committed
chore: add directory validation in separate file
1 parent 94ca584 commit 18680d0

File tree

5 files changed

+147
-35
lines changed

5 files changed

+147
-35
lines changed

cmd/readmevalidation/contributors.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,6 @@ func validateContributorAvatarURL(avatarURL *string) []error {
194194
return problems
195195
}
196196

197-
func addFilePathToError(filePath string, err error) error {
198-
return fmt.Errorf("%q: %v", filePath, err)
199-
}
200-
201197
func validateContributorProfile(yml contributorProfile) []error {
202198
allProblems := []error{}
203199

@@ -313,7 +309,6 @@ func aggregateContributorReadmeFiles() ([]readme, error) {
313309
for _, e := range dirEntries {
314310
dirPath := path.Join(rootRegistryPath, e.Name())
315311
if !e.IsDir() {
316-
problems = append(problems, fmt.Errorf("detected non-directory file %q at base of main Registry directory", dirPath))
317312
continue
318313
}
319314

@@ -331,7 +326,7 @@ func aggregateContributorReadmeFiles() ([]readme, error) {
331326

332327
if len(problems) != 0 {
333328
return nil, validationPhaseError{
334-
phase: validationPhaseFilesystemRead,
329+
phase: validationPhaseFileLoad,
335330
errors: problems,
336331
}
337332
}

cmd/readmevalidation/errors.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package main
2+
3+
import "fmt"
4+
5+
var _ error = validationPhaseError{}
6+
7+
// validationPhaseError represents an error that occurred during a specific
8+
// phase of README validation. It should be used to collect ALL validation
9+
// errors that happened during a specific phase, rather than the first one
10+
// encountered.
11+
type validationPhaseError struct {
12+
phase validationPhase
13+
errors []error
14+
}
15+
16+
func (vpe validationPhaseError) Error() string {
17+
msg := fmt.Sprintf("Error during %q phase of README validation:", vpe.phase.String())
18+
for _, e := range vpe.errors {
19+
msg += fmt.Sprintf("\n- %v", e)
20+
}
21+
msg += "\n"
22+
23+
return msg
24+
}
25+
26+
func addFilePathToError(filePath string, err error) error {
27+
return fmt.Errorf("%q: %v", filePath, err)
28+
}

cmd/readmevalidation/main.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,17 @@ func main() {
5555
} else {
5656
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.")
5757
}
58-
fmt.Printf("actor %q is %s\n", actorUsername, actorOrgStatus.String())
58+
fmt.Printf("Script GitHub actor %q has Coder organization status %q\n", actorUsername, actorOrgStatus.String())
5959

6060
log.Println("Starting README validation")
6161

62+
// Validate file structure of main README directory
63+
err = validateRepoStructure()
64+
if err != nil {
65+
log.Panic(err)
66+
}
67+
68+
// Validate contributor README files
6269
allReadmeFiles, err := aggregateContributorReadmeFiles()
6370
if err != nil {
6471
log.Panic(err)
@@ -75,4 +82,8 @@ func main() {
7582
}
7683
log.Println("All relative URLs for READMEs are valid")
7784
log.Printf("Processed all READMEs in the %q directory\n", rootRegistryPath)
85+
86+
// Validate modules
87+
88+
// Validate templates
7889
}

cmd/readmevalidation/readmes.go

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package main
33
import (
44
"bufio"
55
"errors"
6-
"fmt"
76
"strings"
87
)
98

@@ -72,12 +71,15 @@ func separateFrontmatter(readmeText string) (string, string, error) {
7271
type validationPhase int
7372

7473
const (
75-
// validationPhaseFilesystemRead indicates when a README file is being read
76-
// from the file system
77-
validationPhaseFilesystemRead validationPhase = iota
74+
//
75+
validationPhaseStructureValidation validationPhase = iota
7876

79-
// validationPhaseReadmeParsing indicates when a README's frontmatter is being
80-
// parsed as YAML. This phase does not include YAML validation.
77+
// validationPhaseFileLoad indicates when a README file is being read from
78+
// the file system
79+
validationPhaseFileLoad
80+
81+
// validationPhaseReadmeParsing indicates when a README's frontmatter is
82+
// being parsed as YAML. This phase does not include YAML validation.
8183
validationPhaseReadmeParsing
8284

8385
// validationPhaseReadmeValidation indicates when a README's frontmatter is
@@ -92,7 +94,7 @@ const (
9294

9395
func (p validationPhase) String() string {
9496
switch p {
95-
case validationPhaseFilesystemRead:
97+
case validationPhaseFileLoad:
9698
return "Filesystem reading"
9799
case validationPhaseReadmeParsing:
98100
return "README parsing"
@@ -104,24 +106,3 @@ func (p validationPhase) String() string {
104106
return "Unknown validation phase"
105107
}
106108
}
107-
108-
var _ error = validationPhaseError{}
109-
110-
// validationPhaseError represents an error that occurred during a specific
111-
// phase of README validation. It should be used to collect ALL validation
112-
// errors that happened during a specific phase, rather than the first one
113-
// encountered.
114-
type validationPhaseError struct {
115-
phase validationPhase
116-
errors []error
117-
}
118-
119-
func (vpe validationPhaseError) Error() string {
120-
msg := fmt.Sprintf("Error during %q phase of README validation:", vpe.phase.String())
121-
for _, e := range vpe.errors {
122-
msg += fmt.Sprintf("\n- %v", e)
123-
}
124-
msg += "\n"
125-
126-
return msg
127-
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"path"
8+
)
9+
10+
func validateCoderResourceDirectory(directoryPath string) []error {
11+
errs := []error{}
12+
13+
dir, err := os.Stat(directoryPath)
14+
if err != nil {
15+
// It's valid for a specific resource directory not to exist. It's just
16+
// that if it does exist, it must follow specific rules
17+
if !errors.Is(err, os.ErrNotExist) {
18+
errs = append(errs, addFilePathToError(directoryPath, err))
19+
}
20+
return errs
21+
}
22+
23+
if !dir.IsDir() {
24+
errs = append(errs, fmt.Errorf("%q: path is not a directory", directoryPath))
25+
return errs
26+
}
27+
28+
files, err := os.ReadDir(directoryPath)
29+
if err != nil {
30+
errs = append(errs, fmt.Errorf("%q: %v", directoryPath, err))
31+
return errs
32+
}
33+
for _, f := range files {
34+
if !f.IsDir() {
35+
continue
36+
}
37+
38+
resourceReadmePath := path.Join(directoryPath, f.Name(), "README.md")
39+
_, err := os.Stat(resourceReadmePath)
40+
if err == nil {
41+
continue
42+
}
43+
44+
if errors.Is(err, os.ErrNotExist) {
45+
errs = append(errs, fmt.Errorf("%q: README file does not exist", resourceReadmePath))
46+
} else {
47+
errs = append(errs, addFilePathToError(resourceReadmePath, err))
48+
}
49+
}
50+
51+
return errs
52+
}
53+
54+
func validateRegistryDirectory() []error {
55+
dirEntries, err := os.ReadDir(rootRegistryPath)
56+
if err != nil {
57+
return []error{err}
58+
}
59+
60+
problems := []error{}
61+
for _, e := range dirEntries {
62+
dirPath := path.Join(rootRegistryPath, e.Name())
63+
if !e.IsDir() {
64+
problems = append(problems, fmt.Errorf("detected non-directory file %q at base of main Registry directory", dirPath))
65+
continue
66+
}
67+
68+
readmePath := path.Join(dirPath, "README.md")
69+
_, err := os.Stat(readmePath)
70+
if err != nil {
71+
problems = append(problems, err)
72+
}
73+
74+
modulesPath := path.Join(dirPath, "modules")
75+
if errs := validateCoderResourceDirectory(modulesPath); len(errs) != 0 {
76+
problems = append(problems, errs...)
77+
}
78+
templatesPath := path.Join(dirPath, "templates")
79+
if errs := validateCoderResourceDirectory(templatesPath); len(errs) != 0 {
80+
problems = append(problems, errs...)
81+
}
82+
}
83+
84+
return problems
85+
}
86+
87+
func validateRepoStructure() error {
88+
errs := validateRegistryDirectory()
89+
if len(errs) != 0 {
90+
return validationPhaseError{
91+
phase: validationPhaseFileLoad,
92+
errors: errs,
93+
}
94+
}
95+
96+
return nil
97+
}

0 commit comments

Comments
 (0)