github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/common/hugo/hugo.go (about) 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/bep/godartsass" 28 "github.com/gohugoio/hugo/common/hexec" 29 "github.com/gohugoio/hugo/hugofs/files" 30 31 "github.com/spf13/afero" 32 33 "github.com/gohugoio/hugo/config" 34 "github.com/gohugoio/hugo/hugofs" 35 ) 36 37 const ( 38 EnvironmentDevelopment = "development" 39 EnvironmentProduction = "production" 40 ) 41 42 var ( 43 // buildDate allows vendor-specified build date when .git/ is unavailable. 44 buildDate string 45 // vendorInfo contains vendor notes about the current build. 46 vendorInfo string 47 ) 48 49 // Info contains information about the current Hugo environment 50 type Info struct { 51 CommitHash string 52 BuildDate string 53 54 // The build environment. 55 // Defaults are "production" (hugo) and "development" (hugo server). 56 // This can also be set by the user. 57 // It can be any string, but it will be all lower case. 58 Environment string 59 60 // version of go that the Hugo binary was built with 61 GoVersion string 62 63 deps []*Dependency 64 } 65 66 // Version returns the current version as a comparable version string. 67 func (i Info) Version() VersionString { 68 return CurrentVersion.Version() 69 } 70 71 // Generator a Hugo meta generator HTML tag. 72 func (i Info) Generator() template.HTML { 73 return template.HTML(fmt.Sprintf(`<meta name="generator" content="Hugo %s">`, CurrentVersion.String())) 74 } 75 76 func (i Info) IsProduction() bool { 77 return i.Environment == EnvironmentProduction 78 } 79 80 func (i Info) IsExtended() bool { 81 return IsExtended 82 } 83 84 // Deps gets a list of dependencies for this Hugo build. 85 func (i Info) Deps() []*Dependency { 86 return i.deps 87 } 88 89 // NewInfo creates a new Hugo Info object. 90 func NewInfo(environment string, deps []*Dependency) Info { 91 if environment == "" { 92 environment = EnvironmentProduction 93 } 94 var ( 95 commitHash string 96 buildDate string 97 goVersion string 98 ) 99 100 bi := getBuildInfo() 101 if bi != nil { 102 commitHash = bi.Revision 103 buildDate = bi.RevisionTime 104 goVersion = bi.GoVersion 105 } 106 107 return Info{ 108 CommitHash: commitHash, 109 BuildDate: buildDate, 110 Environment: environment, 111 deps: deps, 112 GoVersion: goVersion, 113 } 114 } 115 116 // GetExecEnviron creates and gets the common os/exec environment used in the 117 // external programs we interact with via os/exec, e.g. postcss. 118 func GetExecEnviron(workDir string, cfg config.Provider, fs afero.Fs) []string { 119 var env []string 120 nodepath := filepath.Join(workDir, "node_modules") 121 if np := os.Getenv("NODE_PATH"); np != "" { 122 nodepath = workDir + string(os.PathListSeparator) + np 123 } 124 config.SetEnvVars(&env, "NODE_PATH", nodepath) 125 config.SetEnvVars(&env, "PWD", workDir) 126 config.SetEnvVars(&env, "HUGO_ENVIRONMENT", cfg.GetString("environment")) 127 config.SetEnvVars(&env, "HUGO_ENV", cfg.GetString("environment")) 128 129 config.SetEnvVars(&env, "HUGO_PUBLISHDIR", filepath.Join(workDir, cfg.GetString("publishDirOrig"))) 130 131 if fs != nil { 132 fis, err := afero.ReadDir(fs, files.FolderJSConfig) 133 if err == nil { 134 for _, fi := range fis { 135 key := fmt.Sprintf("HUGO_FILE_%s", strings.ReplaceAll(strings.ToUpper(fi.Name()), ".", "_")) 136 value := fi.(hugofs.FileMetaInfo).Meta().Filename 137 config.SetEnvVars(&env, key, value) 138 } 139 } 140 } 141 142 return env 143 } 144 145 type buildInfo struct { 146 VersionControlSystem string 147 Revision string 148 RevisionTime string 149 Modified bool 150 151 GoOS string 152 GoArch string 153 154 *debug.BuildInfo 155 } 156 157 var bInfo *buildInfo 158 var bInfoInit sync.Once 159 160 func getBuildInfo() *buildInfo { 161 bInfoInit.Do(func() { 162 bi, ok := debug.ReadBuildInfo() 163 if !ok { 164 return 165 } 166 167 bInfo = &buildInfo{BuildInfo: bi} 168 169 for _, s := range bInfo.Settings { 170 switch s.Key { 171 case "vcs": 172 bInfo.VersionControlSystem = s.Value 173 case "vcs.revision": 174 bInfo.Revision = s.Value 175 case "vcs.time": 176 bInfo.RevisionTime = s.Value 177 case "vcs.modified": 178 bInfo.Modified = s.Value == "true" 179 case "GOOS": 180 bInfo.GoOS = s.Value 181 case "GOARCH": 182 bInfo.GoArch = s.Value 183 } 184 } 185 186 }) 187 188 return bInfo 189 } 190 191 func formatDep(path, version string) string { 192 return fmt.Sprintf("%s=%q", path, version) 193 } 194 195 // GetDependencyList returns a sorted dependency list on the format package="version". 196 // It includes both Go dependencies and (a manually maintained) list of C(++) dependencies. 197 func GetDependencyList() []string { 198 var deps []string 199 200 bi := getBuildInfo() 201 if bi == nil { 202 return deps 203 } 204 205 for _, dep := range bi.Deps { 206 deps = append(deps, formatDep(dep.Path, dep.Version)) 207 } 208 209 deps = append(deps, GetDependencyListNonGo()...) 210 211 sort.Strings(deps) 212 213 return deps 214 } 215 216 // GetDependencyListNonGo returns a list of non-Go dependencies. 217 func GetDependencyListNonGo() []string { 218 var deps []string 219 220 if IsExtended { 221 deps = append( 222 deps, 223 formatDep("github.com/sass/libsass", "3.6.5"), 224 formatDep("github.com/webmproject/libwebp", "v1.2.4"), 225 ) 226 } 227 228 if dartSass := dartSassVersion(); dartSass.ProtocolVersion != "" { 229 const dartSassPath = "github.com/sass/dart-sass-embedded" 230 deps = append(deps, 231 formatDep(dartSassPath+"/protocol", dartSass.ProtocolVersion), 232 formatDep(dartSassPath+"/compiler", dartSass.CompilerVersion), 233 formatDep(dartSassPath+"/implementation", dartSass.ImplementationVersion), 234 ) 235 } 236 return deps 237 } 238 239 // IsRunningAsTest reports whether we are running as a test. 240 func IsRunningAsTest() bool { 241 for _, arg := range os.Args { 242 if strings.HasPrefix(arg, "-test") { 243 return true 244 } 245 } 246 return false 247 } 248 249 // Dependency is a single dependency, which can be either a Hugo Module or a local theme. 250 type Dependency struct { 251 // Returns the path to this module. 252 // This will either be the module path, e.g. "github.com/gohugoio/myshortcodes", 253 // or the path below your /theme folder, e.g. "mytheme". 254 Path string 255 256 // The module version. 257 Version string 258 259 // Whether this dependency is vendored. 260 Vendor bool 261 262 // Time version was created. 263 Time time.Time 264 265 // In the dependency tree, this is the first module that defines this module 266 // as a dependency. 267 Owner *Dependency 268 269 // Replaced by this dependency. 270 Replace *Dependency 271 } 272 273 func dartSassVersion() godartsass.DartSassVersion { 274 // This is also duplicated in the dartsass package. 275 const dartSassEmbeddedBinaryName = "dart-sass-embedded" 276 if !hexec.InPath(dartSassEmbeddedBinaryName) { 277 return godartsass.DartSassVersion{} 278 } 279 v, _ := godartsass.Version(dartSassEmbeddedBinaryName) 280 return v 281 }