hugo.go (5932B)
1 // Copyright 2018 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package hugo
15
16 import (
17 "fmt"
18 "html/template"
19 "os"
20 "path/filepath"
21 "runtime/debug"
22 "sort"
23 "strings"
24 "sync"
25 "time"
26
27 "github.com/gohugoio/hugo/hugofs/files"
28
29 "github.com/spf13/afero"
30
31 "github.com/gohugoio/hugo/config"
32 "github.com/gohugoio/hugo/hugofs"
33 )
34
35 const (
36 EnvironmentDevelopment = "development"
37 EnvironmentProduction = "production"
38 )
39
40 var (
41 // vendorInfo contains vendor notes about the current build.
42 vendorInfo string
43 )
44
45 // Info contains information about the current Hugo environment
46 type Info struct {
47 CommitHash string
48 BuildDate string
49
50 // The build environment.
51 // Defaults are "production" (hugo) and "development" (hugo server).
52 // This can also be set by the user.
53 // It can be any string, but it will be all lower case.
54 Environment string
55
56 // version of go that the Hugo binary was built with
57 GoVersion string
58
59 deps []*Dependency
60 }
61
62 // Version returns the current version as a comparable version string.
63 func (i Info) Version() VersionString {
64 return CurrentVersion.Version()
65 }
66
67 // Generator a Hugo meta generator HTML tag.
68 func (i Info) Generator() template.HTML {
69 return template.HTML(fmt.Sprintf(`<meta name="generator" content="Hugo %s" />`, CurrentVersion.String()))
70 }
71
72 func (i Info) IsProduction() bool {
73 return i.Environment == EnvironmentProduction
74 }
75
76 func (i Info) IsExtended() bool {
77 return IsExtended
78 }
79
80 // Deps gets a list of dependencies for this Hugo build.
81 func (i Info) Deps() []*Dependency {
82 return i.deps
83 }
84
85 // NewInfo creates a new Hugo Info object.
86 func NewInfo(environment string, deps []*Dependency) Info {
87 if environment == "" {
88 environment = EnvironmentProduction
89 }
90 var (
91 commitHash string
92 buildDate string
93 goVersion string
94 )
95
96 bi := getBuildInfo()
97 if bi != nil {
98 commitHash = bi.Revision
99 buildDate = bi.RevisionTime
100 goVersion = bi.GoVersion
101 }
102
103 return Info{
104 CommitHash: commitHash,
105 BuildDate: buildDate,
106 Environment: environment,
107 deps: deps,
108 GoVersion: goVersion,
109 }
110 }
111
112 // GetExecEnviron creates and gets the common os/exec environment used in the
113 // external programs we interact with via os/exec, e.g. postcss.
114 func GetExecEnviron(workDir string, cfg config.Provider, fs afero.Fs) []string {
115 var env []string
116 nodepath := filepath.Join(workDir, "node_modules")
117 if np := os.Getenv("NODE_PATH"); np != "" {
118 nodepath = workDir + string(os.PathListSeparator) + np
119 }
120 config.SetEnvVars(&env, "NODE_PATH", nodepath)
121 config.SetEnvVars(&env, "PWD", workDir)
122 config.SetEnvVars(&env, "HUGO_ENVIRONMENT", cfg.GetString("environment"))
123 config.SetEnvVars(&env, "HUGO_ENV", cfg.GetString("environment"))
124
125 if fs != nil {
126 fis, err := afero.ReadDir(fs, files.FolderJSConfig)
127 if err == nil {
128 for _, fi := range fis {
129 key := fmt.Sprintf("HUGO_FILE_%s", strings.ReplaceAll(strings.ToUpper(fi.Name()), ".", "_"))
130 value := fi.(hugofs.FileMetaInfo).Meta().Filename
131 config.SetEnvVars(&env, key, value)
132 }
133 }
134 }
135
136 return env
137 }
138
139 type buildInfo struct {
140 VersionControlSystem string
141 Revision string
142 RevisionTime string
143 Modified bool
144
145 GoOS string
146 GoArch string
147
148 *debug.BuildInfo
149 }
150
151 var bInfo *buildInfo
152 var bInfoInit sync.Once
153
154 func getBuildInfo() *buildInfo {
155 bInfoInit.Do(func() {
156 bi, ok := debug.ReadBuildInfo()
157 if !ok {
158 return
159 }
160
161 bInfo = &buildInfo{BuildInfo: bi}
162
163 for _, s := range bInfo.Settings {
164 switch s.Key {
165 case "vcs":
166 bInfo.VersionControlSystem = s.Value
167 case "vcs.revision":
168 bInfo.Revision = s.Value
169 case "vcs.time":
170 bInfo.RevisionTime = s.Value
171 case "vcs.modified":
172 bInfo.Modified = s.Value == "true"
173 case "GOOS":
174 bInfo.GoOS = s.Value
175 case "GOARCH":
176 bInfo.GoArch = s.Value
177 }
178 }
179
180 })
181
182 return bInfo
183 }
184
185 // GetDependencyList returns a sorted dependency list on the format package="version".
186 // It includes both Go dependencies and (a manually maintained) list of C(++) dependencies.
187 func GetDependencyList() []string {
188 var deps []string
189
190 formatDep := func(path, version string) string {
191 return fmt.Sprintf("%s=%q", path, version)
192 }
193
194 if IsExtended {
195 deps = append(
196 deps,
197 // TODO(bep) consider adding a DepsNonGo() method to these upstream projects.
198 formatDep("github.com/sass/libsass", "3.6.5"),
199 formatDep("github.com/webmproject/libwebp", "v1.2.0"),
200 )
201 }
202
203 bi := getBuildInfo()
204 if bi == nil {
205 return deps
206 }
207
208 for _, dep := range bi.Deps {
209 deps = append(deps, formatDep(dep.Path, dep.Version))
210 }
211
212 sort.Strings(deps)
213
214 return deps
215 }
216
217 // IsRunningAsTest reports whether we are running as a test.
218 func IsRunningAsTest() bool {
219 for _, arg := range os.Args {
220 if strings.HasPrefix(arg, "-test") {
221 return true
222 }
223 }
224 return false
225 }
226
227 // Dependency is a single dependency, which can be either a Hugo Module or a local theme.
228 type Dependency struct {
229 // Returns the path to this module.
230 // This will either be the module path, e.g. "github.com/gohugoio/myshortcodes",
231 // or the path below your /theme folder, e.g. "mytheme".
232 Path string
233
234 // The module version.
235 Version string
236
237 // Whether this dependency is vendored.
238 Vendor bool
239
240 // Time version was created.
241 Time time.Time
242
243 // In the dependency tree, this is the first module that defines this module
244 // as a dependency.
245 Owner *Dependency
246
247 // Replaced by this dependency.
248 Replace *Dependency
249 }