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 }