Skip to content

Commit

Permalink
docs: add history preprocessor directive
Browse files Browse the repository at this point in the history
I was reading a post on [architecture decision
records](https://andydote.co.uk/2019/06/29/architecture-decision-records/),
and it pointed out that when processing documentation, one can leverage
the vcs to add in historical information.

This adds a tool that slip-streams the history of a file into the
rendered output.

Signed-off-by: Hank Donnay <hdonnay@redhat.com>
  • Loading branch information
hdonnay committed Nov 11, 2021
1 parent 3399a75 commit 59d83ac
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 0 deletions.
154 changes: 154 additions & 0 deletions Documentation/history.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//go:build ignore
// +build ignore

package main

import (
"encoding/json"
"html/template"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
)

func main() {
log.SetFlags(log.LstdFlags | log.Lmsgprefix)
log.SetPrefix("history: ")

// Handle when called with "supports $renderer".
if len(os.Args) == 3 {
switch os.Args[1] {
case "supports":
switch os.Args[2] {
case "html":
default:
os.Exit(1)
}
default:
os.Exit(1)
}
os.Exit(0)
}

// Actual preprocessing mode.
log.Println("running preprocessor")

in := make([]json.RawMessage, 2)
dec := json.NewDecoder(os.Stdin)
if err := dec.Decode(&in); err != nil {
panic(err)
}
var cfg Config
if err := json.Unmarshal(in[0], &cfg); err != nil {
panic(err)
}
var book Book
if err := json.Unmarshal(in[1], &book); err != nil {
panic(err)
}

var b strings.Builder
for _, s := range book.Sections {
if err := s.Process(&b, &cfg, tmpl); err != nil {
panic(err)
}
}
if err := json.NewEncoder(os.Stdout).Encode(&book); err != nil {
panic(err)
}
}

// in: {"root":"/var/home/hank/work/clair/clair","config":{"book":{"authors":["Clair Authors"],"description":"Documentation for Clair.","language":"en","multilingual":false,"src":"Documentation","title":"Clair Documentation"},"output":{"html":{"git-repository-url":"https://github.com/quay/clair","preferred-dark-theme":"coal"}},"preprocessor":{"history":{"command":"go run Documentation/history.go"}}},"renderer":"html","mdbook_version":"0.4.13"}
type Config struct {
Root string `json:"root"`
Renderer string `json:"renderer"`
Version string `json:"mdbook_version"`
Config struct {
Book BookConfig `json:"book"`
} `json:"config"`
}

type BookConfig struct {
Source string `json:"src"`
}

type Book struct {
Sections []Section `json:"sections"`
X *struct{} `json:"__non_exhaustive"`
}

type Section struct {
Chapter *Chapter `json:",omitempty"`
Separator interface{} `json:",omitempty"`
PartTitle string `json:",omitempty"`
}

func (s *Section) Process(b *strings.Builder, cfg *Config, t *template.Template) error {
if s.Chapter != nil {
return s.Chapter.Process(b, cfg, t)
}
return nil
}

type Chapter struct {
Name string `json:"name"`
Content string `json:"content"`
Number []int `json:"number"`
SubItems []Section `json:"sub_items"`
Path *string `json:"path"`
SourcePath *string `json:"source_path"`
ParentNames []string `json:"parent_names"`
}

func (c *Chapter) Process(b *strings.Builder, cfg *Config, t *template.Template) error {
if marker.MatchString(c.Content) && c.Path != nil {
log.Println("inserting history into:", *c.Path)
cmd := exec.Command(`git`, `log`, `--reverse`, "--format=%cs\t%s", `--`, filepath.Join(cfg.Root, cfg.Config.Book.Source, *c.Path))
out, err := cmd.Output()
if err != nil {
return err
}
var ls []Line
for _, l := range strings.FieldsFunc(string(out), func(r rune) bool { return r == '\n' }) {
s := strings.SplitN(l, "\t", 2)
ls = append(ls, Line{Date: s[0], Summary: s[1]})
}
b.Reset()
if err := t.ExecuteTemplate(b, cfg.Renderer, ls); err != nil {
return err
}
c.Content = marker.ReplaceAllString(c.Content, b.String())
}
for _, s := range c.SubItems {
if err := s.Process(b, cfg, t); err != nil {
return err
}
}
return nil
}

type Line struct {
Date string
Summary string
}

var (
tmpl *template.Template
marker = regexp.MustCompile(`\{\{#\s*history\s*\}\}`)
)

func init() {
const (
html = `<details>
<summary>Document History</summary>
<p><ul>
{{ range . }}<li><strong>{{.Date}}</strong> {{.Summary}}</li>
{{ end }}</ul></p>
</details>`
)
tmpl = template.New("")
template.Must(tmpl.New("html").Parse(html))
}
3 changes: 3 additions & 0 deletions book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ create-missing = true
[output.html]
git-repository-url= "https://github.com/quay/clair"
preferred-dark-theme = "coal"

[preprocessor.history]
command = "go run Documentation/history.go"

0 comments on commit 59d83ac

Please sign in to comment.