Skip to content

Commit

Permalink
Change most global flags into local ones
Browse files Browse the repository at this point in the history
This is to ensure that only the relevant command-line flags
for a certain Hugo subcommand is shown to the end user,
reducing clutter and improving user experience.

Fixes #1624 - CLI UX: Flags shouldn't be global
  • Loading branch information
anthonyfok committed Dec 3, 2015
1 parent c9526f6 commit 00d0477
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 108 deletions.
16 changes: 8 additions & 8 deletions commands/benchmark.go
Expand Up @@ -28,23 +28,23 @@ var benchmarkCmd = &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.`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := InitializeConfig(); err != nil {
return err
}

return bench(cmd, args)
},
}

func init() {
initCoreCommonFlags(benchmarkCmd)

benchmarkCmd.Flags().StringVar(&cpuProfilefile, "cpuprofile", "", "path/filename for the CPU profile file")
benchmarkCmd.Flags().StringVar(&memProfilefile, "memprofile", "", "path/filename for the memory profile file")

benchmarkCmd.Flags().IntVarP(&benchmarkTimes, "count", "n", 13, "number of times to build the site")

benchmarkCmd.RunE = benchmark
}

func bench(cmd *cobra.Command, args []string) error {
func benchmark(cmd *cobra.Command, args []string) error {
if err := InitializeConfig(benchmarkCmd); err != nil {
return err
}

if memProfilefile != "" {
f, err := os.Create(memProfilefile)
Expand Down
19 changes: 12 additions & 7 deletions commands/check.go
Expand Up @@ -23,13 +23,18 @@ var checkCmd = &cobra.Command{
Short: "Check content in the source directory",
Long: `Hugo will perform some basic analysis on the content provided
and will give feedback.`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := InitializeConfig(); err != nil {
return err
}
site := hugolib.Site{}
}

func init() {
initCoreCommonFlags(checkCmd)
checkCmd.RunE = check
}

return site.Analyze()
func check(cmd *cobra.Command, args []string) error {
if err := InitializeConfig(checkCmd); err != nil {
return err
}
site := hugolib.Site{}

},
return site.Analyze()
}
1 change: 1 addition & 0 deletions commands/convert.go
Expand Up @@ -74,6 +74,7 @@ func init() {
convertCmd.AddCommand(toTOMLCmd)
convertCmd.AddCommand(toYAMLCmd)
convertCmd.PersistentFlags().StringVarP(&outputDir, "output", "o", "", "filesystem path to write files to")
convertCmd.PersistentFlags().StringVarP(&Source, "source", "s", "", "filesystem path to read files relative from")
convertCmd.PersistentFlags().BoolVar(&unsafe, "unsafe", false, "enable less safe operations, please backup first")
}

Expand Down
135 changes: 68 additions & 67 deletions commands/hugo.go
Expand Up @@ -149,44 +149,52 @@ func AddCommands() {
genCmd.AddCommand(genautocompleteCmd)
genCmd.AddCommand(gendocCmd)
genCmd.AddCommand(genmanCmd)
}

// initCoreCommonFlags initializes common flags used by Hugo core commands
// such as hugo itself, server, check, config and benchmark.
func initCoreCommonFlags(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&Draft, "buildDrafts", "D", false, "include content marked as draft")
cmd.Flags().BoolVarP(&Future, "buildFuture", "F", false, "include content with publishdate in the future")
cmd.Flags().BoolVar(&DisableRSS, "disableRSS", false, "Do not build RSS files")
cmd.Flags().BoolVar(&DisableSitemap, "disableSitemap", false, "Do not build Sitemap file")
cmd.Flags().StringVarP(&Source, "source", "s", "", "filesystem path to read files relative from")
cmd.Flags().StringVarP(&CacheDir, "cacheDir", "", "", "filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/")
cmd.Flags().BoolVarP(&IgnoreCache, "ignoreCache", "", false, "Ignores the cache directory for reading but still writes to it")
cmd.Flags().StringVarP(&Destination, "destination", "d", "", "filesystem path to write files to")
cmd.Flags().StringVarP(&Theme, "theme", "t", "", "theme to use (located in /themes/THEMENAME/)")
cmd.Flags().BoolVar(&UglyURLs, "uglyURLs", false, "if true, use /filename.html instead of /filename/")
cmd.Flags().BoolVar(&CanonifyURLs, "canonifyURLs", false, "if true, all relative URLs will be canonicalized using baseURL")
cmd.Flags().StringVarP(&BaseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. http://spf13.com/")
cmd.Flags().StringVar(&CfgFile, "config", "", "config file (default is path/config.yaml|json|toml)")
cmd.Flags().StringVar(&Editor, "editor", "", "edit new content with this editor, if provided")
cmd.Flags().BoolVar(&nitro.AnalysisOn, "stepAnalysis", false, "display memory and timing of different steps of the program")
cmd.Flags().BoolVar(&PluralizeListTitles, "pluralizeListTitles", true, "Pluralize titles in lists using inflect")
cmd.Flags().BoolVar(&PreserveTaxonomyNames, "preserveTaxonomyNames", false, `Preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")`)

// For bash-completion
validConfigFilenames := []string{"json", "js", "yaml", "yml", "toml", "tml"}
cmd.Flags().SetAnnotation("config", cobra.BashCompFilenameExt, validConfigFilenames)
cmd.Flags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{})
cmd.Flags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
}

// init initializes flags.
func init() {
HugoCmd.PersistentFlags().BoolVarP(&Draft, "buildDrafts", "D", false, "include content marked as draft")
HugoCmd.PersistentFlags().BoolVarP(&Future, "buildFuture", "F", false, "include content with publishdate in the future")
HugoCmd.PersistentFlags().BoolVar(&DisableRSS, "disableRSS", false, "Do not build RSS files")
HugoCmd.PersistentFlags().BoolVar(&DisableSitemap, "disableSitemap", false, "Do not build Sitemap file")
HugoCmd.PersistentFlags().StringVarP(&Source, "source", "s", "", "filesystem path to read files relative from")
HugoCmd.PersistentFlags().StringVarP(&CacheDir, "cacheDir", "", "", "filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/")
HugoCmd.PersistentFlags().BoolVarP(&IgnoreCache, "ignoreCache", "", false, "Ignores the cache directory for reading but still writes to it")
HugoCmd.PersistentFlags().StringVarP(&Destination, "destination", "d", "", "filesystem path to write files to")
HugoCmd.PersistentFlags().StringVarP(&Theme, "theme", "t", "", "theme to use (located in /themes/THEMENAME/)")
HugoCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
HugoCmd.PersistentFlags().BoolVar(&UglyURLs, "uglyURLs", false, "if true, use /filename.html instead of /filename/")
HugoCmd.PersistentFlags().BoolVar(&CanonifyURLs, "canonifyURLs", false, "if true, all relative URLs will be canonicalized using baseURL")
HugoCmd.PersistentFlags().StringVarP(&BaseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. http://spf13.com/")
HugoCmd.PersistentFlags().StringVar(&CfgFile, "config", "", "config file (default is path/config.yaml|json|toml)")
HugoCmd.PersistentFlags().StringVar(&Editor, "editor", "", "edit new content with this editor, if provided")
HugoCmd.PersistentFlags().BoolVar(&Logging, "log", false, "Enable Logging")
HugoCmd.PersistentFlags().StringVar(&LogFile, "logFile", "", "Log File path (if set, logging enabled automatically)")
HugoCmd.PersistentFlags().BoolVar(&VerboseLog, "verboseLog", false, "verbose logging")
HugoCmd.PersistentFlags().BoolVar(&nitro.AnalysisOn, "stepAnalysis", false, "display memory and timing of different steps of the program")
HugoCmd.PersistentFlags().BoolVar(&PluralizeListTitles, "pluralizeListTitles", true, "Pluralize titles in lists using inflect")
HugoCmd.PersistentFlags().BoolVar(&PreserveTaxonomyNames, "preserveTaxonomyNames", false, `Preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")`)

initCoreCommonFlags(HugoCmd)

HugoCmd.Flags().BoolVarP(&BuildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
HugoCmd.Flags().BoolVarP(&NoTimes, "noTimes", "", false, "Don't sync modification time of files")
hugoCmdV = HugoCmd

// For bash-completion
validConfigFilenames := []string{"json", "js", "yaml", "yml", "toml", "tml"}
HugoCmd.PersistentFlags().SetAnnotation("config", cobra.BashCompFilenameExt, validConfigFilenames)
HugoCmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
HugoCmd.PersistentFlags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{})
HugoCmd.PersistentFlags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
HugoCmd.PersistentFlags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
HugoCmd.PersistentFlags().SetAnnotation("logFile", cobra.BashCompFilenameExt, []string{})

// This message will be shown to Windows users if Hugo is opened from explorer.exe
Expand Down Expand Up @@ -241,7 +249,9 @@ func LoadDefaultSettings() {
}

// InitializeConfig initializes a config file with sensible default configuration flags.
func InitializeConfig() error {
// A Hugo command that calls initCoreCommonFlags() can pass itself
// as an argument to have its command-line flags processed here.
func InitializeConfig(subCmdVs ...*cobra.Command) error {
viper.SetConfigFile(CfgFile)
// See https://github.com/spf13/viper/issues/73#issuecomment-126970794
if Source == "" {
Expand All @@ -262,52 +272,47 @@ func InitializeConfig() error {

LoadDefaultSettings()

if hugoCmdV.PersistentFlags().Lookup("buildDrafts").Changed {
viper.Set("BuildDrafts", Draft)
}

if hugoCmdV.PersistentFlags().Lookup("buildFuture").Changed {
viper.Set("BuildFuture", Future)
}

if hugoCmdV.PersistentFlags().Lookup("uglyURLs").Changed {
viper.Set("UglyURLs", UglyURLs)
}

if hugoCmdV.PersistentFlags().Lookup("canonifyURLs").Changed {
viper.Set("CanonifyURLs", CanonifyURLs)
}

if hugoCmdV.PersistentFlags().Lookup("disableRSS").Changed {
viper.Set("DisableRSS", DisableRSS)
}

if hugoCmdV.PersistentFlags().Lookup("disableSitemap").Changed {
viper.Set("DisableSitemap", DisableSitemap)
}

if hugoCmdV.PersistentFlags().Lookup("verbose").Changed {
viper.Set("Verbose", Verbose)
}

if hugoCmdV.PersistentFlags().Lookup("pluralizeListTitles").Changed {
viper.Set("PluralizeListTitles", PluralizeListTitles)
}

if hugoCmdV.PersistentFlags().Lookup("preserveTaxonomyNames").Changed {
viper.Set("PreserveTaxonomyNames", PreserveTaxonomyNames)
}

if hugoCmdV.PersistentFlags().Lookup("editor").Changed {
viper.Set("NewContentEditor", Editor)
}

if hugoCmdV.PersistentFlags().Lookup("logFile").Changed {
viper.Set("LogFile", LogFile)
}

if hugoCmdV.Flags().Lookup("noTimes").Changed {
viper.Set("noTimes", NoTimes)
for _, cmdV := range append([]*cobra.Command{hugoCmdV}, subCmdVs...) {
if cmdV.Flags().Lookup("buildDrafts").Changed {
viper.Set("BuildDrafts", Draft)
}
if cmdV.Flags().Lookup("buildFuture").Changed {
viper.Set("BuildFuture", Future)
}
if cmdV.Flags().Lookup("uglyURLs").Changed {
viper.Set("UglyURLs", UglyURLs)
}
if cmdV.Flags().Lookup("canonifyURLs").Changed {
viper.Set("CanonifyURLs", CanonifyURLs)
}
if cmdV.Flags().Lookup("disableRSS").Changed {
viper.Set("DisableRSS", DisableRSS)
}
if cmdV.Flags().Lookup("disableSitemap").Changed {
viper.Set("DisableSitemap", DisableSitemap)
}
if cmdV.Flags().Lookup("pluralizeListTitles").Changed {
viper.Set("PluralizeListTitles", PluralizeListTitles)
}
if cmdV.Flags().Lookup("preserveTaxonomyNames").Changed {
viper.Set("PreserveTaxonomyNames", PreserveTaxonomyNames)
}
if cmdV.Flags().Lookup("editor").Changed {
viper.Set("NewContentEditor", Editor)
}
if cmdV.Flags().Lookup("ignoreCache").Changed {
viper.Set("IgnoreCache", IgnoreCache)
}
if cmdV.Flags().Lookup("noTimes").Changed {
viper.Set("NoTimes", NoTimes)
}
}

if BaseURL != "" {
Expand Down Expand Up @@ -336,10 +341,6 @@ func InitializeConfig() error {
viper.Set("WorkingDir", dir)
}

if hugoCmdV.PersistentFlags().Lookup("ignoreCache").Changed {
viper.Set("IgnoreCache", IgnoreCache)
}

if CacheDir != "" {
if helpers.FilePathSeparator != CacheDir[len(CacheDir)-1:] {
CacheDir = CacheDir + helpers.FilePathSeparator
Expand Down
1 change: 1 addition & 0 deletions commands/list.go
Expand Up @@ -25,6 +25,7 @@ import (
func init() {
listCmd.AddCommand(listDraftsCmd)
listCmd.AddCommand(listFutureCmd)
listCmd.PersistentFlags().StringVarP(&Source, "source", "s", "", "filesystem path to read files relative from")
}

var listCmd = &cobra.Command{
Expand Down
56 changes: 31 additions & 25 deletions commands/list_config.go
Expand Up @@ -25,34 +25,40 @@ var configCmd = &cobra.Command{
Use: "config",

This comment has been minimized.

Copy link
@bep

bep Dec 3, 2015

Member

Hugo config crashes now, suspect this commit ...:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x50 pc=0x63119]

goroutine 1 [running]:
github.com/spf13/hugo/commands.InitializeConfig(0xc8200bda58, 0x1, 0x1, 0x0, 0x0)
    /Users/bep/go/src/github.com/spf13/hugo/commands/hugo.go:313 +0xd59
github.com/spf13/hugo/commands.config(0xb18ee0, 0xb45ec8, 0x0, 0x0, 0x0, 0x0)
    /Users/bep/go/src/github.com/spf13/hugo/commands/list_config.go:36 +0xb3
github.com/spf13/cobra.(*Command).execute(0xb18ee0, 0xb45ec8, 0x0, 0x0, 0x0, 0x0)
    /Users/bep/go/src/github.com/spf13/cobra/command.go:568 +0x63b
github.com/spf13/cobra.(*Command).ExecuteC(0xb180e0, 0xb18ee0, 0x0, 0x0)
    /Users/bep/go/src/github.com/spf13/cobra/command.go:662 +0x54e
github.com/spf13/hugo/commands.Execute()
    /Users/bep/go/src/github.com/spf13/hugo/commands/hugo.go:125 +0x71
main.main()
    /Users/bep/go/src/github.com/spf13/hugo/main.go:24 +0x28

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1696 +0x1

This comment has been minimized.

Copy link
@anthonyfok

anthonyfok Dec 3, 2015

Author Member

Thank you very much for the heads-up!
I failed to test thoroughly after incorporating tatsushid's PR #1657 into this commit.
My bad indeed. Thank you for catching this crash early on!

Fixed in commit cd06262.

Short: "Print the site configuration",
Long: `Print the site configuration, both default and custom settings.`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := InitializeConfig(); err != nil {
return err
}
}

allSettings := viper.AllSettings()
func init() {
initCoreCommonFlags(configCmd)
configCmd.RunE = config
}

var separator string
if allSettings["metadataformat"] == "toml" {
separator = " = "
} else {
separator = ": "
}
func config(cmd *cobra.Command, args []string) error {
if err := InitializeConfig(configCmd); err != nil {
return err
}

var keys []string
for k := range allSettings {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
kv := reflect.ValueOf(allSettings[k])
if kv.Kind() == reflect.String {
fmt.Printf("%s%s\"%+v\"\n", k, separator, allSettings[k])
} else {
fmt.Printf("%s%s%+v\n", k, separator, allSettings[k])
}
allSettings := viper.AllSettings()

var separator string
if allSettings["metadataformat"] == "toml" {
separator = " = "
} else {
separator = ": "
}

var keys []string
for k := range allSettings {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
kv := reflect.ValueOf(allSettings[k])
if kv.Kind() == reflect.String {
fmt.Printf("%s%s\"%+v\"\n", k, separator, allSettings[k])
} else {
fmt.Printf("%s%s%+v\n", k, separator, allSettings[k])
}
}

return nil
},
return nil
}
3 changes: 2 additions & 1 deletion commands/server.go
Expand Up @@ -81,6 +81,7 @@ func (f noDirFile) Readdir(count int) ([]os.FileInfo, error) {
}

func init() {
initCoreCommonFlags(serverCmd)
serverCmd.Flags().IntVarP(&serverPort, "port", "p", 1313, "port on which the server will listen")
serverCmd.Flags().StringVarP(&serverInterface, "bind", "", "127.0.0.1", "interface to which the server will bind")
serverCmd.Flags().BoolVarP(&serverWatch, "watch", "w", true, "watch filesystem for changes and recreate as needed")
Expand All @@ -94,7 +95,7 @@ func init() {
}

func server(cmd *cobra.Command, args []string) error {
if err := InitializeConfig(); err != nil {
if err := InitializeConfig(serverCmd); err != nil {
return err
}

Expand Down

0 comments on commit 00d0477

Please sign in to comment.