kubernetes/cmd/mungedocs/mungedocs.go
Daniel Smith c4aab16b04 Adds a link fixer/checker to mungedocs.
Links that don't work yet can be prefixed with "TODO:" to avoid the check.
2015-07-10 10:54:47 -07:00

132 lines
3.4 KiB
Go

/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
flag "github.com/spf13/pflag"
)
var (
verify = flag.Bool("verify", false, "Exit with status 1 if files would have needed changes but do not change.")
rootDir = flag.String("root-dir", "", "Root directory containing documents to be processed.")
ErrChangesNeeded = errors.New("mungedocs: changes required")
// TODO: allow selection from command line. (e.g., just check links in the examples directory.)
mungesToMake = munges{
munger(updateTOC),
munger(checkLinks),
}
)
// Munger processes a document, returning an updated document xor an error.
// Munger is NOT allowed to mutate 'before', if changes are needed it must copy
// data into a new byte array.
type munger func(filePath string, before []byte) (after []byte, err error)
type munges []munger
type fileProcessor struct {
// Which munge functions should we call?
munges munges
// Are we allowed to make changes?
verifyOnly bool
}
// 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 {
if !strings.HasSuffix(path, ".md") {
return nil
}
fileBytes, err := ioutil.ReadFile(path)
if err != nil {
return err
}
modificationsMade := false
for _, munge := range f.munges {
after, err := munge(path, fileBytes)
if err != nil {
return err
}
if !modificationsMade {
if !bytes.Equal(after, fileBytes) {
modificationsMade = true
if f.verifyOnly {
// We're not allowed to make changes.
return ErrChangesNeeded
}
}
}
fileBytes = after
}
// Write out new file with any changes.
if modificationsMade {
ioutil.WriteFile(path, fileBytes, 0644)
}
return nil
}
func main() {
flag.Parse()
if *rootDir == "" {
fmt.Fprintf(os.Stderr, "usage: %s [--verify] --root-dir <docs root>\n", flag.Arg(0))
os.Exit(1)
}
fp := fileProcessor{
munges: mungesToMake,
verifyOnly: *verify,
}
// For each markdown file under source docs root, process the doc.
// If any error occurs, will exit with failure.
// If verify is true, then status is 0 for no changes needed, 1 for changes needed
// and >1 for an error during processing.
// If verify is false, then status is 0 if changes successfully made or no changes needed,
// 1 if changes were needed but require human intervention, and >1 for an unexpected
// error during processing.
err := filepath.Walk(*rootDir, fp.visit)
if err != nil {
if err == ErrChangesNeeded {
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)
}
}