commands.go (11437B)
1 // Copyright 2019 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 commands 15 16 import ( 17 "fmt" 18 "os" 19 "time" 20 21 "github.com/gohugoio/hugo/common/hugo" 22 "github.com/gohugoio/hugo/common/loggers" 23 hpaths "github.com/gohugoio/hugo/common/paths" 24 "github.com/gohugoio/hugo/config" 25 "github.com/gohugoio/hugo/helpers" 26 "github.com/spf13/cobra" 27 ) 28 29 type commandsBuilder struct { 30 hugoBuilderCommon 31 32 commands []cmder 33 } 34 35 func newCommandsBuilder() *commandsBuilder { 36 return &commandsBuilder{} 37 } 38 39 func (b *commandsBuilder) addCommands(commands ...cmder) *commandsBuilder { 40 b.commands = append(b.commands, commands...) 41 return b 42 } 43 44 func (b *commandsBuilder) addAll() *commandsBuilder { 45 b.addCommands( 46 b.newServerCmd(), 47 newVersionCmd(), 48 newEnvCmd(), 49 b.newConfigCmd(), 50 b.newDeployCmd(), 51 b.newConvertCmd(), 52 b.newNewCmd(), 53 b.newListCmd(), 54 newImportCmd(), 55 newGenCmd(), 56 createReleaser(), 57 b.newModCmd(), 58 ) 59 60 return b 61 } 62 63 func (b *commandsBuilder) build() *hugoCmd { 64 h := b.newHugoCmd() 65 addCommands(h.getCommand(), b.commands...) 66 return h 67 } 68 69 func addCommands(root *cobra.Command, commands ...cmder) { 70 for _, command := range commands { 71 cmd := command.getCommand() 72 if cmd == nil { 73 continue 74 } 75 root.AddCommand(cmd) 76 } 77 } 78 79 type baseCmd struct { 80 cmd *cobra.Command 81 } 82 83 var _ commandsBuilderGetter = (*baseBuilderCmd)(nil) 84 85 // Used in tests. 86 type commandsBuilderGetter interface { 87 getCommandsBuilder() *commandsBuilder 88 } 89 90 type baseBuilderCmd struct { 91 *baseCmd 92 *commandsBuilder 93 } 94 95 func (b *baseBuilderCmd) getCommandsBuilder() *commandsBuilder { 96 return b.commandsBuilder 97 } 98 99 func (c *baseCmd) getCommand() *cobra.Command { 100 return c.cmd 101 } 102 103 func newBaseCmd(cmd *cobra.Command) *baseCmd { 104 return &baseCmd{cmd: cmd} 105 } 106 107 func (b *commandsBuilder) newBuilderCmd(cmd *cobra.Command) *baseBuilderCmd { 108 bcmd := &baseBuilderCmd{commandsBuilder: b, baseCmd: &baseCmd{cmd: cmd}} 109 bcmd.hugoBuilderCommon.handleFlags(cmd) 110 return bcmd 111 } 112 113 func (b *commandsBuilder) newBuilderBasicCmd(cmd *cobra.Command) *baseBuilderCmd { 114 bcmd := &baseBuilderCmd{commandsBuilder: b, baseCmd: &baseCmd{cmd: cmd}} 115 bcmd.hugoBuilderCommon.handleCommonBuilderFlags(cmd) 116 return bcmd 117 } 118 119 func (c *baseCmd) flagsToConfig(cfg config.Provider) { 120 initializeFlags(c.cmd, cfg) 121 } 122 123 type hugoCmd struct { 124 *baseBuilderCmd 125 126 // Need to get the sites once built. 127 c *commandeer 128 } 129 130 var _ cmder = (*nilCommand)(nil) 131 132 type nilCommand struct{} 133 134 func (c *nilCommand) getCommand() *cobra.Command { 135 return nil 136 } 137 138 func (c *nilCommand) flagsToConfig(cfg config.Provider) { 139 } 140 141 func (b *commandsBuilder) newHugoCmd() *hugoCmd { 142 cc := &hugoCmd{} 143 144 cc.baseBuilderCmd = b.newBuilderCmd(&cobra.Command{ 145 Use: "hugo", 146 Short: "hugo builds your site", 147 Long: `hugo is the main command, used to build your Hugo site. 148 149 Hugo is a Fast and Flexible Static Site Generator 150 built with love by spf13 and friends in Go. 151 152 Complete documentation is available at https://gohugo.io/.`, 153 RunE: func(cmd *cobra.Command, args []string) error { 154 defer cc.timeTrack(time.Now(), "Total") 155 cfgInit := func(c *commandeer) error { 156 if cc.buildWatch { 157 c.Set("disableLiveReload", true) 158 } 159 return nil 160 } 161 162 // prevent cobra printing error so it can be handled here (before the timeTrack prints) 163 cmd.SilenceErrors = true 164 165 c, err := initializeConfig(true, true, cc.buildWatch, &cc.hugoBuilderCommon, cc, cfgInit) 166 if err != nil { 167 cmd.PrintErrln("Error:", err.Error()) 168 return err 169 } 170 cc.c = c 171 172 err = c.build() 173 if err != nil { 174 cmd.PrintErrln("Error:", err.Error()) 175 } 176 return err 177 }, 178 }) 179 180 cc.cmd.PersistentFlags().StringVar(&cc.cfgFile, "config", "", "config file (default is path/config.yaml|json|toml)") 181 cc.cmd.PersistentFlags().StringVar(&cc.cfgDir, "configDir", "config", "config dir") 182 cc.cmd.PersistentFlags().BoolVar(&cc.quiet, "quiet", false, "build in quiet mode") 183 184 // Set bash-completion 185 _ = cc.cmd.PersistentFlags().SetAnnotation("config", cobra.BashCompFilenameExt, config.ValidConfigFileExtensions) 186 187 cc.cmd.PersistentFlags().BoolVarP(&cc.verbose, "verbose", "v", false, "verbose output") 188 cc.cmd.PersistentFlags().BoolVarP(&cc.debug, "debug", "", false, "debug output") 189 cc.cmd.PersistentFlags().BoolVar(&cc.logging, "log", false, "enable Logging") 190 cc.cmd.PersistentFlags().StringVar(&cc.logFile, "logFile", "", "log File path (if set, logging enabled automatically)") 191 cc.cmd.PersistentFlags().BoolVar(&cc.verboseLog, "verboseLog", false, "verbose logging") 192 193 cc.cmd.Flags().BoolVarP(&cc.buildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed") 194 195 cc.cmd.Flags().Bool("renderToMemory", false, "render to memory (only useful for benchmark testing)") 196 197 // Set bash-completion 198 _ = cc.cmd.PersistentFlags().SetAnnotation("logFile", cobra.BashCompFilenameExt, []string{}) 199 200 cc.cmd.SetGlobalNormalizationFunc(helpers.NormalizeHugoFlags) 201 cc.cmd.SilenceUsage = true 202 203 return cc 204 } 205 206 type hugoBuilderCommon struct { 207 source string 208 baseURL string 209 environment string 210 211 buildWatch bool 212 poll string 213 clock string 214 215 gc bool 216 217 // Profile flags (for debugging of performance problems) 218 cpuprofile string 219 memprofile string 220 mutexprofile string 221 traceprofile string 222 printm bool 223 224 // TODO(bep) var vs string 225 logging bool 226 verbose bool 227 verboseLog bool 228 debug bool 229 quiet bool 230 231 cfgFile string 232 cfgDir string 233 logFile string 234 } 235 236 func (cc *hugoBuilderCommon) timeTrack(start time.Time, name string) { 237 if cc.quiet { 238 return 239 } 240 elapsed := time.Since(start) 241 fmt.Printf("%s in %v ms\n", name, int(1000*elapsed.Seconds())) 242 } 243 244 func (cc *hugoBuilderCommon) getConfigDir(baseDir string) string { 245 if cc.cfgDir != "" { 246 return hpaths.AbsPathify(baseDir, cc.cfgDir) 247 } 248 249 if v, found := os.LookupEnv("HUGO_CONFIGDIR"); found { 250 return hpaths.AbsPathify(baseDir, v) 251 } 252 253 return hpaths.AbsPathify(baseDir, "config") 254 } 255 256 func (cc *hugoBuilderCommon) getEnvironment(isServer bool) string { 257 if cc.environment != "" { 258 return cc.environment 259 } 260 261 if v, found := os.LookupEnv("HUGO_ENVIRONMENT"); found { 262 return v 263 } 264 265 // Used by Netlify and Forestry 266 if v, found := os.LookupEnv("HUGO_ENV"); found { 267 return v 268 } 269 270 if isServer { 271 return hugo.EnvironmentDevelopment 272 } 273 274 return hugo.EnvironmentProduction 275 } 276 277 func (cc *hugoBuilderCommon) handleCommonBuilderFlags(cmd *cobra.Command) { 278 cmd.PersistentFlags().StringVarP(&cc.source, "source", "s", "", "filesystem path to read files relative from") 279 cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{}) 280 cmd.PersistentFlags().StringVarP(&cc.environment, "environment", "e", "", "build environment") 281 cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory") 282 cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern") 283 cmd.PersistentFlags().StringVar(&cc.clock, "clock", "", "set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00") 284 } 285 286 func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) { 287 cc.handleCommonBuilderFlags(cmd) 288 cmd.Flags().Bool("cleanDestinationDir", false, "remove files from destination not found in static directories") 289 cmd.Flags().BoolP("buildDrafts", "D", false, "include content marked as draft") 290 cmd.Flags().BoolP("buildFuture", "F", false, "include content with publishdate in the future") 291 cmd.Flags().BoolP("buildExpired", "E", false, "include expired content") 292 cmd.Flags().StringP("contentDir", "c", "", "filesystem path to content directory") 293 cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory") 294 cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/") 295 cmd.Flags().BoolP("ignoreCache", "", false, "ignores the cache directory") 296 cmd.Flags().StringP("destination", "d", "", "filesystem path to write files to") 297 cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)") 298 cmd.Flags().StringVarP(&cc.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. https://spf13.com/") 299 cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date, author, and CODEOWNERS info to the pages") 300 cmd.Flags().BoolVar(&cc.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build") 301 cmd.Flags().StringVar(&cc.poll, "poll", "", "set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes") 302 cmd.Flags().BoolVar(&loggers.PanicOnWarning, "panicOnWarning", false, "panic on first WARNING log") 303 cmd.Flags().Bool("templateMetrics", false, "display metrics about template executions") 304 cmd.Flags().Bool("templateMetricsHints", false, "calculate some improvement hints when combined with --templateMetrics") 305 cmd.Flags().BoolP("forceSyncStatic", "", false, "copy all files when static is changed.") 306 cmd.Flags().BoolP("noTimes", "", false, "don't sync modification time of files") 307 cmd.Flags().BoolP("noChmod", "", false, "don't sync permission mode of files") 308 cmd.Flags().BoolP("noBuildLock", "", false, "don't create .hugo_build.lock file") 309 cmd.Flags().BoolP("printI18nWarnings", "", false, "print missing translations") 310 cmd.Flags().BoolP("printPathWarnings", "", false, "print warnings on duplicate target paths etc.") 311 cmd.Flags().BoolP("printUnusedTemplates", "", false, "print warnings on unused templates.") 312 cmd.Flags().StringVarP(&cc.cpuprofile, "profile-cpu", "", "", "write cpu profile to `file`") 313 cmd.Flags().StringVarP(&cc.memprofile, "profile-mem", "", "", "write memory profile to `file`") 314 cmd.Flags().BoolVarP(&cc.printm, "printMemoryUsage", "", false, "print memory usage to screen at intervals") 315 cmd.Flags().StringVarP(&cc.mutexprofile, "profile-mutex", "", "", "write Mutex profile to `file`") 316 cmd.Flags().StringVarP(&cc.traceprofile, "trace", "", "", "write trace to `file` (not useful in general)") 317 318 // Hide these for now. 319 cmd.Flags().MarkHidden("profile-cpu") 320 cmd.Flags().MarkHidden("profile-mem") 321 cmd.Flags().MarkHidden("profile-mutex") 322 323 cmd.Flags().StringSlice("disableKinds", []string{}, "disable different kind of pages (home, RSS etc.)") 324 325 cmd.Flags().Bool("minify", false, "minify any supported output format (HTML, XML etc.)") 326 327 // Set bash-completion. 328 // Each flag must first be defined before using the SetAnnotation() call. 329 _ = cmd.Flags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{}) 330 _ = cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{}) 331 _ = cmd.Flags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{}) 332 _ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"}) 333 } 334 335 func checkErr(logger loggers.Logger, err error, s ...string) { 336 if err == nil { 337 return 338 } 339 for _, message := range s { 340 logger.Errorln(message) 341 } 342 logger.Errorln(err) 343 }