214 lines
6.5 KiB
Go
214 lines
6.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"github.com/ghodss/yaml"
|
|
)
|
|
|
|
func main() {
|
|
|
|
//get command line arguments without executable
|
|
clArgs := os.Args[1:]
|
|
|
|
//check that an argument has been passed in
|
|
if len(clArgs) == 0 {
|
|
fmt.Fprintf(os.Stderr, "Please specify a config file as a command line argument.\n")
|
|
os.Exit(1)
|
|
}
|
|
configFile := clArgs[0]
|
|
|
|
//get directory of executable
|
|
ex, err := os.Executable()
|
|
checkError(err)
|
|
exPath := filepath.Dir(ex) //file path of updated-imported-docs executable
|
|
suffix := filepath.Base(exPath) //should be "updated-imported-docs"
|
|
|
|
//check if suffix is "updated-imported-docs"
|
|
if suffix != "update-imported-docs" {
|
|
fmt.Fprintf(os.Stderr, "Instead of `go run update-imported-docs.go <config.yml>`, use the compiled binary `./update-imported-docs <config.yml>`\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
//set root directory of website
|
|
websiteRepo := filepath.Clean(strings.TrimSuffix(exPath,suffix)) //path of parent directory
|
|
fmt.Fprintf(os.Stdout, "Website root directory: %s\n", websiteRepo)
|
|
|
|
//read config.yaml file specified by first command line argument
|
|
content, err := ioutil.ReadFile(configFile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error when reading file: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
//convert contents of config.yml file into a map
|
|
var config map[string]interface{}
|
|
err = yaml.Unmarshal(content, &config)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error when unmarshal the config file: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
//change working directory to website root
|
|
err = os.Chdir(websiteRepo)
|
|
checkError(err)
|
|
|
|
//clean out temp directory
|
|
tmpDir := "/tmp/update_docs"
|
|
os.RemoveAll(tmpDir)
|
|
os.Mkdir(tmpDir, 0750)
|
|
|
|
// Match the content between 2 `---`
|
|
// It mostly have something like:
|
|
// ---
|
|
// title: ***
|
|
// notile: ***
|
|
// ---
|
|
titleRegex := regexp.MustCompile("^---\ntitle:(.*\n)*?---\n")
|
|
|
|
// To extract repo path prefix from `remote`
|
|
remoteGitRegex := regexp.MustCompile("(https://.*)\\.git$")
|
|
|
|
//execute for each repo
|
|
repos := config["repos"].([]interface{})
|
|
for _, repo := range repos {
|
|
err = os.Chdir(tmpDir)
|
|
checkError(err)
|
|
|
|
//get config info for repo, clone repo locally
|
|
r := repo.(map[string]interface{})
|
|
repoName := r["name"].(string)
|
|
remotePathMatch := remoteGitRegex.FindAllStringSubmatch(r["remote"].(string), -1)
|
|
if (len(remotePathMatch) == 0) {
|
|
fmt.Fprintf(os.Stderr, "\n\t\t\t!\t!\t!\n\nInvalid remote path %q. Schema should look like: https://<url>.git\n", r["remote"].(string))
|
|
os.Exit(1)
|
|
}
|
|
remotePrefix := fmt.Sprintf("%s/tree/master", remotePathMatch[0][1])
|
|
|
|
cmd := "git"
|
|
args := []string{"clone", "--depth=1", "-b", r["branch"].(string), r["remote"].(string), repoName}
|
|
fmt.Fprintf(os.Stdout, "\n\t\t\t*\t*\t*\n\nCloning repo %q...\n", repoName)
|
|
if err := exec.Command(cmd, args...).Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "\n\t\t\t!\t!\t!\n\nError when cloning repo %q: %v\n", repoName, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
err = os.Chdir(repoName)
|
|
checkError(err)
|
|
|
|
//if generate-command is specified in the repo config,
|
|
//run the command for that repo, e.g. "hack/generate-docs.sh"
|
|
if r["generate-command"] != nil {
|
|
genCmd := r["generate-command"].(string)
|
|
|
|
fmt.Fprintf(os.Stdout, "Generating docs for repo %q with %q...\n\n", repoName, genCmd)
|
|
cmd := exec.Command(genCmd)
|
|
cmdReader, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "\n\t\t\t!\t!\t!\n\nError when generating docs for repo %q: %v\n", repoName, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
//display running output of generate command
|
|
scanner := bufio.NewScanner(cmdReader)
|
|
go func() {
|
|
for scanner.Scan() {
|
|
fmt.Printf("generator output | %s\n", scanner.Text())
|
|
}
|
|
}()
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "Error starting %q command\n", genCmd, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
err = cmd.Wait()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "Error waiting for %q command\n", genCmd, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
}
|
|
|
|
//copy and rename files from src -> dst specified in config
|
|
err = os.Chdir(websiteRepo)
|
|
checkError(err)
|
|
files := r["files"].([]interface{})
|
|
for _, file := range files {
|
|
f := file.(map[string]interface{})
|
|
src := f["src"].(string)
|
|
dst := f["dst"].(string)
|
|
srcDir := filepath.Dir(src)
|
|
absSrc, err := filepath.Abs(path.Join(tmpDir, repoName, src))
|
|
checkError(err)
|
|
absDst, err := filepath.Abs(dst)
|
|
checkError(err)
|
|
// Ignore the error if the old file is not found/
|
|
content, _ := ioutil.ReadFile(absDst)
|
|
titleBlock := titleRegex.Find(content)
|
|
content, err = ioutil.ReadFile(absSrc)
|
|
checkError(err)
|
|
|
|
// Write to new output file
|
|
dstFile, err := os.OpenFile(absDst, os.O_RDWR|os.O_CREATE, 0755)
|
|
checkError(err)
|
|
defer dstFile.Close()
|
|
_, err = dstFile.Write(titleBlock)
|
|
checkError(err)
|
|
|
|
// Process content if necessary
|
|
if r["gen-absolute-links"] != nil {
|
|
content = processLinks(content, remotePrefix, srcDir)
|
|
}
|
|
_, err = dstFile.Write(content)
|
|
checkError(err)
|
|
dstFile.Sync()
|
|
}
|
|
}
|
|
fmt.Fprintf(os.Stdout, "\n\t\t\t*\t*\t*\n\nDocs imported! Run 'git add .' 'git commit -m <comment>' and 'git push' to upload them.\n")
|
|
}
|
|
|
|
//
|
|
func processLinks(content []byte, remotePrefix string, subPath string) []byte {
|
|
// To catch anything of the form [text](url)
|
|
linkRegex := regexp.MustCompile("(\\[.+?\\])\\(([^\\s\\)]+)\\)")
|
|
// Regexes to skip
|
|
absUrlRegex := regexp.MustCompile("https*://")
|
|
mailRegex := regexp.MustCompile("mailto:")
|
|
|
|
processedContent := linkRegex.ReplaceAllFunc(content, func(b []byte) []byte {
|
|
if (absUrlRegex.Match(b) || mailRegex.Match(b)) {
|
|
return b // no processing needed
|
|
}
|
|
match := linkRegex.FindAllStringSubmatch(string(b), -1)
|
|
url := match[0][2]
|
|
if url[0] == '#' { // link on current page
|
|
return b
|
|
} else if url[0] == '/' { // link at root of repo
|
|
return []byte(fmt.Sprintf("%s(%s/%s)", match[0][1], remotePrefix, url[1:]))
|
|
} else { // link relative to current page
|
|
return []byte(fmt.Sprintf("%s(%s/%s/%s)", match[0][1], remotePrefix, subPath, url))
|
|
}
|
|
})
|
|
|
|
h1Regex := regexp.MustCompile("^(# .*)?\n")
|
|
processedContent = h1Regex.ReplaceAll(processedContent, []byte(""))
|
|
|
|
return processedContent
|
|
}
|
|
|
|
func checkError(err error) {
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|