hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

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 }