authelia/cmd/authelia-gen/cmd_docs_date.go

233 lines
4.2 KiB
Go
Raw Permalink Normal View History

package main
import (
"bufio"
"bytes"
"fmt"
"io/fs"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/valyala/fasthttp"
yaml "gopkg.in/yaml.v3"
)
func newDocsDateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: cmdUseDocsDate,
Short: "Generate doc dates",
RunE: docsDateRunE,
DisableAutoGenTag: true,
}
cmd.Flags().String("commit-until", fasthttp.MethodHead, "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 (
pathDocsContent, cwd, commitUtil, commitSince, commitFilter string
)
if pathDocsContent, err = getPFlagPath(cmd.Flags(), cmdFlagRoot, cmdFlagDocs, cmdFlagDocsContent); err != nil {
return err
}
if cwd, err = cmd.Flags().GetString(cmdFlagCwd); 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(pathDocsContent, 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]any{}
if err = yaml.Unmarshal(frontmatterBytes, frontmatter); err != nil {
return err
}
var (
date time.Time
)
if value, ok := frontmatter["date"]; ok {
date, ok = value.(time.Time)
if !ok {
var abspath string
if abspath, err = filepath.Abs(path); err != nil {
abspath = path
}
return fmt.Errorf("frontmatter for %s has an invalid date value: is %T with a value of %s", abspath, value, value)
}
}
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() == delimiterLineFrontMatter:
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() == delimiterLineFrontMatter {
break
}
buf.Write(scanner.Bytes())
buf.Write(newline)
} else if scanner.Text() == delimiterLineFrontMatter {
start = true
}
}
return buf.Bytes()
}