@@ -8,48 +8,73 @@ import (
88 "strings"
99)
1010
11- const rootRegistryPath = "./registry"
11+ const (
12+ rootRegistryPath = "./registry"
13+ fence = "---"
14+
15+ // validationPhaseFileStructureValidation indicates when the entire Registry
16+ // directory is being verified for having all files be placed in the file
17+ // system as expected.
18+ validationPhaseFileStructureValidation validationPhase = "File structure validation"
1219
13- var supportedAvatarFileFormats = []string {".png" , ".jpeg" , ".jpg" , ".gif" , ".svg" }
20+ // validationPhaseFileLoad indicates when README files are being read from
21+ // the file system.
22+ validationPhaseFileLoad = "Filesystem reading"
23+
24+ // validationPhaseReadmeParsing indicates when a README's frontmatter is
25+ // being parsed as YAML. This phase does not include YAML validation.
26+ validationPhaseReadmeParsing = "README parsing"
1427
15- // readme represents a single README file within the repo (usually within the
16- // top-level "/registry" directory).
28+ // validationPhaseAssetCrossReference indicates when a README's frontmatter
29+ // is having all its relative URLs be validated for whether they point to
30+ // valid resources.
31+ validationPhaseAssetCrossReference = "Cross-referencing relative asset URLs"
32+ )
33+
34+ var (
35+ supportedAvatarFileFormats = []string {".png" , ".jpeg" , ".jpg" , ".gif" , ".svg" }
36+ // TODO: an example of what this regex matches would be useful, I think it's just
37+ readmeHeaderRe = regexp .MustCompile ("^(#{1,})(\\ s*)" )
38+ )
39+
40+ // validationPhase represents a specific phase during README validation. It is expected that each phase is discrete, and
41+ // errors during one will prevent a future phase from starting.
42+ type validationPhase string
43+
44+ // readme represents a single README file within the repo (usually within the top-level "/registry" directory).
1745type readme struct {
1846 filePath string
1947 rawText string
2048}
2149
22- // separateFrontmatter attempts to separate a README file's frontmatter content
23- // from the main README body, returning both values in that order. It does not
24- // validate whether the structure of the frontmatter is valid (i.e., that it's
50+ // separateFrontmatter attempts to separate a README file's frontmatter content from the main README body, returning
51+ // both values in that order. It does not validate whether the structure of the frontmatter is valid (i.e., that it's
2552// structured as YAML).
2653func separateFrontmatter (readmeText string ) (string , string , error ) {
2754 if readmeText == "" {
2855 return "" , "" , errors .New ("README is empty" )
2956 }
3057
31- const fence = "---"
3258 fm := ""
3359 body := ""
60+ nextLine := ""
3461 fenceCount := 0
3562
3663 lineScanner := bufio .NewScanner (strings .NewReader (strings .TrimSpace (readmeText )))
3764 for lineScanner .Scan () {
38- nextLine : = lineScanner .Text ()
65+ nextLine = lineScanner .Text ()
3966 if fenceCount < 2 && nextLine == fence {
4067 fenceCount ++
4168 continue
4269 }
43- // Break early if the very first line wasn't a fence, because then we
44- // know for certain that the README has problems
70+ // Break early if the very first line wasn't a fence, because then we know for certain that the README has problems.
4571 if fenceCount == 0 {
4672 break
4773 }
4874
49- // It should be safe to trim each line of the frontmatter on a per-line
50- // basis, because there shouldn't be any extra meaning attached to the
51- // indentation. The same does NOT apply to the README; best we can do is
52- // gather all the lines, and then trim around it
75+ // It should be safe to trim each line of the frontmatter on a per-line basis, because there shouldn't be any
76+ // extra meaning attached to the indentation. The same does NOT apply to the README; best we can do is gather
77+ // all the lines and then trim around it.
5378 if inReadmeBody := fenceCount >= 2 ; inReadmeBody {
5479 body += nextLine + "\n "
5580 } else {
@@ -66,20 +91,17 @@ func separateFrontmatter(readmeText string) (string, string, error) {
6691 return fm , strings .TrimSpace (body ), nil
6792}
6893
69- var readmeHeaderRe = regexp .MustCompile ("^(#{1,})(\\ s*)" )
70-
71- // Todo: This seems to work okay for now, but the really proper way of doing
72- // this is by parsing this as an AST, and then checking the resulting nodes
94+ // TODO: This seems to work okay for now, but the really proper way of doing this is by parsing this as an AST, and then
95+ // checking the resulting nodes.
7396func validateReadmeBody (body string ) []error {
7497 trimmed := strings .TrimSpace (body )
7598
7699 if trimmed == "" {
77100 return []error {errors .New ("README body is empty" )}
78101 }
79102
80- // If the very first line of the README, there's a risk that the rest of the
81- // validation logic will break, since we don't have many guarantees about
82- // how the README is actually structured
103+ // If the very first line of the README, there's a risk that the rest of the validation logic will break, since we
104+ // don't have many guarantees about how the README is actually structured.
83105 if ! strings .HasPrefix (trimmed , "# " ) {
84106 return []error {errors .New ("README body must start with ATX-style h1 header (i.e., \" # \" )" )}
85107 }
@@ -88,14 +110,14 @@ func validateReadmeBody(body string) []error {
88110 latestHeaderLevel := 0
89111 foundFirstH1 := false
90112 isInCodeBlock := false
113+ nextLine := ""
91114
92115 lineScanner := bufio .NewScanner (strings .NewReader (trimmed ))
93116 for lineScanner .Scan () {
94- nextLine : = lineScanner .Text ()
117+ nextLine = lineScanner .Text ()
95118
96- // Have to check this because a lot of programming languages support #
97- // comments (including Terraform), and without any context, there's no
98- // way to tell the difference between a markdown header and code comment
119+ // Have to check this because a lot of programming languages support # comments (including Terraform), and
120+ // without any context, there's no way to tell the difference between a markdown header and code comment.
99121 if strings .HasPrefix (nextLine , "```" ) {
100122 isInCodeBlock = ! isInCodeBlock
101123 continue
@@ -109,8 +131,7 @@ func validateReadmeBody(body string) []error {
109131 continue
110132 }
111133
112- spaceAfterHeader := headerGroups [2 ]
113- if spaceAfterHeader == "" {
134+ if spaceAfterHeader := headerGroups [2 ]; spaceAfterHeader == "" {
114135 errs = append (errs , errors .New ("header does not have space between header characters and main header text" ))
115136 }
116137
@@ -121,8 +142,7 @@ func validateReadmeBody(body string) []error {
121142 continue
122143 }
123144
124- // If we have obviously invalid headers, it's not really safe to keep
125- // proceeding with the rest of the content
145+ // If we have obviously invalid headers, it's not really safe to keep proceeding with the rest of the content.
126146 if nextHeaderLevel == 1 {
127147 errs = append (errs , errors .New ("READMEs cannot contain more than h1 header" ))
128148 break
@@ -132,47 +152,16 @@ func validateReadmeBody(body string) []error {
132152 break
133153 }
134154
135- // This is something we need to enforce for accessibility, not just for
136- // the Registry website, but also when users are viewing the README
137- // files in the GitHub web view
155+ // This is something we need to enforce for accessibility, not just for the Registry website, but also when
156+ // users are viewing the README files in the GitHub web view
138157 if nextHeaderLevel > latestHeaderLevel && nextHeaderLevel != (latestHeaderLevel + 1 ) {
139158 errs = append (errs , fmt .Errorf ("headers are not allowed to increase more than 1 level at a time" ))
140159 continue
141160 }
142161
143- // As long as the above condition passes, there's no problems with
144- // going up a header level or going down 1+ header levels
162+ // As long as the above condition passes, there's no problems with going up a header level or going down 1+ header levels.
145163 latestHeaderLevel = nextHeaderLevel
146164 }
147165
148166 return errs
149167}
150-
151- // validationPhase represents a specific phase during README validation. It is
152- // expected that each phase is discrete, and errors during one will prevent a
153- // future phase from starting.
154- type validationPhase string
155-
156- const (
157- // validationPhaseFileStructureValidation indicates when the entire Registry
158- // directory is being verified for having all files be placed in the file
159- // system as expected.
160- validationPhaseFileStructureValidation validationPhase = "File structure validation"
161-
162- // validationPhaseFileLoad indicates when README files are being read from
163- // the file system
164- validationPhaseFileLoad = "Filesystem reading"
165-
166- // validationPhaseReadmeParsing indicates when a README's frontmatter is
167- // being parsed as YAML. This phase does not include YAML validation.
168- validationPhaseReadmeParsing = "README parsing"
169-
170- // validationPhaseReadmeValidation indicates when a README's frontmatter is
171- // being validated as proper YAML with expected keys.
172- validationPhaseReadmeValidation = "README validation"
173-
174- // validationPhaseAssetCrossReference indicates when a README's frontmatter
175- // is having all its relative URLs be validated for whether they point to
176- // valid resources.
177- validationPhaseAssetCrossReference = "Cross-referencing relative asset URLs"
178- )
0 commit comments