github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/commands/commands.go (about)

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