diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go index d8f92e298..c8db18afe 100644 --- a/common/hugo/hugo.go +++ b/common/hugo/hugo.go @@ -21,6 +21,7 @@ import ( "runtime/debug" "sort" "strings" + "time" "github.com/gohugoio/hugo/hugofs/files" @@ -57,6 +58,8 @@ type Info struct { // This can also be set by the user. // It can be any string, but it will be all lower case. Environment string + + deps []*Dependency } // Version returns the current version as a comparable version string. @@ -77,8 +80,13 @@ func (i Info) IsExtended() bool { return IsExtended } +// Deps gets a list of dependencies for this Hugo build. +func (i Info) Deps() []*Dependency { + return i.deps +} + // NewInfo creates a new Hugo Info object. -func NewInfo(environment string) Info { +func NewInfo(environment string, deps []*Dependency) Info { if environment == "" { environment = EnvironmentProduction } @@ -86,6 +94,7 @@ func NewInfo(environment string) Info { CommitHash: commitHash, BuildDate: buildDate, Environment: environment, + deps: deps, } } @@ -156,3 +165,27 @@ func IsRunningAsTest() bool { } return false } + +// Dependency is a single dependency, which can be either a Hugo Module or a local theme. +type Dependency struct { + // Returns the path to this module. + // This will either be the module path, e.g. "github.com/gohugoio/myshortcodes", + // or the path below your /theme folder, e.g. "mytheme". + Path string + + // The module version. + Version string + + // Whether this dependency is vendored. + Vendor bool + + // Time version was created. + Time time.Time + + // In the dependency tree, this is the first module that defines this module + // as a dependency. + Owner *Dependency + + // Replaced by this dependency. + Replace *Dependency +} diff --git a/common/hugo/hugo_test.go b/common/hugo/hugo_test.go index 0631be62c..ff36cab7c 100644 --- a/common/hugo/hugo_test.go +++ b/common/hugo/hugo_test.go @@ -23,7 +23,7 @@ import ( func TestHugoInfo(t *testing.T) { c := qt.New(t) - hugoInfo := NewInfo("") + hugoInfo := NewInfo("", nil) c.Assert(hugoInfo.Version(), qt.Equals, CurrentVersion.Version()) c.Assert(fmt.Sprintf("%T", VersionString("")), qt.Equals, fmt.Sprintf("%T", hugoInfo.Version())) @@ -34,6 +34,6 @@ func TestHugoInfo(t *testing.T) { c.Assert(hugoInfo.IsProduction(), qt.Equals, true) c.Assert(hugoInfo.IsExtended(), qt.Equals, IsExtended) - devHugoInfo := NewInfo("development") + devHugoInfo := NewInfo("development", nil) c.Assert(devHugoInfo.IsProduction(), qt.Equals, false) } diff --git a/docs/content/en/functions/hugo.md b/docs/content/en/functions/hugo.md index 6cbb36019..fb20d2717 100644 --- a/docs/content/en/functions/hugo.md +++ b/docs/content/en/functions/hugo.md @@ -49,3 +49,67 @@ hugo.IsProduction {{% note "Use the Hugo Generator Tag" %}} We highly recommend using `hugo.Generator` in your website's ``. `hugo.Generator` is included by default in all themes hosted on [themes.gohugo.io](https://themes.gohugo.io). The generator tag allows the Hugo team to track the usage and popularity of Hugo. {{% /note %}} + +hugo.Deps +: See [hugo.Deps](#hugodeps) + + +## hugo.Deps + +{{< new-in "0.92.0" >}} + +`hugo.Deps` returns a list of dependencies for a project (either Hugo Modules or local theme components). + +Eeach dependency contains: + +Path (string) +: Returns the path to this module. This will either be the module path, e.g. "github.com/gohugoio/myshortcodes", or the path below your /theme folder, e.g. "mytheme". + +Version (string) +: The module version. + +Vendor (bool) +: Whether this dependency is vendored. + +Time (time.Time) +: Time version was created. + +Owner +: In the dependency tree, this is the first module that defines this module as a dependency. + +Replace (*Dependency) +: Replaced by this dependency. + +An example table listing the dependencies: + +```html +

Dependencies

+ + + + + + + + + + + + {{ range $index, $element := hugo.Deps }} + + + + + + + + + {{ end }} + +
#PathVersionTimeVendor
{{ add $index 1 }}{{ with $element.Owner }}{{.Path }}{{ else }}PROJECT{{ end }} + {{ $element.Path }} + {{ with $element.Replace}} + => {{ .Path }} + {{ end }} + {{ $element.Version }}{{ with $element.Time }}{{ . }}{{ end }}{{ $element.Vendor }}
+``` \ No newline at end of file diff --git a/hugolib/hugo_smoke_test.go b/hugolib/hugo_smoke_test.go index 798504f0d..46aecf9cc 100644 --- a/hugolib/hugo_smoke_test.go +++ b/hugolib/hugo_smoke_test.go @@ -162,7 +162,7 @@ Some **Markdown** in JSON shortcode. b.WithContent("blog/mybundle/mydata.csv", "Bundled CSV") const ( - commonPageTemplate = `|{{ .Kind }}|{{ .Title }}|{{ .Path }}|{{ .Summary }}|{{ .Content }}|RelPermalink: {{ .RelPermalink }}|WordCount: {{ .WordCount }}|Pages: {{ .Pages }}|Data Pages: Pages({{ len .Data.Pages }})|Resources: {{ len .Resources }}|Summary: {{ .Summary }}` + commonPageTemplate = `|{{ .Kind }}|{{ .Title }}|{{ .File.Path }}|{{ .Summary }}|{{ .Content }}|RelPermalink: {{ .RelPermalink }}|WordCount: {{ .WordCount }}|Pages: {{ .Pages }}|Data Pages: Pages({{ len .Data.Pages }})|Resources: {{ len .Resources }}|Summary: {{ .Summary }}` commonPaginatorTemplate = `|Paginator: {{ with .Paginator }}{{ .PageNumber }}{{ else }}NIL{{ end }}` commonListTemplateNoPaginator = `|{{ $pages := .Pages }}{{ if .IsHome }}{{ $pages = .Site.RegularPages }}{{ end }}{{ range $i, $e := ($pages | first 1) }}|Render {{ $i }}: {{ .Kind }}|{{ .Render "li" }}|{{ end }}|Site params: {{ $.Site.Params.hugo }}|RelPermalink: {{ .RelPermalink }}` commonListTemplate = commonPaginatorTemplate + `|{{ $pages := .Pages }}{{ if .IsHome }}{{ $pages = .Site.RegularPages }}{{ end }}{{ range $i, $e := ($pages | first 1) }}|Render {{ $i }}: {{ .Kind }}|{{ .Render "li" }}|{{ end }}|Site params: {{ $.Site.Params.hugo }}|RelPermalink: {{ .RelPermalink }}` diff --git a/hugolib/site.go b/hugolib/site.go index 624630d80..13d5482b1 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -31,6 +31,7 @@ import ( "time" "github.com/gohugoio/hugo/common/types" + "github.com/gohugoio/hugo/modules" "golang.org/x/text/unicode/norm" "github.com/gohugoio/hugo/common/paths" @@ -1333,6 +1334,32 @@ func (s *Site) initializeSiteInfo() error { } } + // Assemble dependencies to be used in hugo.Deps. + // TODO(bep) another reminder: We need to clean up this Site vs HugoSites construct. + var deps []*hugo.Dependency + var depFromMod func(m modules.Module) *hugo.Dependency + depFromMod = func(m modules.Module) *hugo.Dependency { + dep := &hugo.Dependency{ + Path: m.Path(), + Version: m.Version(), + Time: m.Time(), + Vendor: m.Vendor(), + } + + // These are pointers, but this all came from JSON so there's no recursive navigation, + // so just create new values. + if m.Replace() != nil { + dep.Replace = depFromMod(m.Replace()) + } + if m.Owner() != nil { + dep.Owner = depFromMod(m.Owner()) + } + return dep + } + for _, m := range s.Paths.AllModules { + deps = append(deps, depFromMod(m)) + } + s.Info = &SiteInfo{ title: lang.GetString("title"), Author: lang.GetStringMap("author"), @@ -1351,7 +1378,7 @@ func (s *Site) initializeSiteInfo() error { permalinks: permalinks, owner: s.h, s: s, - hugoInfo: hugo.NewInfo(s.Cfg.GetString("environment")), + hugoInfo: hugo.NewInfo(s.Cfg.GetString("environment"), deps), } rssOutputFormat, found := s.outputFormats[page.KindHome].GetByName(output.RSSFormat.Name) diff --git a/resources/page/site.go b/resources/page/site.go index 31058637b..9728df691 100644 --- a/resources/page/site.go +++ b/resources/page/site.go @@ -120,7 +120,7 @@ func (t testSite) Data() map[string]interface{} { // NewDummyHugoSite creates a new minimal test site. func NewDummyHugoSite(cfg config.Provider) Site { return testSite{ - h: hugo.NewInfo(hugo.EnvironmentProduction), + h: hugo.NewInfo(hugo.EnvironmentProduction, nil), l: langs.NewLanguage("en", cfg), } }