221 lines
3.9 KiB
Go
221 lines
3.9 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"io/fs"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/spf13/cobra"
|
||
|
"gopkg.in/yaml.v3"
|
||
|
)
|
||
|
|
||
|
func newDocsDateCmd() *cobra.Command {
|
||
|
cmd := &cobra.Command{
|
||
|
Use: "date",
|
||
|
Short: "Generate doc dates",
|
||
|
RunE: docsDateRunE,
|
||
|
}
|
||
|
|
||
|
cmd.Flags().StringP("directory", "d", "./docs/content", "The directory to modify")
|
||
|
cmd.Flags().String("commit-until", "HEAD", "The commit to check the logs until")
|
||
|
cmd.Flags().String("commit-since", "", "The commit to check the logs since")
|
||
|
|
||
|
return cmd
|
||
|
}
|
||
|
|
||
|
func docsDateRunE(cmd *cobra.Command, args []string) (err error) {
|
||
|
var (
|
||
|
dir, cwd, commitUtil, commitSince, commitFilter string
|
||
|
)
|
||
|
|
||
|
if dir, err = cmd.Flags().GetString("directory"); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if cwd, err = cmd.Flags().GetString("cwd"); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if cmd.Flags().Changed("commit-since") {
|
||
|
if commitUtil, err = cmd.Flags().GetString("commit-util"); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if commitSince, err = cmd.Flags().GetString("commit-since"); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
commitFilter = fmt.Sprintf("%s...%s", commitUtil, commitSince)
|
||
|
}
|
||
|
|
||
|
return filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if !strings.HasSuffix(info.Name(), ".md") {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
abs, err := filepath.Abs(path)
|
||
|
if err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
frontmatterBytes := getFrontmatter(abs)
|
||
|
|
||
|
if frontmatterBytes == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
frontmatter := map[string]interface{}{}
|
||
|
|
||
|
if err = yaml.Unmarshal(frontmatterBytes, frontmatter); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
date time.Time
|
||
|
)
|
||
|
|
||
|
if value, ok := frontmatter["date"]; ok {
|
||
|
date = value.(time.Time)
|
||
|
}
|
||
|
|
||
|
dateGit := getDateFromGit(cwd, abs, commitFilter)
|
||
|
|
||
|
replaceDates(abs, date, dateGit)
|
||
|
|
||
|
return nil
|
||
|
})
|
||
|
}
|
||
|
|
||
|
var newline = []byte("\n")
|
||
|
|
||
|
func getDateFromGit(cwd, path, commitFilter string) *time.Time {
|
||
|
var args []string
|
||
|
|
||
|
if len(cwd) != 0 {
|
||
|
args = append(args, "-C", cwd)
|
||
|
}
|
||
|
|
||
|
args = append(args, "log")
|
||
|
|
||
|
if len(commitFilter) != 0 {
|
||
|
args = append(args, commitFilter)
|
||
|
}
|
||
|
|
||
|
args = append(args, "-1", "--follow", "--diff-filter=A", "--pretty=format:%cD", "--", path)
|
||
|
|
||
|
return getTimeFromGitCmd(exec.Command("git", args...))
|
||
|
}
|
||
|
|
||
|
func getTimeFromGitCmd(cmd *exec.Cmd) *time.Time {
|
||
|
var (
|
||
|
output []byte
|
||
|
err error
|
||
|
t time.Time
|
||
|
)
|
||
|
|
||
|
if output, err = cmd.Output(); err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if t, err = time.Parse(dateFmtRFC2822, string(output)); err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return &t
|
||
|
}
|
||
|
|
||
|
func replaceDates(path string, date time.Time, dateGit *time.Time) {
|
||
|
f, err := os.Open(path)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
buf := bytes.Buffer{}
|
||
|
|
||
|
scanner := bufio.NewScanner(f)
|
||
|
|
||
|
var dateGitLine string
|
||
|
|
||
|
dateLine := fmt.Sprintf("date: %s", date.Format(dateFmtYAML))
|
||
|
|
||
|
if dateGit != nil {
|
||
|
dateGitLine = fmt.Sprintf("date: %s", dateGit.Format(dateFmtYAML))
|
||
|
} else {
|
||
|
dateGitLine = dateLine
|
||
|
}
|
||
|
|
||
|
found := 0
|
||
|
|
||
|
frontmatter := 0
|
||
|
|
||
|
for scanner.Scan() {
|
||
|
if found < 2 && frontmatter < 2 {
|
||
|
switch {
|
||
|
case scanner.Text() == frontmatterDelimiterLine:
|
||
|
buf.Write(scanner.Bytes())
|
||
|
frontmatter++
|
||
|
case frontmatter != 0 && strings.HasPrefix(scanner.Text(), "date: "):
|
||
|
buf.WriteString(dateGitLine)
|
||
|
found++
|
||
|
default:
|
||
|
buf.Write(scanner.Bytes())
|
||
|
}
|
||
|
} else {
|
||
|
buf.Write(scanner.Bytes())
|
||
|
}
|
||
|
|
||
|
buf.Write(newline)
|
||
|
}
|
||
|
|
||
|
f.Close()
|
||
|
|
||
|
newF, err := os.Create(path)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
_, _ = buf.WriteTo(newF)
|
||
|
|
||
|
newF.Close()
|
||
|
}
|
||
|
|
||
|
func getFrontmatter(path string) []byte {
|
||
|
f, err := os.Open(path)
|
||
|
if err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
defer f.Close()
|
||
|
|
||
|
scanner := bufio.NewScanner(f)
|
||
|
|
||
|
var start bool
|
||
|
|
||
|
buf := bytes.Buffer{}
|
||
|
|
||
|
for scanner.Scan() {
|
||
|
if start {
|
||
|
if scanner.Text() == frontmatterDelimiterLine {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
buf.Write(scanner.Bytes())
|
||
|
buf.Write(newline)
|
||
|
} else if scanner.Text() == frontmatterDelimiterLine {
|
||
|
start = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return buf.Bytes()
|
||
|
}
|