Skip to content

Commit

Permalink
Refactor the GitInfo into the date handlers
Browse files Browse the repository at this point in the history
Fixes #4495
  • Loading branch information
bep committed Mar 11, 2018
1 parent 95d6200 commit ce6e431
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 89 deletions.
46 changes: 18 additions & 28 deletions hugolib/gitinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,51 +19,41 @@ import (
"strings"

"github.com/bep/gitmap"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
)

func (h *HugoSites) assembleGitInfo() {
if !h.Cfg.GetBool("enableGitInfo") {
return
type gitInfo struct {
contentDir string
repo *gitmap.GitRepo
}

func (g *gitInfo) forPage(p *Page) (*gitmap.GitInfo, bool) {
if g == nil {
return nil, false
}
name := path.Join(g.contentDir, filepath.ToSlash(p.Path()))
return g.repo.Files[name], true
}

func newGitInfo(cfg config.Provider) (*gitInfo, error) {
var (
workingDir = h.Cfg.GetString("workingDir")
contentDir = h.Cfg.GetString("contentDir")
workingDir = cfg.GetString("workingDir")
contentDir = cfg.GetString("contentDir")
)

gitRepo, err := gitmap.Map(workingDir, "")
if err != nil {
h.Log.ERROR.Printf("Got error reading Git log: %s", err)
return
return nil, err
}

gitMap := gitRepo.Files
repoPath := filepath.FromSlash(gitRepo.TopLevelAbsPath)

// The Hugo site may be placed in a sub folder in the Git repo,
// one example being the Hugo docs.
// We have to find the root folder to the Hugo site below the Git root.
contentRoot := strings.TrimPrefix(workingDir, repoPath)
contentRoot = strings.TrimPrefix(contentRoot, helpers.FilePathSeparator)
contentDir = path.Join(filepath.ToSlash(contentRoot), contentDir)

s := h.Sites[0]

for _, p := range s.AllPages {
if p.Path() == "" {
// Home page etc. with no content file.
continue
}
// Git normalizes file paths on this form:
filename := path.Join(filepath.ToSlash(contentRoot), contentDir, filepath.ToSlash(p.Path()))
g, ok := gitMap[filename]
if !ok {
h.Log.WARN.Printf("Failed to find GitInfo for %q", filename)
continue
}

p.GitInfo = g
p.Lastmod = p.GitInfo.AuthorDate
}

return &gitInfo{contentDir: contentDir, repo: gitRepo}, nil
}
19 changes: 19 additions & 0 deletions hugolib/hugo_sites.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ type HugoSites struct {

// Keeps track of bundle directories and symlinks to enable partial rebuilding.
ContentChanges *contentChangeMap

// If enabled, keeps a revision map for all content.
gitInfo *gitInfo
}

func (h *HugoSites) IsMultihost() bool {
Expand Down Expand Up @@ -146,9 +149,25 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {

h.Deps = sites[0].Deps

if err := h.initGitInfo(); err != nil {
return nil, err
}

return h, nil
}

func (h *HugoSites) initGitInfo() error {
if h.Cfg.GetBool("enableGitInfo") {
gi, err := newGitInfo(h.Cfg)
if err != nil {
h.Log.ERROR.Println("Failed to read Git log:", err)
} else {
h.gitInfo = gi
}
}
return nil
}

func applyDepsIfNeeded(cfg deps.DepsCfg, sites ...*Site) error {
if cfg.TemplateProvider == nil {
cfg.TemplateProvider = tplimpl.DefaultTemplateProvider
Expand Down
2 changes: 0 additions & 2 deletions hugolib/hugo_sites_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,6 @@ func (h *HugoSites) assemble(config *BuildCfg) error {
}

if config.whatChanged.source {
h.assembleGitInfo()

for _, s := range h.Sites {
if err := s.buildSiteMeta(); err != nil {
return err
Expand Down
28 changes: 22 additions & 6 deletions hugolib/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -1119,13 +1119,20 @@ func (p *Page) update(frontmatter map[string]interface{}) error {
mtime = p.Source.FileInfo().ModTime()
}

var gitAuthorDate time.Time
if p.GitInfo != nil {
gitAuthorDate = p.GitInfo.AuthorDate
}

descriptor := &pagemeta.FrontMatterDescriptor{
Frontmatter: frontmatter,
Params: p.params,
Dates: &p.PageDates,
PageURLs: &p.URLPath,
BaseFilename: p.BaseFileName(),
ModTime: mtime}
Frontmatter: frontmatter,
Params: p.params,
Dates: &p.PageDates,
PageURLs: &p.URLPath,
BaseFilename: p.BaseFileName(),
ModTime: mtime,
GitAuthorDate: gitAuthorDate,
}

// Handle the date separately
// TODO(bep) we need to "do more" in this area so this can be split up and
Expand Down Expand Up @@ -1579,6 +1586,15 @@ func (p *Page) parse(reader io.Reader) error {
meta = map[string]interface{}{}
}

if p.s != nil && p.s.owner != nil {
gi, enabled := p.s.owner.gitInfo.forPage(p)
if gi != nil {
p.GitInfo = gi
} else if enabled {
p.s.Log.WARN.Printf("Failed to find GitInfo for page %q", p.Path())
}
}

return p.update(meta)
}

Expand Down
32 changes: 32 additions & 0 deletions hugolib/page_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ import (
"fmt"
"html/template"
"os"

"path/filepath"
"reflect"
"sort"
"strings"
"testing"
"time"

"github.com/gohugoio/hugo/hugofs"
"github.com/spf13/afero"

"github.com/spf13/viper"

"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers"
"github.com/spf13/cast"
Expand Down Expand Up @@ -904,6 +910,32 @@ func TestPageWithDate(t *testing.T) {
checkPageDate(t, p, d)
}

func TestPageWithLastmodFromGitInfo(t *testing.T) {
assrt := require.New(t)

// We need to use the OS fs for this.
cfg := viper.New()
fs := hugofs.NewFrom(hugofs.Os, cfg)
fs.Destination = &afero.MemMapFs{}

cfg.Set("frontmatter", map[string]interface{}{
"lastmod": []string{":git", "lastmod"},
})

cfg.Set("enableGitInfo", true)

assrt.NoError(loadDefaultSettingsFor(cfg))

wd, err := os.Getwd()
assrt.NoError(err)
cfg.Set("workingDir", filepath.Join(wd, "testsite"))

s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})

assrt.Len(s.RegularPages, 1)
assrt.Equal("2018-02-28", s.RegularPages[0].Lastmod.Format("2006-01-02"))
}

func TestPageWithFrontMatterConfig(t *testing.T) {
t.Parallel()

Expand Down
20 changes: 19 additions & 1 deletion hugolib/pagemeta/page_frontmatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ type FrontMatterDescriptor struct {
// The content file's mod time.
ModTime time.Time

// May be set from the author date in Git.
GitAuthorDate time.Time

// The below are pointers to values on Page and will be modified.

// This is the Page's params.
Expand Down Expand Up @@ -175,13 +178,16 @@ const (

// Gets date from file OS mod time.
fmModTime = ":filemodtime"

// Gets date from Git
fmGitAuthorDate = ":git"
)

// This is the config you get when doing nothing.
func newDefaultFrontmatterConfig() frontmatterConfig {
return frontmatterConfig{
date: []string{fmDate, fmPubDate, fmLastmod},
lastmod: []string{fmLastmod, fmDate, fmPubDate},
lastmod: []string{fmGitAuthorDate, fmLastmod, fmDate, fmPubDate},
publishDate: []string{fmPubDate, fmDate},
expiryDate: []string{fmExpiryDate},
}
Expand Down Expand Up @@ -348,6 +354,8 @@ func (f FrontMatterHandler) createDateHandler(identifiers []string, setter func(
handlers = append(handlers, h.newDateFilenameHandler(setter))
case fmModTime:
handlers = append(handlers, h.newDateModTimeHandler(setter))
case fmGitAuthorDate:
handlers = append(handlers, h.newDateGitAuthorDateHandler(setter))
default:
handlers = append(handlers, h.newDateFieldHandler(identifier, setter))
}
Expand Down Expand Up @@ -410,3 +418,13 @@ func (f *frontmatterFieldHandlers) newDateModTimeHandler(setter func(d *FrontMat
return true, nil
}
}

func (f *frontmatterFieldHandlers) newDateGitAuthorDateHandler(setter func(d *FrontMatterDescriptor, t time.Time)) frontMatterFieldHandler {
return func(d *FrontMatterDescriptor) (bool, error) {
if d.GitAuthorDate.IsZero() {
return false, nil
}
setter(d, d.GitAuthorDate)
return true, nil
}
}
86 changes: 34 additions & 52 deletions hugolib/pagemeta/page_frontmatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package pagemeta

import (
"fmt"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -94,7 +95,7 @@ func TestFrontMatterNewConfig(t *testing.T) {
fc, err = newFrontmatterConfig(cfg)
assert.NoError(err)
assert.Equal([]string{"date", "publishdate", "pubdate", "published", "lastmod", "modified"}, fc.date)
assert.Equal([]string{"lastmod", "modified", "date", "publishdate", "pubdate", "published"}, fc.lastmod)
assert.Equal([]string{":git", "lastmod", "modified", "date", "publishdate", "pubdate", "published"}, fc.lastmod)
assert.Equal([]string{"expirydate", "unpublishdate"}, fc.expiryDate)
assert.Equal([]string{"publishdate", "pubdate", "published", "date"}, fc.publishDate)

Expand All @@ -108,69 +109,50 @@ func TestFrontMatterNewConfig(t *testing.T) {
fc, err = newFrontmatterConfig(cfg)
assert.NoError(err)
assert.Equal([]string{"d1", "date", "publishdate", "pubdate", "published", "lastmod", "modified"}, fc.date)
assert.Equal([]string{"d2", "lastmod", "modified", "date", "publishdate", "pubdate", "published"}, fc.lastmod)
assert.Equal([]string{"d2", ":git", "lastmod", "modified", "date", "publishdate", "pubdate", "published"}, fc.lastmod)
assert.Equal([]string{"d3", "expirydate", "unpublishdate"}, fc.expiryDate)
assert.Equal([]string{"d4", "publishdate", "pubdate", "published", "date"}, fc.publishDate)

}

func TestFrontMatterDatesFilenameModTime(t *testing.T) {
func TestFrontMatterDatesHandlers(t *testing.T) {
assert := require.New(t)

cfg := viper.New()

cfg.Set("frontmatter", map[string]interface{}{
"date": []string{":fileModTime", "date"},
})

handler, err := NewFrontmatterHandler(nil, cfg)
assert.NoError(err)

d1, _ := time.Parse("2006-01-02", "2018-02-01")
d2, _ := time.Parse("2006-01-02", "2018-02-02")

d := newTestFd()
d.ModTime = d1
d.Frontmatter["date"] = d2
assert.NoError(handler.HandleDates(d))
assert.Equal(d1, d.Dates.Date)
assert.Equal(d2, d.Params["date"])

d = newTestFd()
d.Frontmatter["date"] = d2
assert.NoError(handler.HandleDates(d))
assert.Equal(d2, d.Dates.Date)
assert.Equal(d2, d.Params["date"])

}

func TestFrontMatterDatesFilename(t *testing.T) {
assert := require.New(t)
for _, handlerID := range []string{":filename", ":fileModTime", ":git"} {

cfg := viper.New()

cfg.Set("frontmatter", map[string]interface{}{
"date": []string{":filename", "date"},
})
cfg := viper.New()

handler, err := NewFrontmatterHandler(nil, cfg)
assert.NoError(err)
cfg.Set("frontmatter", map[string]interface{}{
"date": []string{handlerID, "date"},
})

d1, _ := time.Parse("2006-01-02", "2018-02-01")
d2, _ := time.Parse("2006-01-02", "2018-02-02")
handler, err := NewFrontmatterHandler(nil, cfg)
assert.NoError(err)

d := newTestFd()
d.BaseFilename = "2018-02-01-page.md"
d.Frontmatter["date"] = d2
assert.NoError(handler.HandleDates(d))
assert.Equal(d1, d.Dates.Date)
assert.Equal(d2, d.Params["date"])
d1, _ := time.Parse("2006-01-02", "2018-02-01")
d2, _ := time.Parse("2006-01-02", "2018-02-02")

d := newTestFd()
switch strings.ToLower(handlerID) {
case ":filename":
d.BaseFilename = "2018-02-01-page.md"
case ":filemodtime":
d.ModTime = d1
case ":git":
d.GitAuthorDate = d1
}
d.Frontmatter["date"] = d2
assert.NoError(handler.HandleDates(d))
assert.Equal(d1, d.Dates.Date)
assert.Equal(d2, d.Params["date"])

d = newTestFd()
d.Frontmatter["date"] = d2
assert.NoError(handler.HandleDates(d))
assert.Equal(d2, d.Dates.Date)
assert.Equal(d2, d.Params["date"])

d = newTestFd()
d.Frontmatter["date"] = d2
assert.NoError(handler.HandleDates(d))
assert.Equal(d2, d.Dates.Date)
assert.Equal(d2, d.Params["date"])
}
}

func TestFrontMatterDatesCustomConfig(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions hugolib/testsite/content/first-post.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: "My First Post"
lastmod: 2018-02-28
---

0 comments on commit ce6e431

Please sign in to comment.