shithub: hugo

Download patch

ref: 831d23cb4d1ca99cdc15ed31c8ee1f981497be8f
parent: 514e18dc27ce37a0e9a231741d616cf29d50d610
author: Bjørn Erik Pedersen <[email protected]>
date: Mon Nov 26 05:11:22 EST 2018

Add tpl/site and tpl/hugo

This means that the current `.Site` and ´.Hugo` is available as a globals, so you can do `site.IsServer`, `hugo.Version` etc.

Fixes #5470
Fixes #5467
Fixes #5503

--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-hugo
+/hugo
 docs/public*
 /.idea
 hugo.exe
--- a/commands/commandeer.go
+++ b/commands/commandeer.go
@@ -17,10 +17,11 @@
 	"bytes"
 	"errors"
 
-	"github.com/gohugoio/hugo/common/herrors"
-
 	"io/ioutil"
 
+	"github.com/gohugoio/hugo/common/herrors"
+	"github.com/gohugoio/hugo/common/hugo"
+
 	jww "github.com/spf13/jwalterweatherman"
 
 	"os"
@@ -105,7 +106,7 @@
 	m := make(map[string]interface{})
 
 	m["Error"] = errors.New(removeErrorPrefixFromLog(c.logger.Errors()))
-	m["Version"] = hugoVersionString()
+	m["Version"] = hugo.BuildVersionString()
 
 	fe := herrors.UnwrapErrorWithFileContext(c.buildErr)
 	if fe != nil {
@@ -379,7 +380,7 @@
 	if themeVersionMismatch {
 		name := filepath.Base(dir)
 		cfg.Logger.ERROR.Printf("%s theme does not support Hugo version %s. Minimum version required is %s\n",
-			strings.ToUpper(name), helpers.CurrentHugoVersion.ReleaseVersion(), minVersion)
+			strings.ToUpper(name), hugo.CurrentVersion.ReleaseVersion(), minVersion)
 	}
 
 	return nil
--- a/commands/genman.go
+++ b/commands/genman.go
@@ -17,6 +17,7 @@
 	"fmt"
 	"strings"
 
+	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/gohugoio/hugo/helpers"
 	"github.com/gohugoio/hugo/hugofs"
 	"github.com/spf13/cobra"
@@ -45,7 +46,7 @@
 			header := &doc.GenManHeader{
 				Section: "1",
 				Manual:  "Hugo Manual",
-				Source:  fmt.Sprintf("Hugo %s", helpers.CurrentHugoVersion),
+				Source:  fmt.Sprintf("Hugo %s", hugo.CurrentVersion),
 			}
 			if !strings.HasSuffix(cc.genmandir, helpers.FilePathSeparator) {
 				cc.genmandir += helpers.FilePathSeparator
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -23,6 +23,7 @@
 	"sort"
 	"sync/atomic"
 
+	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/pkg/errors"
 
 	"github.com/gohugoio/hugo/common/herrors"
@@ -1041,7 +1042,7 @@
 		}
 
 		if minVersion, ok := tomlMeta["min_version"]; ok {
-			if helpers.CompareVersion(minVersion) > 0 {
+			if hugo.CompareVersion(minVersion) > 0 {
 				return absThemeDir, true, fmt.Sprint(minVersion)
 			}
 		}
--- a/commands/version.go
+++ b/commands/version.go
@@ -14,16 +14,9 @@
 package commands
 
 import (
-	"fmt"
-	"runtime"
-	"strings"
-
-	jww "github.com/spf13/jwalterweatherman"
-
-	"github.com/gohugoio/hugo/helpers"
-	"github.com/gohugoio/hugo/hugolib"
-	"github.com/gohugoio/hugo/resource/tocss/scss"
+	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/spf13/cobra"
+	jww "github.com/spf13/jwalterweatherman"
 )
 
 var _ cmder = (*versionCmd)(nil)
@@ -47,29 +40,5 @@
 }
 
 func printHugoVersion() {
-	jww.FEEDBACK.Println(hugoVersionString())
-}
-
-func hugoVersionString() string {
-	program := "Hugo Static Site Generator"
-
-	version := "v" + helpers.CurrentHugoVersion.String()
-	if hugolib.CommitHash != "" {
-		version += "-" + strings.ToUpper(hugolib.CommitHash)
-	}
-	if scss.Supports() {
-		version += "/extended"
-	}
-
-	osArch := runtime.GOOS + "/" + runtime.GOARCH
-
-	var buildDate string
-	if hugolib.BuildDate != "" {
-		buildDate = hugolib.BuildDate
-	} else {
-		buildDate = "unknown"
-	}
-
-	return fmt.Sprintf("%s %s %s BuildDate: %s", program, version, osArch, buildDate)
-
+	jww.FEEDBACK.Println(hugo.BuildVersionString())
 }
--- /dev/null
+++ b/common/hugo/hugo.go
@@ -1,0 +1,45 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugo
+
+import (
+	"fmt"
+	"html/template"
+)
+
+var (
+	// CommitHash contains the current Git revision. Use make to build to make
+	// sure this gets set.
+	CommitHash string
+
+	// BuildDate contains the date of the current build.
+	BuildDate string
+)
+
+// Info contains information about the current Hugo environment
+type Info struct {
+	Version    VersionString
+	Generator  template.HTML
+	CommitHash string
+	BuildDate  string
+}
+
+func NewInfo() Info {
+	return Info{
+		Version:    CurrentVersion.Version(),
+		CommitHash: CommitHash,
+		BuildDate:  BuildDate,
+		Generator:  template.HTML(fmt.Sprintf(`<meta name="generator" content="Hugo %s" />`, CurrentVersion.String())),
+	}
+}
--- /dev/null
+++ b/common/hugo/hugo_test.go
@@ -1,0 +1,34 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugo
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestHugoInfo(t *testing.T) {
+	assert := require.New(t)
+
+	hugoInfo := NewInfo()
+
+	assert.Equal(CurrentVersion.Version(), hugoInfo.Version)
+	assert.IsType(VersionString(""), hugoInfo.Version)
+	assert.Equal(CommitHash, hugoInfo.CommitHash)
+	assert.Equal(BuildDate, hugoInfo.BuildDate)
+	assert.Contains(hugoInfo.Generator, fmt.Sprintf("Hugo %s", hugoInfo.Version))
+
+}
--- /dev/null
+++ b/common/hugo/site.go
@@ -1,0 +1,24 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugo
+
+import "github.com/gohugoio/hugo/langs"
+
+// Site represents a site in the build. This is currently a very narrow interface,
+// but the actual implementation will be richer, see hugolib.SiteInfo.
+type Site interface {
+	Language() *langs.Language
+	IsServer() bool
+	Hugo() Info
+}
--- /dev/null
+++ b/common/hugo/vars_extended.go
@@ -1,0 +1,18 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build extended
+
+package hugo
+
+var isExtended = true
--- /dev/null
+++ b/common/hugo/vars_regular.go
@@ -1,0 +1,18 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !extended
+
+package hugo
+
+var isExtended = false
--- /dev/null
+++ b/common/hugo/version.go
@@ -1,0 +1,239 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugo
+
+import (
+	"fmt"
+
+	"runtime"
+	"strings"
+
+	"github.com/gohugoio/hugo/compare"
+	"github.com/spf13/cast"
+)
+
+// Version represents the Hugo build version.
+type Version struct {
+	// Major and minor version.
+	Number float32
+
+	// Increment this for bug releases
+	PatchLevel int
+
+	// HugoVersionSuffix is the suffix used in the Hugo version string.
+	// It will be blank for release versions.
+	Suffix string
+}
+
+var (
+	_ compare.Eqer     = (*VersionString)(nil)
+	_ compare.Comparer = (*VersionString)(nil)
+)
+
+func (v Version) String() string {
+	return version(v.Number, v.PatchLevel, v.Suffix)
+}
+
+// Version returns the Hugo version.
+func (v Version) Version() VersionString {
+	return VersionString(v.String())
+}
+
+// VersionString represents a Hugo version string.
+type VersionString string
+
+func (h VersionString) String() string {
+	return string(h)
+}
+
+// Compare implements the compare.Comparer interface.
+func (h VersionString) Compare(other interface{}) int {
+	v := MustParseVersion(h.String())
+	return compareVersionsWithSuffix(v.Number, v.PatchLevel, v.Suffix, other)
+}
+
+// Eq implements the compare.Eqer interface.
+func (h VersionString) Eq(other interface{}) bool {
+	s, err := cast.ToStringE(other)
+	if err != nil {
+		return false
+	}
+	return s == h.String()
+}
+
+var versionSuffixes = []string{"-test", "-DEV"}
+
+// ParseVersion parses a version string.
+func ParseVersion(s string) (Version, error) {
+	var vv Version
+	for _, suffix := range versionSuffixes {
+		if strings.HasSuffix(s, suffix) {
+			vv.Suffix = suffix
+			s = strings.TrimSuffix(s, suffix)
+		}
+	}
+
+	v, p := parseVersion(s)
+
+	vv.Number = v
+	vv.PatchLevel = p
+
+	return vv, nil
+}
+
+// MustParseVersion parses a version string
+// and panics if any error occurs.
+func MustParseVersion(s string) Version {
+	vv, err := ParseVersion(s)
+	if err != nil {
+		panic(err)
+	}
+	return vv
+}
+
+// ReleaseVersion represents the release version.
+func (v Version) ReleaseVersion() Version {
+	v.Suffix = ""
+	return v
+}
+
+// Next returns the next Hugo release version.
+func (v Version) Next() Version {
+	return Version{Number: v.Number + 0.01}
+}
+
+// Prev returns the previous Hugo release version.
+func (v Version) Prev() Version {
+	return Version{Number: v.Number - 0.01}
+}
+
+// NextPatchLevel returns the next patch/bugfix Hugo version.
+// This will be a patch increment on the previous Hugo version.
+func (v Version) NextPatchLevel(level int) Version {
+	return Version{Number: v.Number - 0.01, PatchLevel: level}
+}
+
+// BuildVersionString creates a version string. This is what you see when
+// running "hugo version".
+func BuildVersionString() string {
+	program := "Hugo Static Site Generator"
+
+	version := "v" + CurrentVersion.String()
+	if CommitHash != "" {
+		version += "-" + strings.ToUpper(CommitHash)
+	}
+	if isExtended {
+		version += "/extended"
+	}
+
+	osArch := runtime.GOOS + "/" + runtime.GOARCH
+
+	var buildDate string
+	if BuildDate != "" {
+		buildDate = BuildDate
+	} else {
+		buildDate = "unknown"
+	}
+
+	return fmt.Sprintf("%s %s %s BuildDate: %s", program, version, osArch, buildDate)
+
+}
+
+func version(version float32, patchVersion int, suffix string) string {
+	if patchVersion > 0 {
+		return fmt.Sprintf("%.2f.%d%s", version, patchVersion, suffix)
+	}
+	return fmt.Sprintf("%.2f%s", version, suffix)
+}
+
+// CompareVersion compares the given version string or number against the
+// running Hugo version.
+// It returns -1 if the given version is less than, 0 if equal and 1 if greater than
+// the running version.
+func CompareVersion(version interface{}) int {
+	return compareVersionsWithSuffix(CurrentVersion.Number, CurrentVersion.PatchLevel, CurrentVersion.Suffix, version)
+}
+
+func compareVersions(inVersion float32, inPatchVersion int, in interface{}) int {
+	return compareVersionsWithSuffix(inVersion, inPatchVersion, "", in)
+}
+
+func compareVersionsWithSuffix(inVersion float32, inPatchVersion int, suffix string, in interface{}) int {
+	var c int
+	switch d := in.(type) {
+	case float64:
+		c = compareFloatVersions(inVersion, float32(d))
+	case float32:
+		c = compareFloatVersions(inVersion, d)
+	case int:
+		c = compareFloatVersions(inVersion, float32(d))
+	case int32:
+		c = compareFloatVersions(inVersion, float32(d))
+	case int64:
+		c = compareFloatVersions(inVersion, float32(d))
+	default:
+		s, err := cast.ToStringE(in)
+		if err != nil {
+			return -1
+		}
+
+		v, err := ParseVersion(s)
+		if err != nil {
+			return -1
+		}
+
+		if v.Number == inVersion && v.PatchLevel == inPatchVersion {
+			return strings.Compare(suffix, v.Suffix)
+		}
+
+		if v.Number < inVersion || (v.Number == inVersion && v.PatchLevel < inPatchVersion) {
+			return -1
+		}
+
+		return 1
+	}
+
+	if c == 0 && suffix != "" {
+		return 1
+	}
+
+	return c
+}
+
+func parseVersion(s string) (float32, int) {
+	var (
+		v float32
+		p int
+	)
+
+	if strings.Count(s, ".") == 2 {
+		li := strings.LastIndex(s, ".")
+		p = cast.ToInt(s[li+1:])
+		s = s[:li]
+	}
+
+	v = float32(cast.ToFloat64(s))
+
+	return v, p
+}
+
+func compareFloatVersions(version float32, v float32) int {
+	if v == version {
+		return 0
+	}
+	if v < version {
+		return -1
+	}
+	return 1
+}
--- /dev/null
+++ b/common/hugo/version_current.go
@@ -1,0 +1,22 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugo
+
+// CurrentVersion represents the current build version.
+// This should be the only one.
+var CurrentVersion = Version{
+	Number:     0.53,
+	PatchLevel: 0,
+	Suffix:     "-DEV",
+}
--- /dev/null
+++ b/common/hugo/version_test.go
@@ -1,0 +1,70 @@
+// Copyright 2015 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugo
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestHugoVersion(t *testing.T) {
+	assert.Equal(t, "0.15-DEV", version(0.15, 0, "-DEV"))
+	assert.Equal(t, "0.15.2-DEV", version(0.15, 2, "-DEV"))
+
+	v := Version{Number: 0.21, PatchLevel: 0, Suffix: "-DEV"}
+
+	require.Equal(t, v.ReleaseVersion().String(), "0.21")
+	require.Equal(t, "0.21-DEV", v.String())
+	require.Equal(t, "0.22", v.Next().String())
+	nextVersionString := v.Next().Version()
+	require.Equal(t, "0.22", nextVersionString.String())
+	require.True(t, nextVersionString.Eq("0.22"))
+	require.False(t, nextVersionString.Eq("0.21"))
+	require.True(t, nextVersionString.Eq(nextVersionString))
+	require.Equal(t, "0.20.3", v.NextPatchLevel(3).String())
+}
+
+func TestCompareVersions(t *testing.T) {
+	require.Equal(t, 0, compareVersions(0.20, 0, 0.20))
+	require.Equal(t, 0, compareVersions(0.20, 0, float32(0.20)))
+	require.Equal(t, 0, compareVersions(0.20, 0, float64(0.20)))
+	require.Equal(t, 1, compareVersions(0.19, 1, 0.20))
+	require.Equal(t, 1, compareVersions(0.19, 3, "0.20.2"))
+	require.Equal(t, -1, compareVersions(0.19, 1, 0.01))
+	require.Equal(t, 1, compareVersions(0, 1, 3))
+	require.Equal(t, 1, compareVersions(0, 1, int32(3)))
+	require.Equal(t, 1, compareVersions(0, 1, int64(3)))
+	require.Equal(t, 0, compareVersions(0.20, 0, "0.20"))
+	require.Equal(t, 0, compareVersions(0.20, 1, "0.20.1"))
+	require.Equal(t, -1, compareVersions(0.20, 1, "0.20"))
+	require.Equal(t, 1, compareVersions(0.20, 0, "0.20.1"))
+	require.Equal(t, 1, compareVersions(0.20, 1, "0.20.2"))
+	require.Equal(t, 1, compareVersions(0.21, 1, "0.22.1"))
+	require.Equal(t, -1, compareVersions(0.22, 0, "0.22-DEV"))
+	require.Equal(t, 1, compareVersions(0.22, 0, "0.22.1-DEV"))
+	require.Equal(t, 1, compareVersionsWithSuffix(0.22, 0, "-DEV", "0.22"))
+	require.Equal(t, -1, compareVersionsWithSuffix(0.22, 1, "-DEV", "0.22"))
+	require.Equal(t, 0, compareVersionsWithSuffix(0.22, 1, "-DEV", "0.22.1-DEV"))
+
+}
+
+func TestParseHugoVersion(t *testing.T) {
+	require.Equal(t, "0.25", MustParseVersion("0.25").String())
+	require.Equal(t, "0.25.2", MustParseVersion("0.25.2").String())
+	require.Equal(t, "0.25-test", MustParseVersion("0.25-test").String())
+	require.Equal(t, "0.25-DEV", MustParseVersion("0.25-DEV").String())
+
+}
--- a/deps/deps.go
+++ b/deps/deps.go
@@ -7,6 +7,7 @@
 	"github.com/pkg/errors"
 
 	"github.com/gohugoio/hugo/cache/filecache"
+	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/gohugoio/hugo/common/loggers"
 	"github.com/gohugoio/hugo/config"
 	"github.com/gohugoio/hugo/helpers"
@@ -62,8 +63,12 @@
 	// The translation func to use
 	Translate func(translationID string, args ...interface{}) string `json:"-"`
 
+	// The language in use. TODO(bep) consolidate with site
 	Language *langs.Language
 
+	// The site building.
+	Site hugo.Site
+
 	// All the output formats available for the current site.
 	OutputFormatsConfig output.Formats
 
@@ -230,6 +235,7 @@
 		ResourceSpec:        resourceSpec,
 		Cfg:                 cfg.Language,
 		Language:            cfg.Language,
+		Site:                cfg.Site,
 		FileCaches:          fileCaches,
 		BuildStartListeners: &Listeners{},
 		Timeout:             time.Duration(timeoutms) * time.Millisecond,
@@ -245,7 +251,7 @@
 
 // ForLanguage creates a copy of the Deps with the language dependent
 // parts switched out.
-func (d Deps) ForLanguage(cfg DepsCfg) (*Deps, error) {
+func (d Deps) ForLanguage(cfg DepsCfg, onCreated func(d *Deps) error) (*Deps, error) {
 	l := cfg.Language
 	var err error
 
@@ -259,6 +265,8 @@
 		return nil, err
 	}
 
+	d.Site = cfg.Site
+
 	// The resource cache is global so reuse.
 	// TODO(bep) clean up these inits.
 	resourceCache := d.ResourceSpec.ResourceCache
@@ -271,6 +279,12 @@
 	d.Cfg = l
 	d.Language = l
 
+	if onCreated != nil {
+		if err = onCreated(&d); err != nil {
+			return nil, err
+		}
+	}
+
 	if err := d.translationProvider.Clone(&d); err != nil {
 		return nil, err
 	}
@@ -298,6 +312,9 @@
 
 	// The language to use.
 	Language *langs.Language
+
+	// The Site in use
+	Site hugo.Site
 
 	// The configuration to use.
 	Cfg config.Provider
--- a/goreleaser-extended.yml
+++ b/goreleaser-extended.yml
@@ -2,7 +2,7 @@
 builds:
 - binary: hugo
   ldflags:
-    - -s -w -X github.com/gohugoio/hugo/hugolib.BuildDate={{.Date}}
+    - -s -w -X github.com/gohugoio/hugo/common/hugo.BuildDate={{.Date}} -X github.com/gohugoio/hugo/common/hugo.CommitHash={{ .ShortCommit }}
     - "-extldflags '-static'"
   env:
     - CGO_ENABLED=1
--- a/goreleaser.yml
+++ b/goreleaser.yml
@@ -2,7 +2,7 @@
 build:
   main: main.go
   binary: hugo
-  ldflags: -s -w -X github.com/gohugoio/hugo/hugolib.BuildDate={{.Date}}
+  ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.BuildDate={{.Date}} -X github.com/gohugoio/hugo/common/hugo.CommitHash={{ .ShortCommit }}
   env:
     - CGO_ENABLED=0
   goos:
--- a/helpers/general.go
+++ b/helpers/general.go
@@ -27,6 +27,8 @@
 	"unicode"
 	"unicode/utf8"
 
+	"github.com/gohugoio/hugo/common/hugo"
+
 	"github.com/gohugoio/hugo/hugofs"
 
 	"github.com/spf13/afero"
@@ -324,7 +326,7 @@
 // plenty of time to fix their templates.
 func Deprecated(object, item, alternative string, err bool) {
 	if err {
-		DistinctErrorLog.Printf("%s's %s is deprecated and will be removed in Hugo %s. %s", object, item, CurrentHugoVersion.Next().ReleaseVersion(), alternative)
+		DistinctErrorLog.Printf("%s's %s is deprecated and will be removed in Hugo %s. %s", object, item, hugo.CurrentVersion.Next().ReleaseVersion(), alternative)
 
 	} else {
 		// Make sure the users see this while avoiding build breakage. This will not lead to an os.Exit(-1)
--- a/helpers/hugo.go
+++ /dev/null
@@ -1,219 +1,0 @@
-// Copyright 2015 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package helpers
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/gohugoio/hugo/compare"
-	"github.com/spf13/cast"
-)
-
-// HugoVersion represents the Hugo build version.
-type HugoVersion struct {
-	// Major and minor version.
-	Number float32
-
-	// Increment this for bug releases
-	PatchLevel int
-
-	// HugoVersionSuffix is the suffix used in the Hugo version string.
-	// It will be blank for release versions.
-	Suffix string
-}
-
-var (
-	_ compare.Eqer     = (*HugoVersionString)(nil)
-	_ compare.Comparer = (*HugoVersionString)(nil)
-)
-
-func (v HugoVersion) String() string {
-	return hugoVersion(v.Number, v.PatchLevel, v.Suffix)
-}
-
-// Version returns the Hugo version.
-func (v HugoVersion) Version() HugoVersionString {
-	return HugoVersionString(v.String())
-}
-
-// HugoVersionString represents a Hugo version string.
-type HugoVersionString string
-
-func (h HugoVersionString) String() string {
-	return string(h)
-}
-
-// Compare implements the compare.Comparer interface.
-func (h HugoVersionString) Compare(other interface{}) int {
-	v := MustParseHugoVersion(h.String())
-	return compareVersionsWithSuffix(v.Number, v.PatchLevel, v.Suffix, other)
-}
-
-// Eq implements the compare.Eqer interface.
-func (h HugoVersionString) Eq(other interface{}) bool {
-	s, err := cast.ToStringE(other)
-	if err != nil {
-		return false
-	}
-	return s == h.String()
-}
-
-var versionSuffixes = []string{"-test", "-DEV"}
-
-// ParseHugoVersion parses a version string.
-func ParseHugoVersion(s string) (HugoVersion, error) {
-	var vv HugoVersion
-	for _, suffix := range versionSuffixes {
-		if strings.HasSuffix(s, suffix) {
-			vv.Suffix = suffix
-			s = strings.TrimSuffix(s, suffix)
-		}
-	}
-
-	v, p := parseVersion(s)
-
-	vv.Number = v
-	vv.PatchLevel = p
-
-	return vv, nil
-}
-
-// MustParseHugoVersion parses a version string
-// and panics if any error occurs.
-func MustParseHugoVersion(s string) HugoVersion {
-	vv, err := ParseHugoVersion(s)
-	if err != nil {
-		panic(err)
-	}
-	return vv
-}
-
-// ReleaseVersion represents the release version.
-func (v HugoVersion) ReleaseVersion() HugoVersion {
-	v.Suffix = ""
-	return v
-}
-
-// Next returns the next Hugo release version.
-func (v HugoVersion) Next() HugoVersion {
-	return HugoVersion{Number: v.Number + 0.01}
-}
-
-// Prev returns the previous Hugo release version.
-func (v HugoVersion) Prev() HugoVersion {
-	return HugoVersion{Number: v.Number - 0.01}
-}
-
-// NextPatchLevel returns the next patch/bugfix Hugo version.
-// This will be a patch increment on the previous Hugo version.
-func (v HugoVersion) NextPatchLevel(level int) HugoVersion {
-	return HugoVersion{Number: v.Number - 0.01, PatchLevel: level}
-}
-
-// CurrentHugoVersion represents the current build version.
-// This should be the only one.
-var CurrentHugoVersion = HugoVersion{
-	Number:     0.53,
-	PatchLevel: 0,
-	Suffix:     "-DEV",
-}
-
-func hugoVersion(version float32, patchVersion int, suffix string) string {
-	if patchVersion > 0 {
-		return fmt.Sprintf("%.2f.%d%s", version, patchVersion, suffix)
-	}
-	return fmt.Sprintf("%.2f%s", version, suffix)
-}
-
-// CompareVersion compares the given version string or number against the
-// running Hugo version.
-// It returns -1 if the given version is less than, 0 if equal and 1 if greater than
-// the running version.
-func CompareVersion(version interface{}) int {
-	return compareVersionsWithSuffix(CurrentHugoVersion.Number, CurrentHugoVersion.PatchLevel, CurrentHugoVersion.Suffix, version)
-}
-
-func compareVersions(inVersion float32, inPatchVersion int, in interface{}) int {
-	return compareVersionsWithSuffix(inVersion, inPatchVersion, "", in)
-}
-
-func compareVersionsWithSuffix(inVersion float32, inPatchVersion int, suffix string, in interface{}) int {
-	var c int
-	switch d := in.(type) {
-	case float64:
-		c = compareFloatVersions(inVersion, float32(d))
-	case float32:
-		c = compareFloatVersions(inVersion, d)
-	case int:
-		c = compareFloatVersions(inVersion, float32(d))
-	case int32:
-		c = compareFloatVersions(inVersion, float32(d))
-	case int64:
-		c = compareFloatVersions(inVersion, float32(d))
-	default:
-		s, err := cast.ToStringE(in)
-		if err != nil {
-			return -1
-		}
-
-		v, err := ParseHugoVersion(s)
-		if err != nil {
-			return -1
-		}
-
-		if v.Number == inVersion && v.PatchLevel == inPatchVersion {
-			return strings.Compare(suffix, v.Suffix)
-		}
-
-		if v.Number < inVersion || (v.Number == inVersion && v.PatchLevel < inPatchVersion) {
-			return -1
-		}
-
-		return 1
-	}
-
-	if c == 0 && suffix != "" {
-		return 1
-	}
-
-	return c
-}
-
-func parseVersion(s string) (float32, int) {
-	var (
-		v float32
-		p int
-	)
-
-	if strings.Count(s, ".") == 2 {
-		li := strings.LastIndex(s, ".")
-		p = cast.ToInt(s[li+1:])
-		s = s[:li]
-	}
-
-	v = float32(cast.ToFloat64(s))
-
-	return v, p
-}
-
-func compareFloatVersions(version float32, v float32) int {
-	if v == version {
-		return 0
-	}
-	if v < version {
-		return -1
-	}
-	return 1
-}
--- a/helpers/hugo_test.go
+++ /dev/null
@@ -1,70 +1,0 @@
-// Copyright 2015 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package helpers
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-func TestHugoVersion(t *testing.T) {
-	assert.Equal(t, "0.15-DEV", hugoVersion(0.15, 0, "-DEV"))
-	assert.Equal(t, "0.15.2-DEV", hugoVersion(0.15, 2, "-DEV"))
-
-	v := HugoVersion{Number: 0.21, PatchLevel: 0, Suffix: "-DEV"}
-
-	require.Equal(t, v.ReleaseVersion().String(), "0.21")
-	require.Equal(t, "0.21-DEV", v.String())
-	require.Equal(t, "0.22", v.Next().String())
-	nextVersionString := v.Next().Version()
-	require.Equal(t, "0.22", nextVersionString.String())
-	require.True(t, nextVersionString.Eq("0.22"))
-	require.False(t, nextVersionString.Eq("0.21"))
-	require.True(t, nextVersionString.Eq(nextVersionString))
-	require.Equal(t, "0.20.3", v.NextPatchLevel(3).String())
-}
-
-func TestCompareVersions(t *testing.T) {
-	require.Equal(t, 0, compareVersions(0.20, 0, 0.20))
-	require.Equal(t, 0, compareVersions(0.20, 0, float32(0.20)))
-	require.Equal(t, 0, compareVersions(0.20, 0, float64(0.20)))
-	require.Equal(t, 1, compareVersions(0.19, 1, 0.20))
-	require.Equal(t, 1, compareVersions(0.19, 3, "0.20.2"))
-	require.Equal(t, -1, compareVersions(0.19, 1, 0.01))
-	require.Equal(t, 1, compareVersions(0, 1, 3))
-	require.Equal(t, 1, compareVersions(0, 1, int32(3)))
-	require.Equal(t, 1, compareVersions(0, 1, int64(3)))
-	require.Equal(t, 0, compareVersions(0.20, 0, "0.20"))
-	require.Equal(t, 0, compareVersions(0.20, 1, "0.20.1"))
-	require.Equal(t, -1, compareVersions(0.20, 1, "0.20"))
-	require.Equal(t, 1, compareVersions(0.20, 0, "0.20.1"))
-	require.Equal(t, 1, compareVersions(0.20, 1, "0.20.2"))
-	require.Equal(t, 1, compareVersions(0.21, 1, "0.22.1"))
-	require.Equal(t, -1, compareVersions(0.22, 0, "0.22-DEV"))
-	require.Equal(t, 1, compareVersions(0.22, 0, "0.22.1-DEV"))
-	require.Equal(t, 1, compareVersionsWithSuffix(0.22, 0, "-DEV", "0.22"))
-	require.Equal(t, -1, compareVersionsWithSuffix(0.22, 1, "-DEV", "0.22"))
-	require.Equal(t, 0, compareVersionsWithSuffix(0.22, 1, "-DEV", "0.22.1-DEV"))
-
-}
-
-func TestParseHugoVersion(t *testing.T) {
-	require.Equal(t, "0.25", MustParseHugoVersion("0.25").String())
-	require.Equal(t, "0.25.2", MustParseHugoVersion("0.25.2").String())
-	require.Equal(t, "0.25-test", MustParseHugoVersion("0.25-test").String())
-	require.Equal(t, "0.25-DEV", MustParseHugoVersion("0.25-DEV").String())
-
-}
--- /dev/null
+++ b/htesting/test_structs.go
@@ -1,0 +1,51 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package htesting
+
+import (
+	"github.com/gohugoio/hugo/common/hugo"
+	"github.com/gohugoio/hugo/langs"
+	"github.com/spf13/viper"
+)
+
+type testSite struct {
+	h hugo.Info
+	l *langs.Language
+}
+
+func (t testSite) Hugo() hugo.Info {
+	return t.h
+}
+
+func (t testSite) IsServer() bool {
+	return false
+}
+
+func (t testSite) Language() *langs.Language {
+	return t.l
+}
+
+// NewTestHugoSite creates a new minimal test site.
+func NewTestHugoSite() hugo.Site {
+	return testSite{
+		h: hugo.NewInfo(),
+		l: langs.NewLanguage("en", newTestConfig()),
+	}
+}
+
+func newTestConfig() *viper.Viper {
+	v := viper.New()
+	v.Set("contentDir", "content")
+	return v
+}
--- a/hugolib/hugo_info.go
+++ /dev/null
@@ -1,49 +1,0 @@
-// Copyright 2018 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package hugolib
-
-import (
-	"fmt"
-	"html/template"
-
-	"github.com/gohugoio/hugo/helpers"
-)
-
-var (
-	// CommitHash contains the current Git revision. Use make to build to make
-	// sure this gets set.
-	CommitHash string
-
-	// BuildDate contains the date of the current build.
-	BuildDate string
-)
-
-var hugoInfo *HugoInfo
-
-// HugoInfo contains information about the current Hugo environment
-type HugoInfo struct {
-	Version    helpers.HugoVersionString
-	Generator  template.HTML
-	CommitHash string
-	BuildDate  string
-}
-
-func init() {
-	hugoInfo = &HugoInfo{
-		Version:    helpers.CurrentHugoVersion.Version(),
-		CommitHash: CommitHash,
-		BuildDate:  BuildDate,
-		Generator:  template.HTML(fmt.Sprintf(`<meta name="generator" content="Hugo %s" />`, helpers.CurrentHugoVersion.String())),
-	}
-}
--- a/hugolib/hugo_info_test.go
+++ /dev/null
@@ -1,33 +1,0 @@
-// Copyright 2018 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package hugolib
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/gohugoio/hugo/helpers"
-	"github.com/stretchr/testify/require"
-)
-
-func TestHugoInfo(t *testing.T) {
-	assert := require.New(t)
-
-	assert.Equal(helpers.CurrentHugoVersion.Version(), hugoInfo.Version)
-	assert.IsType(helpers.HugoVersionString(""), hugoInfo.Version)
-	assert.Equal(CommitHash, hugoInfo.CommitHash)
-	assert.Equal(BuildDate, hugoInfo.BuildDate)
-	assert.Contains(hugoInfo.Generator, fmt.Sprintf("Hugo %s", hugoInfo.Version))
-
-}
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -21,12 +21,13 @@
 	"strings"
 	"sync"
 
+	"github.com/gohugoio/hugo/publisher"
+
 	"github.com/gohugoio/hugo/common/herrors"
 	"github.com/gohugoio/hugo/common/loggers"
 	"github.com/gohugoio/hugo/deps"
 	"github.com/gohugoio/hugo/helpers"
 	"github.com/gohugoio/hugo/langs"
-	"github.com/gohugoio/hugo/publisher"
 
 	"github.com/gohugoio/hugo/i18n"
 	"github.com/gohugoio/hugo/tpl"
@@ -224,6 +225,27 @@
 			continue
 		}
 
+		onCreated := func(d *deps.Deps) error {
+			s.Deps = d
+
+			// Set up the main publishing chain.
+			s.publisher = publisher.NewDestinationPublisher(d.PathSpec.BaseFs.PublishFs, s.outputFormatsConfig, s.mediaTypesConfig, cfg.Cfg.GetBool("minify"))
+
+			if err := s.initializeSiteInfo(); err != nil {
+				return err
+			}
+
+			d.Site = &s.Info
+
+			siteConfig, err := loadSiteConfig(s.Language)
+			if err != nil {
+				return err
+			}
+			s.siteConfig = siteConfig
+			s.siteRefLinker, err = newSiteRefLinker(s.Language, s)
+			return err
+		}
+
 		cfg.Language = s.Language
 		cfg.MediaTypes = s.mediaTypesConfig
 		cfg.OutputFormats = s.outputFormatsConfig
@@ -238,37 +260,23 @@
 			}
 
 			d.OutputFormatsConfig = s.outputFormatsConfig
-			s.Deps = d
 
+			if err := onCreated(d); err != nil {
+				return err
+			}
+
 			if err = d.LoadResources(); err != nil {
 				return err
 			}
 
 		} else {
-			d, err = d.ForLanguage(cfg)
+			d, err = d.ForLanguage(cfg, onCreated)
 			if err != nil {
 				return err
 			}
 			d.OutputFormatsConfig = s.outputFormatsConfig
-			s.Deps = d
 		}
 
-		// Set up the main publishing chain.
-		s.publisher = publisher.NewDestinationPublisher(d.PathSpec.BaseFs.PublishFs, s.outputFormatsConfig, s.mediaTypesConfig, cfg.Cfg.GetBool("minify"))
-
-		if err := s.initializeSiteInfo(); err != nil {
-			return err
-		}
-
-		siteConfig, err := loadSiteConfig(s.Language)
-		if err != nil {
-			return err
-		}
-		s.siteConfig = siteConfig
-		s.siteRefLinker, err = newSiteRefLinker(s.Language, s)
-		if err != nil {
-			return err
-		}
 	}
 
 	return nil
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -21,6 +21,8 @@
 	"math/rand"
 	"reflect"
 
+	"github.com/gohugoio/hugo/common/hugo"
+
 	"github.com/gohugoio/hugo/common/maps"
 	"github.com/gohugoio/hugo/common/urls"
 	"github.com/gohugoio/hugo/media"
@@ -1873,8 +1875,8 @@
 	return &c
 }
 
-func (p *Page) Hugo() *HugoInfo {
-	return hugoInfo
+func (p *Page) Hugo() hugo.Info {
+	return p.s.Info.hugoInfo
 }
 
 // GetPage looks up a page for the given ref.
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -1436,7 +1436,7 @@
 		{func(n *Page) bool { return n.IsNode() }},
 		{func(n *Page) bool { return !n.IsPage() }},
 		{func(n *Page) bool { return n.Scratch() != nil }},
-		{func(n *Page) bool { return n.Hugo() != nil }},
+		{func(n *Page) bool { return n.Hugo().Version != "" }},
 	} {
 
 		n := s.newHomePage()
--- a/hugolib/pages_language_merge_test.go
+++ b/hugolib/pages_language_merge_test.go
@@ -66,7 +66,7 @@
 
 	firstNN := nnSite.RegularPages[0]
 	assert.Equal(4, len(firstNN.Sites()))
-	assert.Equal("en", firstNN.Sites().First().Language.Lang)
+	assert.Equal("en", firstNN.Sites().First().Language().Lang)
 
 	nnBundle := nnSite.getPage("page", "bundle")
 	enBundle := enSite.getPage("page", "bundle")
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -36,11 +36,11 @@
 
 	"github.com/gohugoio/hugo/common/herrors"
 
-	_errors "github.com/pkg/errors"
-
+	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/gohugoio/hugo/common/maps"
 	"github.com/gohugoio/hugo/publisher"
 	"github.com/gohugoio/hugo/resource"
+	_errors "github.com/pkg/errors"
 
 	"github.com/gohugoio/hugo/langs"
 
@@ -388,7 +388,7 @@
 	Social     SiteSocial
 	*PageCollections
 	Menus                          *Menus
-	Hugo                           *HugoInfo
+	hugoInfo                       hugo.Info
 	Title                          string
 	RSSLink                        string
 	Author                         map[string]interface{}
@@ -405,7 +405,7 @@
 	Data                           *map[string]interface{}
 	owner                          *HugoSites
 	s                              *Site
-	Language                       *langs.Language
+	language                       *langs.Language
 	LanguagePrefix                 string
 	Languages                      langs.Languages
 	defaultContentLanguageInSubdir bool
@@ -412,10 +412,18 @@
 	sectionPagesMenu               string
 }
 
+func (s *SiteInfo) Language() *langs.Language {
+	return s.language
+}
+
 func (s *SiteInfo) Config() SiteConfig {
 	return s.s.siteConfig
 }
 
+func (s *SiteInfo) Hugo() hugo.Info {
+	return s.hugoInfo
+}
+
 func (s *SiteInfo) String() string {
 	return fmt.Sprintf("Site(%q)", s.Title)
 }
@@ -797,7 +805,10 @@
 				MediaTypes:    site.mediaTypesConfig,
 				OutputFormats: site.outputFormatsConfig,
 			}
-			site.Deps, err = first.Deps.ForLanguage(depsCfg)
+			site.Deps, err = first.Deps.ForLanguage(depsCfg, func(d *deps.Deps) error {
+				d.Site = &site.Info
+				return nil
+			})
 			if err != nil {
 				return whatChanged{}, err
 			}
@@ -1122,7 +1133,7 @@
 func (s *SiteInfo) HomeAbsURL() string {
 	base := ""
 	if s.IsMultiLingual() {
-		base = s.Language.Lang
+		base = s.Language().Lang
 	}
 	return s.owner.AbsURL(base, false)
 }
@@ -1194,7 +1205,7 @@
 		Social:                         lang.GetStringMapString("social"),
 		LanguageCode:                   lang.GetString("languageCode"),
 		Copyright:                      lang.GetString("copyright"),
-		Language:                       lang,
+		language:                       lang,
 		LanguagePrefix:                 languagePrefix,
 		Languages:                      languages,
 		defaultContentLanguageInSubdir: defaultContentInSubDir,
@@ -1211,6 +1222,7 @@
 		Data:                           &s.Data,
 		owner:                          s.owner,
 		s:                              s,
+		hugoInfo:                       hugo.NewInfo(),
 		// TODO(bep) make this Menu and similar into delegate methods on SiteInfo
 		Taxonomies: s.Taxonomies,
 	}
--- a/hugolib/template_test.go
+++ b/hugolib/template_test.go
@@ -236,3 +236,24 @@
 	b.AssertFileContent("public/page/index.html", "Base: Hi!?")
 
 }
+
+func TestTemplateFuncs(t *testing.T) {
+
+	b := newTestSitesBuilder(t).WithDefaultMultiSiteConfig()
+
+	homeTpl := `Site: {{ site.Language.Lang }} / {{ .Site.Language.Lang }} / {{ site.BaseURL }}
+Hugo: {{ hugo.Generator }}
+`
+
+	b.WithTemplatesAdded(
+		"index.html", homeTpl,
+		"index.fr.html", homeTpl,
+	)
+
+	b.CreateSites().Build(BuildCfg{})
+
+	b.AssertFileContent("public/en/index.html", "Site: en / en / http://example.com/blog",
+		"Hugo: <meta name=\"generator\" content=\"Hugo")
+	b.AssertFileContent("public/fr/index.html", "Site: fr / fr / http://example.com/blog")
+
+}
--- a/i18n/i18n_test.go
+++ b/i18n/i18n_test.go
@@ -20,6 +20,7 @@
 	"github.com/gohugoio/hugo/tpl/tplimpl"
 
 	"github.com/gohugoio/hugo/common/loggers"
+	"github.com/gohugoio/hugo/htesting"
 	"github.com/gohugoio/hugo/langs"
 	"github.com/spf13/afero"
 
@@ -183,6 +184,7 @@
 	l.Set("i18nDir", "i18n")
 	return deps.DepsCfg{
 		Language:            l,
+		Site:                htesting.NewTestHugoSite(),
 		Cfg:                 cfg,
 		Fs:                  fs,
 		Logger:              logger,
--- a/magefile.go
+++ b/magefile.go
@@ -21,10 +21,10 @@
 
 const (
 	packageName  = "github.com/gohugoio/hugo"
-	noGitLdflags = "-X $PACKAGE/hugolib.BuildDate=$BUILD_DATE"
+	noGitLdflags = "-X $PACKAGE/common/hugo.BuildDate=$BUILD_DATE"
 )
 
-var ldflags = "-X $PACKAGE/hugolib.CommitHash=$COMMIT_HASH -X $PACKAGE/hugolib.BuildDate=$BUILD_DATE"
+var ldflags = "-X $PACKAGE/common/hugo.CommitHash=$COMMIT_HASH -X $PACKAGE/common/hugo.BuildDate=$BUILD_DATE"
 
 // allow user to override go executable by running as GOEXE=xxx make ... on unix-like systems
 var goexe = "go"
--- a/releaser/releaser.go
+++ b/releaser/releaser.go
@@ -25,9 +25,8 @@
 	"regexp"
 	"strings"
 
+	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/pkg/errors"
-
-	"github.com/gohugoio/hugo/helpers"
 )
 
 const commitPrefix = "releaser:"
@@ -52,8 +51,8 @@
 	git func(args ...string) (string, error)
 }
 
-func (r ReleaseHandler) calculateVersions() (helpers.HugoVersion, helpers.HugoVersion) {
-	newVersion := helpers.MustParseHugoVersion(r.cliVersion)
+func (r ReleaseHandler) calculateVersions() (hugo.Version, hugo.Version) {
+	newVersion := hugo.MustParseVersion(r.cliVersion)
 	finalVersion := newVersion.Next()
 	finalVersion.PatchLevel = 0
 
@@ -261,7 +260,7 @@
 	return nil
 }
 
-func (r *ReleaseHandler) bumpVersions(ver helpers.HugoVersion) error {
+func (r *ReleaseHandler) bumpVersions(ver hugo.Version) error {
 	toDev := ""
 
 	if ver.Suffix != "" {
@@ -268,7 +267,7 @@
 		toDev = ver.Suffix
 	}
 
-	if err := r.replaceInFile("helpers/hugo.go",
+	if err := r.replaceInFile("common/hugo/version_current.go",
 		`Number:(\s{4,})(.*),`, fmt.Sprintf(`Number:${1}%.2f,`, ver.Number),
 		`PatchLevel:(\s*)(.*),`, fmt.Sprintf(`PatchLevel:${1}%d,`, ver.PatchLevel),
 		`Suffix:(\s{4,})".*",`, fmt.Sprintf(`Suffix:${1}"%s",`, toDev)); err != nil {
--- a/tpl/compare/compare_test.go
+++ b/tpl/compare/compare_test.go
@@ -21,8 +21,7 @@
 	"testing"
 	"time"
 
-	"github.com/gohugoio/hugo/helpers"
-
+	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/spf13/cast"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -173,17 +172,17 @@
 		{tstEqerType1("a"), tstEqerType2("a"), 0},
 		{tstEqerType2("a"), tstEqerType1("a"), 0},
 		{tstEqerType2("a"), tstEqerType1("b"), -1},
-		{helpers.MustParseHugoVersion("0.32.1").Version(), helpers.MustParseHugoVersion("0.32").Version(), 1},
-		{helpers.MustParseHugoVersion("0.35").Version(), helpers.MustParseHugoVersion("0.32").Version(), 1},
-		{helpers.MustParseHugoVersion("0.36").Version(), helpers.MustParseHugoVersion("0.36").Version(), 0},
-		{helpers.MustParseHugoVersion("0.32").Version(), helpers.MustParseHugoVersion("0.36").Version(), -1},
-		{helpers.MustParseHugoVersion("0.32").Version(), "0.36", -1},
-		{"0.36", helpers.MustParseHugoVersion("0.32").Version(), 1},
-		{"0.36", helpers.MustParseHugoVersion("0.36").Version(), 0},
-		{"0.37", helpers.MustParseHugoVersion("0.37-DEV").Version(), 1},
-		{"0.37-DEV", helpers.MustParseHugoVersion("0.37").Version(), -1},
-		{"0.36", helpers.MustParseHugoVersion("0.37-DEV").Version(), -1},
-		{"0.37-DEV", helpers.MustParseHugoVersion("0.37-DEV").Version(), 0},
+		{hugo.MustParseVersion("0.32.1").Version(), hugo.MustParseVersion("0.32").Version(), 1},
+		{hugo.MustParseVersion("0.35").Version(), hugo.MustParseVersion("0.32").Version(), 1},
+		{hugo.MustParseVersion("0.36").Version(), hugo.MustParseVersion("0.36").Version(), 0},
+		{hugo.MustParseVersion("0.32").Version(), hugo.MustParseVersion("0.36").Version(), -1},
+		{hugo.MustParseVersion("0.32").Version(), "0.36", -1},
+		{"0.36", hugo.MustParseVersion("0.32").Version(), 1},
+		{"0.36", hugo.MustParseVersion("0.36").Version(), 0},
+		{"0.37", hugo.MustParseVersion("0.37-DEV").Version(), 1},
+		{"0.37-DEV", hugo.MustParseVersion("0.37").Version(), -1},
+		{"0.36", hugo.MustParseVersion("0.37-DEV").Version(), -1},
+		{"0.37-DEV", hugo.MustParseVersion("0.37-DEV").Version(), 0},
 	} {
 		result := funcUnderTest(test.left, test.right)
 		success := false
--- /dev/null
+++ b/tpl/hugo/init.go
@@ -1,0 +1,41 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package site
+
+import (
+	"github.com/gohugoio/hugo/deps"
+	"github.com/gohugoio/hugo/htesting"
+	"github.com/gohugoio/hugo/tpl/internal"
+)
+
+const name = "hugo"
+
+func init() {
+	f := func(d *deps.Deps) *internal.TemplateFuncsNamespace {
+
+		h := htesting.NewTestHugoSite().Hugo()
+
+		ns := &internal.TemplateFuncsNamespace{
+			Name:    name,
+			Context: func(args ...interface{}) interface{} { return h },
+		}
+
+		// We just add the Hugo struct as the namespace here. No method mappings.
+
+		return ns
+
+	}
+
+	internal.AddTemplateFuncsNamespace(f)
+}
--- /dev/null
+++ b/tpl/hugo/init_test.go
@@ -1,0 +1,41 @@
+// Copyright 2017 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package site
+
+import (
+	"testing"
+
+	"github.com/gohugoio/hugo/htesting"
+
+	"github.com/gohugoio/hugo/deps"
+	"github.com/gohugoio/hugo/tpl/internal"
+	"github.com/stretchr/testify/require"
+)
+
+func TestInit(t *testing.T) {
+	var found bool
+	var ns *internal.TemplateFuncsNamespace
+	s := htesting.NewTestHugoSite()
+
+	for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
+		ns = nsf(&deps.Deps{Site: s})
+		if ns.Name == name {
+			found = true
+			break
+		}
+	}
+
+	require.True(t, found)
+	require.IsType(t, s.Hugo(), ns.Context())
+}
--- /dev/null
+++ b/tpl/site/init.go
@@ -1,0 +1,44 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package site
+
+import (
+	"github.com/gohugoio/hugo/deps"
+
+	"github.com/gohugoio/hugo/tpl/internal"
+)
+
+const name = "site"
+
+func init() {
+	f := func(d *deps.Deps) *internal.TemplateFuncsNamespace {
+
+		s := d.Site
+		ns := &internal.TemplateFuncsNamespace{
+			Name:    name,
+			Context: func(args ...interface{}) interface{} { return s },
+		}
+
+		if s == nil {
+			panic("no Site")
+		}
+
+		// We just add the Site as the namespace here. No method mappings.
+
+		return ns
+
+	}
+
+	internal.AddTemplateFuncsNamespace(f)
+}
--- /dev/null
+++ b/tpl/site/init_test.go
@@ -1,0 +1,40 @@
+// Copyright 2017 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package site
+
+import (
+	"testing"
+
+	"github.com/gohugoio/hugo/deps"
+	"github.com/gohugoio/hugo/htesting"
+	"github.com/gohugoio/hugo/tpl/internal"
+	"github.com/stretchr/testify/require"
+)
+
+func TestInit(t *testing.T) {
+	var found bool
+	var ns *internal.TemplateFuncsNamespace
+	s := htesting.NewTestHugoSite()
+
+	for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
+		ns = nsf(&deps.Deps{Site: s})
+		if ns.Name == name {
+			found = true
+			break
+		}
+	}
+
+	require.True(t, found)
+	require.IsType(t, s, ns.Context())
+}
--- a/tpl/tplimpl/template_funcs.go
+++ b/tpl/tplimpl/template_funcs.go
@@ -30,6 +30,7 @@
 	_ "github.com/gohugoio/hugo/tpl/data"
 	_ "github.com/gohugoio/hugo/tpl/encoding"
 	_ "github.com/gohugoio/hugo/tpl/fmt"
+	_ "github.com/gohugoio/hugo/tpl/hugo"
 	_ "github.com/gohugoio/hugo/tpl/images"
 	_ "github.com/gohugoio/hugo/tpl/inflect"
 	_ "github.com/gohugoio/hugo/tpl/lang"
@@ -39,6 +40,7 @@
 	_ "github.com/gohugoio/hugo/tpl/path"
 	_ "github.com/gohugoio/hugo/tpl/resources"
 	_ "github.com/gohugoio/hugo/tpl/safe"
+	_ "github.com/gohugoio/hugo/tpl/site"
 	_ "github.com/gohugoio/hugo/tpl/strings"
 	_ "github.com/gohugoio/hugo/tpl/templates"
 	_ "github.com/gohugoio/hugo/tpl/time"
--- a/tpl/tplimpl/template_funcs_test.go
+++ b/tpl/tplimpl/template_funcs_test.go
@@ -21,10 +21,12 @@
 	"testing"
 	"time"
 
+	"github.com/gohugoio/hugo/htesting"
+
+	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/gohugoio/hugo/common/loggers"
 	"github.com/gohugoio/hugo/config"
 	"github.com/gohugoio/hugo/deps"
-	"github.com/gohugoio/hugo/helpers"
 	"github.com/gohugoio/hugo/hugofs"
 	"github.com/gohugoio/hugo/i18n"
 	"github.com/gohugoio/hugo/langs"
@@ -57,6 +59,7 @@
 	l := langs.NewLanguage("en", cfg)
 	return deps.DepsCfg{
 		Language:            l,
+		Site:                htesting.NewTestHugoSite(),
 		Cfg:                 cfg,
 		Fs:                  hugofs.NewMem(l),
 		Logger:              logger,
@@ -98,7 +101,7 @@
 	data.Title = "**BatMan**"
 	data.Section = "blog"
 	data.Params = map[string]interface{}{"langCode": "en"}
-	data.Hugo = map[string]interface{}{"Version": helpers.MustParseHugoVersion("0.36.1").Version()}
+	data.Hugo = map[string]interface{}{"Version": hugo.MustParseVersion("0.36.1").Version()}
 
 	for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
 		ns := nsf(d)
--- a/transform/metainject/hugogenerator.go
+++ b/transform/metainject/hugogenerator.go
@@ -18,12 +18,13 @@
 	"fmt"
 	"regexp"
 
+	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/gohugoio/hugo/helpers"
 	"github.com/gohugoio/hugo/transform"
 )
 
 var metaTagsCheck = regexp.MustCompile(`(?i)<meta\s+name=['|"]?generator['|"]?`)
-var hugoGeneratorTag = fmt.Sprintf(`<meta name="generator" content="Hugo %s" />`, helpers.CurrentHugoVersion)
+var hugoGeneratorTag = fmt.Sprintf(`<meta name="generator" content="Hugo %s" />`, hugo.CurrentVersion)
 
 // HugoGenerator injects a meta generator tag for Hugo if none present.
 func HugoGenerator(ft transform.FromTo) error {