cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

main
012 / scripts / doccheck / redirects.go
redirects.go 3.2K
1package main23import (4 "encoding/json"5 "fmt"6 "os"7 "os/exec"8 "path"9 "slices"10 "strings"11)1213// The docs site keeps its old addresses: a page that moves or goes14// leaves a redirect in website/redirects.json, from its old address to15// where its reader goes now. The site's build checks that each redirect16// leads to a page; checkRedirects checks that none is missing, by17// finding every docs page the site has had since it began (in git's18// history) that isn't a page any more.1920// siteConfig is the file whose first commit began the site.21const siteConfig = "website/docusaurus.config.ts"2223// redirectsFile lists the site's redirects.24const redirectsFile = "website/redirects.json"2526// checkRedirects reports each docs page the site has published that is27// gone without a redirect from its address. Without git or the history28// back to the site's beginning (a shallow clone), there is nothing to29// compare with, and it checks nothing.30func checkRedirects(docs map[string]*doc) []string {31 published, ok := publishedPages()32 if !ok {33 return nil34 }35 b, err := os.ReadFile(redirectsFile)36 if err != nil {37 return []string{redirectsFile + ": " + err.Error()}38 }39 var redirects []struct{ From, To string }40 if err := json.Unmarshal(b, &redirects); err != nil {41 return []string{redirectsFile + ": " + err.Error()}42 }43 from := map[string]bool{}44 for _, r := range redirects {45 from[r.From] = true46 }47 var problems []string48 for _, p := range published {49 if _, still := docs[p]; still {50 continue51 }52 if url := pageURL(p); !from[url] {53 problems = append(problems, fmt.Sprintf("%s: gone, but the site has published it at %s: add a redirect from there in %s", p, url, redirectsFile))54 }55 }56 return problems57}5859// publishedPages lists the docs pages that existed when the site began60// or were added, changed or deleted since, sorted.61func publishedPages() ([]string, bool) {62 adds, err := gitLines("log", "--diff-filter=A", "--format=%H", "--", siteConfig)63 if err != nil || len(adds) == 0 {64 return nil, false65 }66 first := adds[len(adds)-1]67 then, err := gitLines("ls-tree", "-r", "--name-only", first, "--", "docs")68 if err != nil {69 return nil, false70 }71 since, err := gitLines("log", "--no-renames", "--format=", "--name-only", first+"..HEAD", "--", "docs")72 if err != nil {73 return nil, false74 }75 var pages []string76 for _, p := range append(then, since...) {77 if isPage(p) {78 pages = append(pages, p)79 }80 }81 slices.Sort(pages)82 return slices.Compact(pages), true83}8485func gitLines(args ...string) ([]string, error) {86 out, err := exec.Command("git", args...).Output()87 if err != nil {88 return nil, err89 }90 return strings.Fields(string(out)), nil91}9293// isPage reports whether p, a path under docs/, is a page of the site:94// Markdown not excluded by a leading _ (see the docs preset's exclude).95func isPage(p string) bool {96 if path.Ext(p) != ".md" {97 return false98 }99 for _, part := range strings.Split(p, "/") {100 if strings.HasPrefix(part, "_") {101 return false102 }103 }104 return true105}106107// pageURL is where the site serves the page at p: its path without108// .md and with a trailing slash, a folder's README.md at the folder's109// address.110func pageURL(p string) string {111 p = strings.TrimSuffix(p, ".md")112 if base := path.Base(p); base == "README" {113 p = path.Dir(p)114 }115 return "/" + p + "/"116}