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:
Bjørn Erik Pedersen 2015-12-02 11:42:53 +01:00 committed by Anthony Fok
parent 6959b7fa80
commit 3f0f7eed68
17 changed files with 219 additions and 155 deletions

View file

@ -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
}

View file

@ -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()
},
}

View file

@ -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 {

View file

@ -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
},
}

View file

@ -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
},
}

View file

@ -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
},
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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) {

View file

@ -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
},
}

View file

@ -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
},
}

View file

@ -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
},
}

View file

@ -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) {

View file

@ -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) {

View file

@ -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

View file

@ -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
},
}

View file

@ -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() {