@@ -8,48 +8,73 @@ import (
8
8
"strings"
9
9
)
10
10
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"
12
19
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"
14
27
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).
17
45
type readme struct {
18
46
filePath string
19
47
rawText string
20
48
}
21
49
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
25
52
// structured as YAML).
26
53
func separateFrontmatter (readmeText string ) (string , string , error ) {
27
54
if readmeText == "" {
28
55
return "" , "" , errors .New ("README is empty" )
29
56
}
30
57
31
- const fence = "---"
32
58
fm := ""
33
59
body := ""
60
+ nextLine := ""
34
61
fenceCount := 0
35
62
36
63
lineScanner := bufio .NewScanner (strings .NewReader (strings .TrimSpace (readmeText )))
37
64
for lineScanner .Scan () {
38
- nextLine : = lineScanner .Text ()
65
+ nextLine = lineScanner .Text ()
39
66
if fenceCount < 2 && nextLine == fence {
40
67
fenceCount ++
41
68
continue
42
69
}
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.
45
71
if fenceCount == 0 {
46
72
break
47
73
}
48
74
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.
53
78
if inReadmeBody := fenceCount >= 2 ; inReadmeBody {
54
79
body += nextLine + "\n "
55
80
} else {
@@ -66,20 +91,17 @@ func separateFrontmatter(readmeText string) (string, string, error) {
66
91
return fm , strings .TrimSpace (body ), nil
67
92
}
68
93
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.
73
96
func validateReadmeBody (body string ) []error {
74
97
trimmed := strings .TrimSpace (body )
75
98
76
99
if trimmed == "" {
77
100
return []error {errors .New ("README body is empty" )}
78
101
}
79
102
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.
83
105
if ! strings .HasPrefix (trimmed , "# " ) {
84
106
return []error {errors .New ("README body must start with ATX-style h1 header (i.e., \" # \" )" )}
85
107
}
@@ -88,14 +110,14 @@ func validateReadmeBody(body string) []error {
88
110
latestHeaderLevel := 0
89
111
foundFirstH1 := false
90
112
isInCodeBlock := false
113
+ nextLine := ""
91
114
92
115
lineScanner := bufio .NewScanner (strings .NewReader (trimmed ))
93
116
for lineScanner .Scan () {
94
- nextLine : = lineScanner .Text ()
117
+ nextLine = lineScanner .Text ()
95
118
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.
99
121
if strings .HasPrefix (nextLine , "```" ) {
100
122
isInCodeBlock = ! isInCodeBlock
101
123
continue
@@ -109,8 +131,7 @@ func validateReadmeBody(body string) []error {
109
131
continue
110
132
}
111
133
112
- spaceAfterHeader := headerGroups [2 ]
113
- if spaceAfterHeader == "" {
134
+ if spaceAfterHeader := headerGroups [2 ]; spaceAfterHeader == "" {
114
135
errs = append (errs , errors .New ("header does not have space between header characters and main header text" ))
115
136
}
116
137
@@ -121,8 +142,7 @@ func validateReadmeBody(body string) []error {
121
142
continue
122
143
}
123
144
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.
126
146
if nextHeaderLevel == 1 {
127
147
errs = append (errs , errors .New ("READMEs cannot contain more than h1 header" ))
128
148
break
@@ -132,47 +152,16 @@ func validateReadmeBody(body string) []error {
132
152
break
133
153
}
134
154
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
138
157
if nextHeaderLevel > latestHeaderLevel && nextHeaderLevel != (latestHeaderLevel + 1 ) {
139
158
errs = append (errs , fmt .Errorf ("headers are not allowed to increase more than 1 level at a time" ))
140
159
continue
141
160
}
142
161
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.
145
163
latestHeaderLevel = nextHeaderLevel
146
164
}
147
165
148
166
return errs
149
167
}
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