Run all munges on all docs

Rather than terminating, Collect the errors and print them per-file-per-munge.
This commit is contained in:
Tim Hockin 2015-07-10 14:35:47 -07:00
parent 3f2ac7864b
commit 698b212491
2 changed files with 64 additions and 43 deletions

View File

@ -58,7 +58,7 @@ func checkLinks(filePath string, fileBytes []byte) ([]byte, error) {
if err != nil { if err != nil {
errors = append( errors = append(
errors, errors,
fmt.Sprintf("%v, link %q is unparsable: %v", filePath, linkText, err), fmt.Sprintf("link %q is unparsable: %v", linkText, err),
) )
return in return in
} }
@ -74,7 +74,7 @@ func checkLinks(filePath string, fileBytes []byte) ([]byte, error) {
if !targetExists { if !targetExists {
errors = append( errors = append(
errors, errors,
fmt.Sprintf("%v, %q: target not found\n", filePath, linkText), fmt.Sprintf("%q: target not found", linkText),
) )
} }
u.Path = newPath u.Path = newPath

View File

@ -34,30 +34,32 @@ var (
ErrChangesNeeded = errors.New("mungedocs: changes required") ErrChangesNeeded = errors.New("mungedocs: changes required")
// All of the munge operations to perform.
// TODO: allow selection from command line. (e.g., just check links in the examples directory.) // TODO: allow selection from command line. (e.g., just check links in the examples directory.)
mungesToMake = munges{ allMunges = []munge{
munger(updateTOC), {"table-of-contents", updateTOC},
munger(checkLinks), {"check-links", checkLinks},
} }
) )
// Munger processes a document, returning an updated document xor an error. // a munge processes a document, returning an updated document xor an error.
// Munger is NOT allowed to mutate 'before', if changes are needed it must copy // The fn is NOT allowed to mutate 'before', if changes are needed it must copy
// data into a new byte array. // data into a new byte array and return that.
type munger func(filePath string, before []byte) (after []byte, err error) type munge struct {
name string
type munges []munger fn func(filePath string, before []byte) (after []byte, err error)
}
type fileProcessor struct { type fileProcessor struct {
// Which munge functions should we call? // Which munge functions should we call?
munges munges munges []munge
// Are we allowed to make changes? // Are we allowed to make changes?
verifyOnly bool verifyOnly bool
} }
// Either change a file or verify that it needs no changes (according to modify argument) // Either change a file or verify that it needs no changes (according to modify argument)
func (f fileProcessor) visit(path string, i os.FileInfo, e error) error { func (f fileProcessor) visit(path string) error {
if !strings.HasSuffix(path, ".md") { if !strings.HasSuffix(path, ".md") {
return nil return nil
} }
@ -68,31 +70,55 @@ func (f fileProcessor) visit(path string, i os.FileInfo, e error) error {
} }
modificationsMade := false modificationsMade := false
errFound := false
filePrinted := false
for _, munge := range f.munges { for _, munge := range f.munges {
after, err := munge(path, fileBytes) after, err := munge.fn(path, fileBytes)
if err != nil || !bytes.Equal(after, fileBytes) {
if !filePrinted {
fmt.Printf("%s\n----\n", path)
filePrinted = true
}
fmt.Printf("%s:\n", munge.name)
if err != nil { if err != nil {
return err fmt.Println(err)
} errFound = true
if !modificationsMade { } else {
if !bytes.Equal(after, fileBytes) { fmt.Println("contents were modified")
modificationsMade = true modificationsMade = true
if f.verifyOnly {
// We're not allowed to make changes.
return ErrChangesNeeded
}
} }
fmt.Println("")
} }
fileBytes = after fileBytes = after
} }
// Write out new file with any changes. // Write out new file with any changes.
if modificationsMade { if modificationsMade {
if f.verifyOnly {
// We're not allowed to make changes.
return ErrChangesNeeded
}
ioutil.WriteFile(path, fileBytes, 0644) ioutil.WriteFile(path, fileBytes, 0644)
} }
if errFound {
return ErrChangesNeeded
}
return nil return nil
} }
func newWalkFunc(fp *fileProcessor, changesNeeded *bool) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if err := fp.visit(path); err != nil {
*changesNeeded = true
if err != ErrChangesNeeded {
return err
}
}
return nil
}
}
func main() { func main() {
flag.Parse() flag.Parse()
@ -102,30 +128,25 @@ func main() {
} }
fp := fileProcessor{ fp := fileProcessor{
munges: mungesToMake, munges: allMunges,
verifyOnly: *verify, verifyOnly: *verify,
} }
// For each markdown file under source docs root, process the doc. // For each markdown file under source docs root, process the doc.
// If any error occurs, will exit with failure. // - If any error occurs: exit with failure (exit >1).
// If verify is true, then status is 0 for no changes needed, 1 for changes needed // - If verify is true: exit 0 if no changes needed, exit 1 if changes
// and >1 for an error during processing. // needed.
// If verify is false, then status is 0 if changes successfully made or no changes needed, // - If verify is false: exit 0 if changes successfully made or no
// 1 if changes were needed but require human intervention, and >1 for an unexpected // changes needed.
// error during processing. var changesNeeded bool
err := filepath.Walk(*rootDir, fp.visit)
err := filepath.Walk(*rootDir, newWalkFunc(&fp, &changesNeeded))
if err != nil { if err != nil {
if err == ErrChangesNeeded { fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
if *verify {
fmt.Fprintf(os.Stderr,
"Some changes needed but not made due to --verify=true\n")
} else {
fmt.Fprintf(os.Stderr,
"Some changes needed but human intervention is required\n")
}
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "filepath.Walk() returned %v\n", err)
os.Exit(2) os.Exit(2)
} }
if changesNeeded && *verify {
fmt.Fprintf(os.Stderr, "FAIL: changes needed but not made due to --verify\n")
os.Exit(1)
}
} }