mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
Improve error handling in commands
Cobra, the CLI commander in use in Hugo, has some long awaited improvements in the error handling department. This enables a more centralized error handling approach. This commit introduces that by changing all the command funcs to `RunE`: * The core part of the error logging, usage logging and `os.Exit(-1)` is now performed in one place and that one place only. * The usage text is now only shown on invalid arguments etc. (user errors) Fixes #1502
This commit is contained in:
parent
6959b7fa80
commit
3f0f7eed68
17 changed files with 219 additions and 155 deletions
|
@ -28,9 +28,12 @@ var benchmark = &cobra.Command{
|
|||
Short: "Benchmark hugo by building a site a number of times.",
|
||||
Long: `Hugo can build a site many times over and analyze the running process
|
||||
creating a benchmark.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
InitializeConfig()
|
||||
bench(cmd, args)
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := InitializeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bench(cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -41,13 +44,13 @@ func init() {
|
|||
benchmark.Flags().IntVarP(&benchmarkTimes, "count", "n", 13, "number of times to build the site")
|
||||
}
|
||||
|
||||
func bench(cmd *cobra.Command, args []string) {
|
||||
func bench(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if memProfilefile != "" {
|
||||
f, err := os.Create(memProfilefile)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
for i := 0; i < benchmarkTimes; i++ {
|
||||
_ = buildSite()
|
||||
|
@ -62,7 +65,7 @@ func bench(cmd *cobra.Command, args []string) {
|
|||
f, err := os.Create(cpuProfilefile)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
|
||||
pprof.StartCPUProfile(f)
|
||||
|
@ -72,4 +75,6 @@ func bench(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
|
|
@ -23,9 +23,13 @@ var check = &cobra.Command{
|
|||
Short: "Check content in the source directory",
|
||||
Long: `Hugo will perform some basic analysis on the content provided
|
||||
and will give feedback.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
InitializeConfig()
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := InitializeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
site := hugolib.Site{}
|
||||
site.Analyze()
|
||||
|
||||
return site.Analyze()
|
||||
|
||||
},
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ var convertCmd = &cobra.Command{
|
|||
Long: `Convert your content (e.g. front matter) to different formats.
|
||||
|
||||
See convert's subcommands toJSON, toTOML and toYAML for more information.`,
|
||||
Run: nil,
|
||||
RunE: nil,
|
||||
}
|
||||
|
||||
var toJSONCmd = &cobra.Command{
|
||||
|
@ -44,11 +44,8 @@ var toJSONCmd = &cobra.Command{
|
|||
Short: "Convert front matter to JSON",
|
||||
Long: `toJSON converts all front matter in the content directory
|
||||
to use JSON for the front matter.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := convertContents(rune([]byte(parser.JSON_LEAD)[0]))
|
||||
if err != nil {
|
||||
jww.ERROR.Println(err)
|
||||
}
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return convertContents(rune([]byte(parser.JSON_LEAD)[0]))
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -57,11 +54,8 @@ var toTOMLCmd = &cobra.Command{
|
|||
Short: "Convert front matter to TOML",
|
||||
Long: `toTOML converts all front matter in the content directory
|
||||
to use TOML for the front matter.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := convertContents(rune([]byte(parser.TOML_LEAD)[0]))
|
||||
if err != nil {
|
||||
jww.ERROR.Println(err)
|
||||
}
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return convertContents(rune([]byte(parser.TOML_LEAD)[0]))
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -70,11 +64,8 @@ var toYAMLCmd = &cobra.Command{
|
|||
Short: "Convert front matter to YAML",
|
||||
Long: `toYAML converts all front matter in the content directory
|
||||
to use YAML for the front matter.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := convertContents(rune([]byte(parser.YAML_LEAD)[0]))
|
||||
if err != nil {
|
||||
jww.ERROR.Println(err)
|
||||
}
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return convertContents(rune([]byte(parser.YAML_LEAD)[0]))
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -87,7 +78,9 @@ func init() {
|
|||
}
|
||||
|
||||
func convertContents(mark rune) (err error) {
|
||||
InitializeConfig()
|
||||
if err := InitializeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
site := &hugolib.Site{}
|
||||
|
||||
if err := site.Initialise(); err != nil {
|
||||
|
|
|
@ -31,16 +31,19 @@ or just source them in directly:
|
|||
|
||||
$ . /etc/bash_completion`,
|
||||
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if autocompleteType != "bash" {
|
||||
jww.FATAL.Fatalln("Only Bash is supported for now")
|
||||
return newUserError("Only Bash is supported for now")
|
||||
}
|
||||
|
||||
err := cmd.Root().GenBashCompletionFile(autocompleteTarget)
|
||||
|
||||
if err != nil {
|
||||
jww.FATAL.Fatalln("Failed to generate shell completion file:", err)
|
||||
return err
|
||||
} else {
|
||||
jww.FEEDBACK.Println("Bash completion file for Hugo saved to", autocompleteTarget)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ of Hugo's command-line interface for http://gohugo.io/.
|
|||
It creates one Markdown file per command with front matter suitable
|
||||
for rendering in Hugo.`,
|
||||
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !strings.HasSuffix(gendocdir, helpers.FilePathSeparator) {
|
||||
gendocdir += helpers.FilePathSeparator
|
||||
}
|
||||
|
@ -55,6 +55,8 @@ for rendering in Hugo.`,
|
|||
jww.FEEDBACK.Println("Generating Hugo command-line documentation in", gendocdir, "...")
|
||||
cobra.GenMarkdownTreeCustom(cmd.Root(), gendocdir, prepender, linkHandler)
|
||||
jww.FEEDBACK.Println("Done.")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ var genmanCmd = &cobra.Command{
|
|||
command-line interface. By default, it creates the man page files
|
||||
in the "man" directory under the current directory.`,
|
||||
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
header := &cobra.GenManHeader{
|
||||
Section: "1",
|
||||
Manual: "Hugo Manual",
|
||||
|
@ -37,6 +37,8 @@ in the "man" directory under the current directory.`,
|
|||
cmd.Root().GenManTree(header, genmandir)
|
||||
|
||||
jww.FEEDBACK.Println("Done.")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -40,8 +40,44 @@ import (
|
|||
"github.com/spf13/nitro"
|
||||
"github.com/spf13/viper"
|
||||
"gopkg.in/fsnotify.v1"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// userError is an error used to signal different error situations in command handling.
|
||||
type commandError struct {
|
||||
s string
|
||||
userError bool
|
||||
}
|
||||
|
||||
func (u commandError) Error() string {
|
||||
return u.s
|
||||
}
|
||||
|
||||
func (u commandError) isUserError() bool {
|
||||
return u.userError
|
||||
}
|
||||
|
||||
func newUserError(messages ...interface{}) commandError {
|
||||
return commandError{s: fmt.Sprintln(messages...), userError: true}
|
||||
}
|
||||
|
||||
func newSystemError(messages ...interface{}) commandError {
|
||||
return commandError{s: fmt.Sprintln(messages...), userError: false}
|
||||
}
|
||||
|
||||
// catch some of the obvious user errors from Cobra.
|
||||
// We don't want to show the usage message for every error.
|
||||
// The below may be to generic. Time will show.
|
||||
var userErrorRegexp = regexp.MustCompile("argument|flag|shorthand")
|
||||
|
||||
func isUserError(err error) bool {
|
||||
if cErr, ok := err.(commandError); ok && cErr.isUserError() {
|
||||
return true
|
||||
}
|
||||
|
||||
return userErrorRegexp.MatchString(err.Error())
|
||||
}
|
||||
|
||||
//HugoCmd is Hugo's root command. Every other command attached to HugoCmd is a child command to it.
|
||||
var HugoCmd = &cobra.Command{
|
||||
Use: "hugo",
|
||||
|
@ -52,10 +88,15 @@ Hugo is a Fast and Flexible Static Site Generator
|
|||
built with love by spf13 and friends in Go.
|
||||
|
||||
Complete documentation is available at http://gohugo.io/.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
InitializeConfig()
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := InitializeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
watchConfig()
|
||||
build()
|
||||
|
||||
return build()
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -68,9 +109,17 @@ var Source, CacheDir, Destination, Theme, BaseURL, CfgFile, LogFile, Editor stri
|
|||
//Execute adds all child commands to the root command HugoCmd and sets flags appropriately.
|
||||
func Execute() {
|
||||
HugoCmd.SetGlobalNormalizationFunc(helpers.NormalizeHugoFlags)
|
||||
|
||||
HugoCmd.SilenceUsage = true
|
||||
|
||||
AddCommands()
|
||||
if err := HugoCmd.Execute(); err != nil {
|
||||
// the err is already logged by Cobra
|
||||
|
||||
if c, err := HugoCmd.ExecuteC(); err != nil {
|
||||
if isUserError(err) {
|
||||
c.Println("")
|
||||
c.Println(c.UsageString())
|
||||
}
|
||||
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +233,7 @@ func LoadDefaultSettings() {
|
|||
}
|
||||
|
||||
// InitializeConfig initializes a config file with sensible default configuration flags.
|
||||
func InitializeConfig() {
|
||||
func InitializeConfig() error {
|
||||
viper.SetConfigFile(CfgFile)
|
||||
// See https://github.com/spf13/viper/issues/73#issuecomment-126970794
|
||||
if Source == "" {
|
||||
|
@ -195,9 +244,9 @@ func InitializeConfig() {
|
|||
err := viper.ReadInConfig()
|
||||
if err != nil {
|
||||
if _, ok := err.(viper.ConfigParseError); ok {
|
||||
jww.ERROR.Println(err)
|
||||
return newSystemError(err)
|
||||
} else {
|
||||
jww.ERROR.Println("Unable to locate Config file. Perhaps you need to create a new site. Run `hugo help new` for details", err)
|
||||
return newSystemError("Unable to locate Config file. Perhaps you need to create a new site. Run `hugo help new` for details", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,7 +369,7 @@ func InitializeConfig() {
|
|||
themeDir := helpers.GetThemeDir()
|
||||
if themeDir != "" {
|
||||
if _, err := os.Stat(themeDir); os.IsNotExist(err) {
|
||||
jww.FATAL.Fatalln("Unable to find theme Directory:", themeDir)
|
||||
return newSystemError("Unable to find theme Directory:", themeDir)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,6 +379,8 @@ func InitializeConfig() {
|
|||
jww.ERROR.Printf("Current theme does not support Hugo version %s. Minimum version required is %s\n",
|
||||
helpers.HugoReleaseVersion(), minVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func watchConfig() {
|
||||
|
@ -344,23 +395,26 @@ func watchConfig() {
|
|||
})
|
||||
}
|
||||
|
||||
func build(watches ...bool) {
|
||||
err := copyStatic()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
utils.StopOnErr(err, fmt.Sprintf("Error copying static files to %s", helpers.AbsPathify(viper.GetString("PublishDir"))))
|
||||
func build(watches ...bool) error {
|
||||
|
||||
if err := copyStatic(); err != nil {
|
||||
return fmt.Errorf("Error copying static files to %s: %s", helpers.AbsPathify(viper.GetString("PublishDir")), err)
|
||||
}
|
||||
watch := false
|
||||
if len(watches) > 0 && watches[0] {
|
||||
watch = true
|
||||
}
|
||||
utils.StopOnErr(buildSite(BuildWatch || watch))
|
||||
if err := buildSite(BuildWatch || watch); err != nil {
|
||||
return fmt.Errorf("Error building site: %s", err)
|
||||
}
|
||||
|
||||
if BuildWatch {
|
||||
jww.FEEDBACK.Println("Watching for changes in", helpers.AbsPathify(viper.GetString("ContentDir")))
|
||||
jww.FEEDBACK.Println("Press Ctrl+C to stop")
|
||||
utils.CheckErr(NewWatcher(0))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyStatic() error {
|
||||
|
@ -483,7 +537,6 @@ func NewWatcher(port int) error {
|
|||
var wg sync.WaitGroup
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var importCmd = &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Import your site from others.",
|
||||
Long: `Import your site from other web site generators like Jekyll.
|
||||
|
||||
Import requires a subcommand, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.",
|
||||
Run: nil,
|
||||
}
|
||||
|
||||
func init() {
|
||||
importCmd.AddCommand(importJekyllCmd)
|
||||
}
|
|
@ -35,34 +35,44 @@ import (
|
|||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
func init() {
|
||||
importCmd.AddCommand(importJekyllCmd)
|
||||
}
|
||||
|
||||
var importCmd = &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Import your site from others.",
|
||||
Long: `Import your site from other web site generators like Jekyll.
|
||||
|
||||
Import requires a subcommand, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.",
|
||||
RunE: nil,
|
||||
}
|
||||
|
||||
var importJekyllCmd = &cobra.Command{
|
||||
Use: "jekyll",
|
||||
Short: "hugo import from Jekyll",
|
||||
Long: `hugo import from Jekyll.
|
||||
|
||||
Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.",
|
||||
Run: importFromJekyll,
|
||||
RunE: importFromJekyll,
|
||||
}
|
||||
|
||||
func importFromJekyll(cmd *cobra.Command, args []string) {
|
||||
func importFromJekyll(cmd *cobra.Command, args []string) error {
|
||||
jww.SetLogThreshold(jww.LevelTrace)
|
||||
jww.SetStdoutThreshold(jww.LevelWarn)
|
||||
|
||||
if len(args) < 2 {
|
||||
jww.ERROR.Println(`Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.")
|
||||
return
|
||||
return newUserError(`Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.")
|
||||
}
|
||||
|
||||
jekyllRoot, err := filepath.Abs(filepath.Clean(args[0]))
|
||||
if err != nil {
|
||||
jww.ERROR.Println("Path error:", args[0])
|
||||
return
|
||||
return newUserError("Path error:", args[0])
|
||||
}
|
||||
|
||||
targetDir, err := filepath.Abs(filepath.Clean(args[1]))
|
||||
if err != nil {
|
||||
jww.ERROR.Println("Path error:", args[1])
|
||||
return
|
||||
return newUserError("Path error:", args[1])
|
||||
}
|
||||
|
||||
createSiteFromJekyll(jekyllRoot, targetDir)
|
||||
|
@ -82,8 +92,7 @@ func importFromJekyll(cmd *cobra.Command, args []string) {
|
|||
|
||||
relPath, err := filepath.Rel(jekyllRoot, path)
|
||||
if err != nil {
|
||||
jww.ERROR.Println("Get rel path error:", path)
|
||||
return err
|
||||
return newUserError("Get rel path error:", path)
|
||||
}
|
||||
|
||||
relPath = filepath.ToSlash(relPath)
|
||||
|
@ -106,13 +115,15 @@ func importFromJekyll(cmd *cobra.Command, args []string) {
|
|||
err = filepath.Walk(jekyllRoot, callback)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
} else {
|
||||
fmt.Println("Congratulations!", fileCount, "posts imported!")
|
||||
fmt.Println("Now, start Hugo by yourself: \n" +
|
||||
"$ git clone https://github.com/spf13/herring-cove.git " + args[1] + "/themes/herring-cove")
|
||||
fmt.Println("$ cd " + args[1] + "\n$ hugo server -w --theme=herring-cove")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSiteFromJekyll(jekyllRoot, targetDir string) {
|
||||
|
|
|
@ -30,12 +30,13 @@ var limit = &cobra.Command{
|
|||
Short: "Check system ulimit settings",
|
||||
Long: `Hugo will inspect the current ulimit settings on the system.
|
||||
This is primarily to ensure that Hugo can watch enough files on some OSs`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var rLimit syscall.Rlimit
|
||||
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
jww.ERROR.Println("Error Getting Rlimit ", err)
|
||||
return newSystemError("Error Getting Rlimit ", err)
|
||||
}
|
||||
|
||||
jww.FEEDBACK.Println("Current rLimit:", rLimit)
|
||||
|
||||
jww.FEEDBACK.Println("Attempting to increase limit")
|
||||
|
@ -43,13 +44,15 @@ var limit = &cobra.Command{
|
|||
rLimit.Cur = 999999
|
||||
err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
jww.ERROR.Println("Error Setting rLimit ", err)
|
||||
return newSystemError("Error Setting rLimit ", err)
|
||||
}
|
||||
err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
jww.ERROR.Println("Error Getting rLimit ", err)
|
||||
return newSystemError("Error Getting rLimit ", err)
|
||||
}
|
||||
jww.FEEDBACK.Println("rLimit after change:", rLimit)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -33,22 +33,25 @@ var listCmd = &cobra.Command{
|
|||
Long: `Listing out various types of content.
|
||||
|
||||
List requires a subcommand, e.g. ` + "`hugo list drafts`.",
|
||||
Run: nil,
|
||||
RunE: nil,
|
||||
}
|
||||
|
||||
var listDraftsCmd = &cobra.Command{
|
||||
Use: "drafts",
|
||||
Short: "List all drafts",
|
||||
Long: `List all of the drafts in your content directory.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if err := InitializeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
InitializeConfig()
|
||||
viper.Set("BuildDrafts", true)
|
||||
|
||||
site := &hugolib.Site{}
|
||||
|
||||
if err := site.Process(); err != nil {
|
||||
fmt.Println("Error Processing Source Content", err)
|
||||
return newSystemError("Error Processing Source Content", err)
|
||||
}
|
||||
|
||||
for _, p := range site.Pages {
|
||||
|
@ -58,6 +61,8 @@ var listDraftsCmd = &cobra.Command{
|
|||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -66,15 +71,18 @@ var listFutureCmd = &cobra.Command{
|
|||
Short: "List all posts dated in the future",
|
||||
Long: `List all of the posts in your content directory which will be
|
||||
posted in the future.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if err := InitializeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
InitializeConfig()
|
||||
viper.Set("BuildFuture", true)
|
||||
|
||||
site := &hugolib.Site{}
|
||||
|
||||
if err := site.Process(); err != nil {
|
||||
fmt.Println("Error Processing Source Content", err)
|
||||
return newSystemError("Error Processing Source Content", err)
|
||||
}
|
||||
|
||||
for _, p := range site.Pages {
|
||||
|
@ -84,5 +92,7 @@ posted in the future.`,
|
|||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
},
|
||||
}
|
||||
|
|
|
@ -25,8 +25,11 @@ var config = &cobra.Command{
|
|||
Use: "config",
|
||||
Short: "Print the site configuration",
|
||||
Long: `Print the site configuration, both default and custom settings.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
InitializeConfig()
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := InitializeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allSettings := viper.AllSettings()
|
||||
|
||||
var separator string
|
||||
|
@ -49,5 +52,7 @@ var config = &cobra.Command{
|
|||
fmt.Printf("%s%s%+v\n", k, separator, allSettings[k])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ You can also specify the kind with ` + "`-k KIND`" + `.
|
|||
|
||||
If archetypes are provided in your theme or site, they will be used.`,
|
||||
|
||||
Run: NewContent,
|
||||
RunE: NewContent,
|
||||
}
|
||||
|
||||
var newSiteCmd = &cobra.Command{
|
||||
|
@ -64,7 +64,7 @@ var newSiteCmd = &cobra.Command{
|
|||
Long: `Create a new site in the provided directory.
|
||||
The new site will have the correct structure, but no content or theme yet.
|
||||
Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
Run: NewSite,
|
||||
RunE: NewSite,
|
||||
}
|
||||
|
||||
var newThemeCmd = &cobra.Command{
|
||||
|
@ -74,20 +74,21 @@ var newThemeCmd = &cobra.Command{
|
|||
New theme is a skeleton. Please add content to the touched files. Add your
|
||||
name to the copyright line in the license and adjust the theme.toml file
|
||||
as you see fit.`,
|
||||
Run: NewTheme,
|
||||
RunE: NewTheme,
|
||||
}
|
||||
|
||||
// NewContent adds new content to a Hugo site.
|
||||
func NewContent(cmd *cobra.Command, args []string) {
|
||||
InitializeConfig()
|
||||
func NewContent(cmd *cobra.Command, args []string) error {
|
||||
if err := InitializeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd.Flags().Lookup("format").Changed {
|
||||
viper.Set("MetaDataFormat", configFormat)
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
cmd.Usage()
|
||||
jww.FATAL.Fatalln("path needs to be provided")
|
||||
return newUserError("path needs to be provided")
|
||||
}
|
||||
|
||||
createpath := args[0]
|
||||
|
@ -100,10 +101,8 @@ func NewContent(cmd *cobra.Command, args []string) {
|
|||
kind = contentType
|
||||
}
|
||||
|
||||
err := create.NewContent(kind, createpath)
|
||||
if err != nil {
|
||||
jww.ERROR.Println(err)
|
||||
}
|
||||
return create.NewContent(kind, createpath)
|
||||
|
||||
}
|
||||
|
||||
func doNewSite(basepath string, force bool) error {
|
||||
|
@ -146,32 +145,31 @@ func doNewSite(basepath string, force bool) error {
|
|||
}
|
||||
|
||||
// NewSite creates a new hugo site and initializes a structured Hugo directory.
|
||||
func NewSite(cmd *cobra.Command, args []string) {
|
||||
func NewSite(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
cmd.Usage()
|
||||
jww.FATAL.Fatalln("path needs to be provided")
|
||||
return newUserError("path needs to be provided")
|
||||
}
|
||||
|
||||
createpath, err := filepath.Abs(filepath.Clean(args[0]))
|
||||
if err != nil {
|
||||
cmd.Usage()
|
||||
jww.FATAL.Fatalln(err)
|
||||
return newUserError(err)
|
||||
}
|
||||
|
||||
forceNew, _ := cmd.Flags().GetBool("force")
|
||||
if err := doNewSite(createpath, forceNew); err != nil {
|
||||
cmd.Usage()
|
||||
jww.FATAL.Fatalln(err)
|
||||
}
|
||||
|
||||
return doNewSite(createpath, forceNew)
|
||||
|
||||
}
|
||||
|
||||
// NewTheme creates a new Hugo theme.
|
||||
func NewTheme(cmd *cobra.Command, args []string) {
|
||||
InitializeConfig()
|
||||
func NewTheme(cmd *cobra.Command, args []string) error {
|
||||
if err := InitializeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
cmd.Usage()
|
||||
jww.FATAL.Fatalln("theme name needs to be provided")
|
||||
|
||||
return newUserError("theme name needs to be provided")
|
||||
}
|
||||
|
||||
createpath := helpers.AbsPathify(filepath.Join("themes", args[0]))
|
||||
|
@ -229,10 +227,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), hugofs.SourceFs)
|
||||
if err != nil {
|
||||
jww.FATAL.Fatalln(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
createThemeMD(createpath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mkdir(x ...string) {
|
||||
|
|
|
@ -57,7 +57,7 @@ By default hugo will also watch your files for any changes you make and
|
|||
automatically rebuild the site. It will then live reload any open browser pages
|
||||
and push the latest content to them. As most Hugo sites are built in a fraction
|
||||
of a second, you will be able to save and see your changes nearly instantly.`,
|
||||
//Run: server,
|
||||
//RunE: server,
|
||||
}
|
||||
|
||||
type filesOnlyFs struct {
|
||||
|
@ -90,10 +90,10 @@ func init() {
|
|||
serverCmd.Flags().BoolVarP(&NoTimes, "noTimes", "", false, "Don't sync modification time of files")
|
||||
serverCmd.Flags().String("memstats", "", "log memory usage to this file")
|
||||
serverCmd.Flags().Int("meminterval", 100, "interval to poll memory usage (requires --memstats)")
|
||||
serverCmd.Run = server
|
||||
serverCmd.RunE = server
|
||||
}
|
||||
|
||||
func server(cmd *cobra.Command, args []string) {
|
||||
func server(cmd *cobra.Command, args []string) error {
|
||||
InitializeConfig()
|
||||
|
||||
if cmd.Flags().Lookup("disableLiveReload").Changed {
|
||||
|
@ -116,8 +116,7 @@ func server(cmd *cobra.Command, args []string) {
|
|||
jww.ERROR.Println("port", serverPort, "already in use, attempting to use an available port")
|
||||
sp, err := helpers.FindAvailablePort()
|
||||
if err != nil {
|
||||
jww.ERROR.Println("Unable to find alternative port to use")
|
||||
jww.ERROR.Fatalln(err)
|
||||
return newSystemError("Unable to find alternative port to use:", err)
|
||||
}
|
||||
serverPort = sp.Port
|
||||
}
|
||||
|
@ -126,7 +125,7 @@ func server(cmd *cobra.Command, args []string) {
|
|||
|
||||
BaseURL, err := fixURL(BaseURL)
|
||||
if err != nil {
|
||||
jww.ERROR.Fatal(err)
|
||||
return err
|
||||
}
|
||||
viper.Set("BaseURL", BaseURL)
|
||||
|
||||
|
@ -146,7 +145,9 @@ func server(cmd *cobra.Command, args []string) {
|
|||
viper.Set("PublishDir", "/")
|
||||
}
|
||||
|
||||
build(serverWatch)
|
||||
if err := build(serverWatch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Watch runs its own server as part of the routine
|
||||
if serverWatch {
|
||||
|
@ -160,12 +161,15 @@ func server(cmd *cobra.Command, args []string) {
|
|||
|
||||
jww.FEEDBACK.Printf("Watching for changes in %s/{%s}\n", baseWatchDir, rootWatchDirs)
|
||||
err := NewWatcher(serverPort)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
serve(serverPort)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func serve(port int) {
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/hugo/parser"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var undraftCmd = &cobra.Command{
|
||||
|
@ -29,53 +28,50 @@ var undraftCmd = &cobra.Command{
|
|||
Long: `Undraft changes the content's draft status from 'True' to 'False'
|
||||
and updates the date to the current date and time.
|
||||
If the content's draft status is 'False', nothing is done.`,
|
||||
Run: Undraft,
|
||||
RunE: Undraft,
|
||||
}
|
||||
|
||||
// Publish publishes the specified content by setting its draft status
|
||||
// to false and setting its publish date to now. If the specified content is
|
||||
// not a draft, it will log an error.
|
||||
func Undraft(cmd *cobra.Command, args []string) {
|
||||
InitializeConfig()
|
||||
func Undraft(cmd *cobra.Command, args []string) error {
|
||||
if err := InitializeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
cmd.Usage()
|
||||
jww.FATAL.Fatalln("a piece of content needs to be specified")
|
||||
return newUserError("a piece of content needs to be specified")
|
||||
}
|
||||
|
||||
location := args[0]
|
||||
// open the file
|
||||
f, err := os.Open(location)
|
||||
if err != nil {
|
||||
jww.ERROR.Print(err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// get the page from file
|
||||
p, err := parser.ReadFrom(f)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
jww.ERROR.Print(err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := undraftContent(p)
|
||||
if err != nil {
|
||||
jww.ERROR.Printf("an error occurred while undrafting %q: %s", location, err)
|
||||
return
|
||||
return newSystemError("an error occurred while undrafting %q: %s", location, err)
|
||||
}
|
||||
|
||||
f, err = os.OpenFile(location, os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
jww.ERROR.Printf("%q not be undrafted due to error opening file to save changes: %q\n", location, err)
|
||||
return
|
||||
return newSystemError("%q not be undrafted due to error opening file to save changes: %q\n", location, err)
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = w.WriteTo(f)
|
||||
if err != nil {
|
||||
jww.ERROR.Printf("%q not be undrafted due to save error: %q\n", location, err)
|
||||
return newSystemError("%q not be undrafted due to save error: %q\n", location, err)
|
||||
}
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
// undraftContent: if the content is a draft, change its draft status to
|
||||
|
|
|
@ -32,7 +32,7 @@ var version = &cobra.Command{
|
|||
Use: "version",
|
||||
Short: "Print the version number of Hugo",
|
||||
Long: `All software has versions. This is Hugo's.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if hugolib.BuildDate == "" {
|
||||
setBuildDate() // set the build date from executable's mdate
|
||||
} else {
|
||||
|
@ -43,6 +43,8 @@ var version = &cobra.Command{
|
|||
} else {
|
||||
fmt.Printf("Hugo Static Site Generator v%s-%s BuildDate: %s\n", helpers.HugoVersion(), strings.ToUpper(hugolib.CommitHash), hugolib.BuildDate)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -254,9 +254,11 @@ func (s *Site) Build() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Site) Analyze() {
|
||||
s.Process()
|
||||
s.ShowPlan(os.Stdout)
|
||||
func (s *Site) Analyze() error {
|
||||
if err := s.Process(); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ShowPlan(os.Stdout)
|
||||
}
|
||||
|
||||
func (s *Site) prepTemplates() {
|
||||
|
|
Loading…
Reference in a new issue