2016-02-24 18:52:11 -05:00
// Copyright 2016 The Hugo Authors. All rights reserved.
2013-09-29 02:09:03 -04:00
//
2015-11-23 22:16:36 -05:00
// Licensed under the Apache License, Version 2.0 (the "License");
2013-09-29 02:09:03 -04:00
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2015-11-23 22:16:36 -05:00
// http://www.apache.org/licenses/LICENSE-2.0
2013-09-29 02:09:03 -04:00
//
// 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.
2015-12-16 22:41:33 -05:00
// Package commands defines and implements command-line commands and flags
// used by Hugo. Commands and flags are implemented using Cobra.
2013-09-29 02:09:03 -04:00
package commands
import (
2014-02-17 05:54:15 -05:00
"fmt"
2014-05-16 17:49:27 -04:00
"net/http"
2014-02-17 05:54:15 -05:00
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
2014-03-31 13:23:34 -04:00
2016-08-09 05:41:56 -04:00
"github.com/spf13/hugo/tpl"
2016-04-18 12:37:32 -04:00
"github.com/spf13/hugo/hugofs"
2015-11-09 23:31:52 -05:00
"github.com/spf13/hugo/parser"
2016-02-05 12:41:40 -05:00
flag "github.com/spf13/pflag"
2015-11-09 23:31:52 -05:00
2015-12-04 08:17:48 -05:00
"regexp"
2016-04-20 10:28:26 -04:00
"github.com/fsnotify/fsnotify"
2016-01-28 09:31:25 -05:00
"github.com/spf13/afero"
2014-03-31 13:23:34 -04:00
"github.com/spf13/cobra"
2014-11-01 11:57:29 -04:00
"github.com/spf13/fsync"
2014-04-05 01:26:43 -04:00
"github.com/spf13/hugo/helpers"
2014-03-31 13:23:34 -04:00
"github.com/spf13/hugo/hugolib"
2014-05-16 17:49:27 -04:00
"github.com/spf13/hugo/livereload"
2014-03-31 13:23:34 -04:00
"github.com/spf13/hugo/utils"
"github.com/spf13/hugo/watcher"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/nitro"
2014-04-05 01:26:43 -04:00
"github.com/spf13/viper"
2013-09-29 02:09:03 -04:00
)
2016-07-26 04:24:27 -04:00
// Hugo represents the Hugo sites to build. This variable is exported as it
2016-03-24 09:24:22 -04:00
// is used by at least one external library (the Hugo caddy plugin). We should
// provide a cleaner external API, but until then, this is it.
2016-07-28 03:30:58 -04:00
var Hugo * hugolib . HugoSites
2015-12-22 00:10:01 -05:00
2016-08-16 06:50:26 -04:00
// Reset resets Hugo ready for a new full build. This is mainly only useful
// for benchmark testing etc. via the CLI commands.
func Reset ( ) error {
2016-08-16 06:49:07 -04:00
Hugo = nil
2016-08-16 06:50:26 -04:00
viper . Reset ( )
return nil
}
2015-12-02 05:42:53 -05:00
// 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
}
2015-12-02 17:37:40 -05:00
func newUserError ( a ... interface { } ) commandError {
return commandError { s : fmt . Sprintln ( a ... ) , userError : true }
2015-12-02 05:42:53 -05:00
}
2015-12-02 17:37:40 -05:00
func newUserErrorF ( format string , a ... interface { } ) commandError {
return commandError { s : fmt . Sprintf ( format , a ... ) , userError : true }
}
func newSystemError ( a ... interface { } ) commandError {
return commandError { s : fmt . Sprintln ( a ... ) , userError : false }
}
func newSystemErrorF ( format string , a ... interface { } ) commandError {
return commandError { s : fmt . Sprintf ( format , a ... ) , userError : false }
2015-12-02 05:42:53 -05:00
}
// 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 ( ) )
}
2016-01-13 01:34:29 -05:00
// HugoCmd is Hugo's root command.
// Every other command attached to HugoCmd is a child command to it.
2016-02-06 06:40:16 -05:00
var HugoCmd = & cobra . Command {
2014-02-17 05:54:15 -05:00
Use : "hugo" ,
2015-05-21 07:30:01 -04:00
Short : "hugo builds your site" ,
2015-08-04 05:15:12 -04:00
Long : ` hugo is the main command , used to build your Hugo site .
2013-09-29 02:09:03 -04:00
2015-08-04 05:15:12 -04:00
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/.`,
2015-12-02 05:42:53 -05:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
if err := InitializeConfig ( ) ; err != nil {
return err
}
2016-02-05 12:41:40 -05:00
if buildWatch {
2016-01-13 01:34:29 -05:00
viper . Set ( "DisableLiveReload" , true )
watchConfig ( )
}
2015-12-02 05:42:53 -05:00
return build ( )
2014-02-17 05:54:15 -05:00
} ,
2013-09-29 02:09:03 -04:00
}
2014-05-16 17:49:27 -04:00
2013-11-12 18:36:23 -05:00
var hugoCmdV * cobra . Command
2015-12-02 13:56:36 -05:00
// Flags that are to be added to commands.
2016-02-05 12:41:40 -05:00
var (
2016-02-05 17:25:07 -05:00
buildWatch bool
2016-02-05 12:41:40 -05:00
canonifyURLs bool
cleanDestination bool
2016-04-05 16:20:39 -04:00
enableRobotsTXT bool
2016-04-02 18:22:31 -04:00
disable404 bool
2016-02-05 12:41:40 -05:00
disableRSS bool
disableSitemap bool
draft bool
2016-02-05 17:25:07 -05:00
forceSync bool
2016-02-05 12:41:40 -05:00
future bool
2016-05-11 10:10:35 -04:00
expired bool
2016-02-05 12:41:40 -05:00
ignoreCache bool
logging bool
noTimes bool
pluralizeListTitles bool
preserveTaxonomyNames bool
2016-02-05 17:25:07 -05:00
renderToMemory bool // for benchmark testing
2016-02-05 12:41:40 -05:00
uglyURLs bool
verbose bool
verboseLog bool
)
2016-02-05 16:58:17 -05:00
var (
baseURL string
cacheDir string
2015-12-09 01:45:24 -05:00
contentDir string
layoutDir string
2016-02-05 16:58:17 -05:00
cfgFile string
destination string
logFile string
theme string
source string
)
2013-09-29 02:09:03 -04:00
2015-12-02 13:56:36 -05:00
// Execute adds all child commands to the root command HugoCmd and sets flags appropriately.
2013-09-29 02:09:03 -04:00
func Execute ( ) {
2016-02-06 06:40:16 -05:00
HugoCmd . SetGlobalNormalizationFunc ( helpers . NormalizeHugoFlags )
2015-12-02 05:42:53 -05:00
2016-02-06 06:40:16 -05:00
HugoCmd . SilenceUsage = true
2015-12-02 05:42:53 -05:00
2014-02-17 05:54:15 -05:00
AddCommands ( )
2015-12-02 05:42:53 -05:00
2016-02-06 06:40:16 -05:00
if c , err := HugoCmd . ExecuteC ( ) ; err != nil {
2015-12-02 05:42:53 -05:00
if isUserError ( err ) {
c . Println ( "" )
c . Println ( c . UsageString ( ) )
}
2015-10-05 14:26:49 -04:00
os . Exit ( - 1 )
}
2013-09-29 02:09:03 -04:00
}
2015-12-02 13:56:36 -05:00
// AddCommands adds child commands to the root command HugoCmd.
2013-09-29 02:09:03 -04:00
func AddCommands ( ) {
2016-02-06 06:40:16 -05:00
HugoCmd . AddCommand ( serverCmd )
HugoCmd . AddCommand ( versionCmd )
HugoCmd . AddCommand ( configCmd )
HugoCmd . AddCommand ( checkCmd )
HugoCmd . AddCommand ( benchmarkCmd )
HugoCmd . AddCommand ( convertCmd )
HugoCmd . AddCommand ( newCmd )
HugoCmd . AddCommand ( listCmd )
HugoCmd . AddCommand ( undraftCmd )
HugoCmd . AddCommand ( importCmd )
HugoCmd . AddCommand ( genCmd )
2015-11-23 10:51:12 -05:00
genCmd . AddCommand ( genautocompleteCmd )
genCmd . AddCommand ( gendocCmd )
genCmd . AddCommand ( genmanCmd )
2015-12-02 14:00:47 -05:00
}
2016-02-05 12:41:40 -05:00
// initHugoBuilderFlags initializes all common flags, typically used by the
2016-02-06 10:31:20 -05:00
// core build commands, namely hugo itself, server, check and benchmark.
2016-02-05 12:41:40 -05:00
func initHugoBuilderFlags ( cmd * cobra . Command ) {
initHugoBuildCommonFlags ( cmd )
}
2016-07-07 12:06:48 -04:00
func initRootPersistentFlags ( ) {
HugoCmd . PersistentFlags ( ) . StringVar ( & cfgFile , "config" , "" , "config file (default is path/config.yaml|json|toml)" )
2016-02-06 10:31:20 -05:00
// Set bash-completion
2015-12-02 14:00:47 -05:00
validConfigFilenames := [ ] string { "json" , "js" , "yaml" , "yml" , "toml" , "tml" }
2016-07-07 12:06:48 -04:00
HugoCmd . PersistentFlags ( ) . SetAnnotation ( "config" , cobra . BashCompFilenameExt , validConfigFilenames )
2013-09-29 02:09:03 -04:00
}
2016-02-06 10:31:20 -05:00
// initHugoBuildCommonFlags initialize common flags related to the Hugo build.
// Called by initHugoBuilderFlags.
2016-02-05 12:41:40 -05:00
func initHugoBuildCommonFlags ( cmd * cobra . Command ) {
cmd . Flags ( ) . BoolVar ( & cleanDestination , "cleanDestinationDir" , false , "Remove files from destination not found in static directories" )
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" )
2016-05-11 10:10:35 -04:00
cmd . Flags ( ) . BoolVarP ( & expired , "buildExpired" , "E" , false , "include expired content" )
2016-04-02 18:22:31 -04:00
cmd . Flags ( ) . BoolVar ( & disable404 , "disable404" , false , "Do not render 404 page" )
2016-02-05 12:41:40 -05:00
cmd . Flags ( ) . BoolVar ( & disableRSS , "disableRSS" , false , "Do not build RSS files" )
cmd . Flags ( ) . BoolVar ( & disableSitemap , "disableSitemap" , false , "Do not build Sitemap file" )
2016-02-05 16:58:17 -05:00
cmd . Flags ( ) . StringVarP ( & source , "source" , "s" , "" , "filesystem path to read files relative from" )
2015-12-09 01:45:24 -05:00
cmd . Flags ( ) . StringVarP ( & contentDir , "contentDir" , "c" , "" , "filesystem path to content directory" )
cmd . Flags ( ) . StringVarP ( & layoutDir , "layoutDir" , "l" , "" , "filesystem path to layout directory" )
2016-02-05 16:58:17 -05:00
cmd . Flags ( ) . StringVarP ( & cacheDir , "cacheDir" , "" , "" , "filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/" )
2016-04-10 06:11:18 -04:00
cmd . Flags ( ) . BoolVarP ( & ignoreCache , "ignoreCache" , "" , false , "Ignores the cache directory" )
2016-02-05 16:58:17 -05:00
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/)" )
2016-02-05 12:41:40 -05:00
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" )
2016-02-05 16:58:17 -05:00
cmd . Flags ( ) . StringVarP ( & baseURL , "baseURL" , "b" , "" , "hostname (and path) to the root, e.g. http://spf13.com/" )
2016-02-05 12:41:40 -05:00
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") ` )
cmd . Flags ( ) . BoolVarP ( & forceSync , "forceSyncStatic" , "" , false , "Copy all files when static is changed." )
cmd . Flags ( ) . BoolVarP ( & noTimes , "noTimes" , "" , false , "Don't sync modification time of files" )
2016-08-09 05:41:56 -04:00
cmd . Flags ( ) . BoolVarP ( & tpl . Logi18nWarnings , "i18n-warnings" , "" , false , "Print missing translations" )
2016-02-05 12:41:40 -05:00
2016-02-06 10:31:20 -05:00
// Set bash-completion.
// Each flag must first be defined before using the SetAnnotation() call.
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" } )
2016-02-05 12:41:40 -05:00
}
2016-02-05 15:30:48 -05:00
func initBenchmarkBuildingFlags ( cmd * cobra . Command ) {
cmd . Flags ( ) . BoolVar ( & renderToMemory , "renderToMemory" , false , "render to memory (only useful for benchmark testing)" )
}
2015-12-02 13:56:36 -05:00
// init initializes flags.
2013-09-29 02:09:03 -04:00
func init ( ) {
2016-02-06 06:40:16 -05:00
HugoCmd . PersistentFlags ( ) . BoolVarP ( & verbose , "verbose" , "v" , false , "verbose output" )
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" )
2015-12-02 14:00:47 -05:00
2016-07-07 12:06:48 -04:00
initRootPersistentFlags ( )
2016-02-06 06:40:16 -05:00
initHugoBuilderFlags ( HugoCmd )
initBenchmarkBuildingFlags ( HugoCmd )
2015-05-31 14:30:53 -04:00
2016-02-06 06:40:16 -05:00
HugoCmd . Flags ( ) . BoolVarP ( & buildWatch , "watch" , "w" , false , "watch filesystem for changes and recreate as needed" )
hugoCmdV = HugoCmd
2015-04-07 07:01:04 -04:00
2016-02-06 10:31:20 -05:00
// Set bash-completion
2016-02-06 06:40:16 -05:00
HugoCmd . PersistentFlags ( ) . SetAnnotation ( "logFile" , cobra . BashCompFilenameExt , [ ] string { } )
2013-09-29 02:09:03 -04:00
}
2015-05-20 18:50:32 -04:00
// InitializeConfig initializes a config file with sensible default configuration flags.
2015-12-02 14:00:47 -05:00
func InitializeConfig ( subCmdVs ... * cobra . Command ) error {
2016-08-09 09:31:16 -04:00
if err := hugolib . LoadGlobalConfig ( source , cfgFile ) ; err != nil {
return err
}
2014-02-17 05:54:15 -05:00
2015-12-02 14:00:47 -05:00
for _ , cmdV := range append ( [ ] * cobra . Command { hugoCmdV } , subCmdVs ... ) {
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . PersistentFlags ( ) , "verbose" ) {
viper . Set ( "Verbose" , verbose )
2016-01-29 16:14:23 -05:00
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . PersistentFlags ( ) , "logFile" ) {
2016-02-05 16:58:17 -05:00
viper . Set ( "LogFile" , logFile )
2015-12-02 14:00:47 -05:00
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . Flags ( ) , "cleanDestinationDir" ) {
viper . Set ( "cleanDestinationDir" , cleanDestination )
2015-12-02 14:00:47 -05:00
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . Flags ( ) , "buildDrafts" ) {
viper . Set ( "BuildDrafts" , draft )
2015-12-02 14:00:47 -05:00
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . Flags ( ) , "buildFuture" ) {
viper . Set ( "BuildFuture" , future )
2015-12-02 14:00:47 -05:00
}
2016-05-11 10:10:35 -04:00
if flagChanged ( cmdV . Flags ( ) , "buildExpired" ) {
viper . Set ( "BuildExpired" , expired )
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . Flags ( ) , "uglyURLs" ) {
viper . Set ( "UglyURLs" , uglyURLs )
2015-12-02 14:00:47 -05:00
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . Flags ( ) , "canonifyURLs" ) {
viper . Set ( "CanonifyURLs" , canonifyURLs )
2015-12-02 14:00:47 -05:00
}
2016-04-02 18:22:31 -04:00
if flagChanged ( cmdV . Flags ( ) , "disable404" ) {
viper . Set ( "Disable404" , disable404 )
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . Flags ( ) , "disableRSS" ) {
viper . Set ( "DisableRSS" , disableRSS )
2015-12-08 16:13:09 -05:00
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . Flags ( ) , "disableSitemap" ) {
viper . Set ( "DisableSitemap" , disableSitemap )
2015-12-02 14:00:47 -05:00
}
2016-04-05 16:20:39 -04:00
if flagChanged ( cmdV . Flags ( ) , "enableRobotsTXT" ) {
viper . Set ( "EnableRobotsTXT" , enableRobotsTXT )
2015-12-02 14:00:47 -05:00
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . Flags ( ) , "pluralizeListTitles" ) {
viper . Set ( "PluralizeListTitles" , pluralizeListTitles )
2015-12-04 08:17:48 -05:00
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . Flags ( ) , "preserveTaxonomyNames" ) {
viper . Set ( "PreserveTaxonomyNames" , preserveTaxonomyNames )
2015-12-02 14:00:47 -05:00
}
2016-02-05 12:41:40 -05:00
if flagChanged ( cmdV . Flags ( ) , "ignoreCache" ) {
viper . Set ( "IgnoreCache" , ignoreCache )
}
if flagChanged ( cmdV . Flags ( ) , "forceSyncStatic" ) {
viper . Set ( "ForceSyncStatic" , forceSync )
}
if flagChanged ( cmdV . Flags ( ) , "noTimes" ) {
viper . Set ( "NoTimes" , noTimes )
2015-12-02 14:00:47 -05:00
}
2015-12-03 14:02:38 -05:00
2015-12-01 11:48:52 -05:00
}
2016-02-05 16:58:17 -05:00
if baseURL != "" {
if ! strings . HasSuffix ( baseURL , "/" ) {
baseURL = baseURL + "/"
2014-04-05 01:26:43 -04:00
}
2016-02-05 16:58:17 -05:00
viper . Set ( "BaseURL" , baseURL )
2014-02-17 05:54:15 -05:00
}
2014-04-10 08:10:12 -04:00
2015-05-16 09:42:10 -04:00
if ! viper . GetBool ( "RelativeURLs" ) && viper . GetString ( "BaseURL" ) == "" {
2015-02-06 04:39:54 -05:00
jww . ERROR . Println ( "No 'baseurl' set in configuration or as a flag. Features like page menus will not work without one." )
}
2016-02-05 16:58:17 -05:00
if theme != "" {
viper . Set ( "theme" , theme )
2014-04-10 08:10:12 -04:00
}
2016-02-05 16:58:17 -05:00
if destination != "" {
viper . Set ( "PublishDir" , destination )
2014-04-05 01:26:43 -04:00
}
2016-02-05 16:58:17 -05:00
if source != "" {
dir , _ := filepath . Abs ( source )
2015-12-19 07:19:31 -05:00
viper . Set ( "WorkingDir" , dir )
2014-04-05 01:26:43 -04:00
} else {
2014-11-20 19:40:45 -05:00
dir , _ := os . Getwd ( )
2014-04-05 01:26:43 -04:00
viper . Set ( "WorkingDir" , dir )
2014-02-17 05:54:15 -05:00
}
2014-03-31 13:23:34 -04:00
2015-12-09 01:45:24 -05:00
if contentDir != "" {
viper . Set ( "ContentDir" , contentDir )
}
if layoutDir != "" {
viper . Set ( "LayoutDir" , layoutDir )
}
2016-02-05 16:58:17 -05:00
if cacheDir != "" {
if helpers . FilePathSeparator != cacheDir [ len ( cacheDir ) - 1 : ] {
cacheDir = cacheDir + helpers . FilePathSeparator
2014-12-26 22:40:10 -05:00
}
2016-03-21 19:28:42 -04:00
isDir , err := helpers . DirExists ( cacheDir , hugofs . Source ( ) )
2015-02-02 04:14:59 -05:00
utils . CheckErr ( err )
if isDir == false {
2016-02-05 16:58:17 -05:00
mkdir ( cacheDir )
2015-02-02 04:14:59 -05:00
}
2016-02-05 16:58:17 -05:00
viper . Set ( "CacheDir" , cacheDir )
2014-12-26 22:40:10 -05:00
} else {
2016-03-21 19:28:42 -04:00
viper . Set ( "CacheDir" , helpers . GetTempDir ( "hugo_cache" , hugofs . Source ( ) ) )
2014-12-26 22:40:10 -05:00
}
2016-02-05 12:41:40 -05:00
if verboseLog || logging || ( viper . IsSet ( "LogFile" ) && viper . GetString ( "LogFile" ) != "" ) {
2014-04-05 01:26:43 -04:00
if viper . IsSet ( "LogFile" ) && viper . GetString ( "LogFile" ) != "" {
jww . SetLogFile ( viper . GetString ( "LogFile" ) )
2014-03-31 13:23:34 -04:00
} else {
jww . UseTempLogFile ( "hugo" )
}
} else {
jww . DiscardLogging ( )
}
2014-04-05 01:26:43 -04:00
if viper . GetBool ( "verbose" ) {
2014-05-27 18:29:55 -04:00
jww . SetStdoutThreshold ( jww . LevelInfo )
2014-03-31 13:23:34 -04:00
}
2016-02-05 12:41:40 -05:00
if verboseLog {
2014-05-27 18:29:55 -04:00
jww . SetLogThreshold ( jww . LevelInfo )
2014-03-31 13:23:34 -04:00
}
2014-04-07 23:29:35 -04:00
jww . INFO . Println ( "Using config file:" , viper . ConfigFileUsed ( ) )
2015-04-28 14:39:11 -04:00
2016-03-21 19:28:42 -04:00
// Init file systems. This may be changed at a later point.
hugofs . InitDefaultFs ( )
2015-07-12 13:25:43 -04:00
themeDir := helpers . GetThemeDir ( )
if themeDir != "" {
if _ , err := os . Stat ( themeDir ) ; os . IsNotExist ( err ) {
2015-12-02 05:42:53 -05:00
return newSystemError ( "Unable to find theme Directory:" , themeDir )
2015-07-12 13:25:43 -04:00
}
}
2015-09-14 11:31:39 -04:00
themeVersionMismatch , minVersion := isThemeVsHugoVersionMismatch ( )
2015-07-12 13:25:43 -04:00
2015-04-28 14:39:11 -04:00
if themeVersionMismatch {
jww . ERROR . Printf ( "Current theme does not support Hugo version %s. Minimum version required is %s\n" ,
helpers . HugoReleaseVersion ( ) , minVersion )
}
2015-12-02 05:42:53 -05:00
2016-07-26 04:24:27 -04:00
return nil
2016-05-14 00:35:16 -04:00
2013-09-29 02:09:03 -04:00
}
2016-02-05 12:41:40 -05:00
func flagChanged ( flags * flag . FlagSet , key string ) bool {
flag := flags . Lookup ( key )
if flag == nil {
return false
}
return flag . Changed
}
2015-11-09 23:31:52 -05:00
func watchConfig ( ) {
viper . WatchConfig ( )
viper . OnConfigChange ( func ( e fsnotify . Event ) {
fmt . Println ( "Config file changed:" , e . Name )
2016-02-14 09:16:55 -05:00
// Force a full rebuild
2016-08-06 08:51:50 -04:00
utils . CheckErr ( reCreateAndbuildSites ( true ) )
2015-11-09 23:31:52 -05:00
if ! viper . GetBool ( "DisableLiveReload" ) {
2016-01-13 01:34:29 -05:00
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
2015-11-09 23:31:52 -05:00
livereload . ForceRefresh ( )
}
} )
}
2015-12-02 05:42:53 -05:00
func build ( watches ... bool ) error {
2016-02-05 15:30:48 -05:00
// Hugo writes the output to memory instead of the disk
// This is only used for benchmark testing. Cause the content is only visible
// in memory
if renderToMemory {
2016-03-21 19:28:42 -04:00
hugofs . SetDestination ( new ( afero . MemMapFs ) )
2016-02-05 15:30:48 -05:00
// Rendering to memoryFS, publish to Root regardless of publishDir.
viper . Set ( "PublishDir" , "/" )
}
2015-12-02 05:42:53 -05:00
if err := copyStatic ( ) ; err != nil {
return fmt . Errorf ( "Error copying static files to %s: %s" , helpers . AbsPathify ( viper . GetString ( "PublishDir" ) ) , err )
2015-11-16 21:52:37 -05:00
}
2014-02-17 05:54:15 -05:00
watch := false
if len ( watches ) > 0 && watches [ 0 ] {
watch = true
}
2016-07-26 04:24:27 -04:00
if err := buildSites ( buildWatch || watch ) ; err != nil {
2015-12-02 05:42:53 -05:00
return fmt . Errorf ( "Error building site: %s" , err )
}
2014-02-17 05:54:15 -05:00
2016-02-05 12:41:40 -05:00
if buildWatch {
2014-04-05 01:26:43 -04:00
jww . FEEDBACK . Println ( "Watching for changes in" , helpers . AbsPathify ( viper . GetString ( "ContentDir" ) ) )
2015-01-29 16:19:12 -05:00
jww . FEEDBACK . Println ( "Press Ctrl+C to stop" )
2014-02-17 05:54:15 -05:00
utils . CheckErr ( NewWatcher ( 0 ) )
}
2015-12-02 05:42:53 -05:00
return nil
2013-09-29 02:09:03 -04:00
}
2016-01-13 11:42:43 -05:00
func getStaticSourceFs ( ) afero . Fs {
2016-03-21 19:28:42 -04:00
source := hugofs . Source ( )
2016-01-13 11:42:43 -05:00
themeDir , err := helpers . GetThemeStaticDirPath ( )
staticDir := helpers . GetStaticDirPath ( ) + helpers . FilePathSeparator
useTheme := true
useStatic := true
if err != nil {
jww . WARN . Println ( err )
useTheme = false
} else {
if _ , err := source . Stat ( themeDir ) ; os . IsNotExist ( err ) {
jww . WARN . Println ( "Unable to find Theme Static Directory:" , themeDir )
useTheme = false
}
}
if _ , err := source . Stat ( staticDir ) ; os . IsNotExist ( err ) {
jww . WARN . Println ( "Unable to find Static Directory:" , staticDir )
useStatic = false
}
if ! useStatic && ! useTheme {
return nil
}
if ! useStatic {
2016-01-29 16:54:33 -05:00
jww . INFO . Println ( themeDir , "is the only static directory available to sync from" )
2016-01-13 11:42:43 -05:00
return afero . NewReadOnlyFs ( afero . NewBasePathFs ( source , themeDir ) )
}
if ! useTheme {
2016-01-29 16:54:33 -05:00
jww . INFO . Println ( staticDir , "is the only static directory available to sync from" )
2016-01-13 11:42:43 -05:00
return afero . NewReadOnlyFs ( afero . NewBasePathFs ( source , staticDir ) )
}
2016-01-29 16:54:33 -05:00
jww . INFO . Println ( "using a UnionFS for static directory comprised of:" )
jww . INFO . Println ( "Base:" , themeDir )
jww . INFO . Println ( "Overlay:" , staticDir )
2016-03-21 19:28:42 -04:00
base := afero . NewReadOnlyFs ( afero . NewBasePathFs ( hugofs . Source ( ) , themeDir ) )
overlay := afero . NewReadOnlyFs ( afero . NewBasePathFs ( hugofs . Source ( ) , staticDir ) )
2016-01-13 11:42:43 -05:00
return afero . NewCopyOnWriteFs ( base , overlay )
}
2013-09-29 02:09:03 -04:00
func copyStatic ( ) error {
2015-12-04 08:17:48 -05:00
publishDir := helpers . AbsPathify ( viper . GetString ( "PublishDir" ) ) + helpers . FilePathSeparator
2014-04-10 08:10:12 -04:00
2015-11-16 21:53:05 -05:00
// If root, remove the second '/'
if publishDir == "//" {
2015-12-04 08:17:48 -05:00
publishDir = helpers . FilePathSeparator
2015-11-16 21:53:05 -05:00
}
2016-01-25 14:40:44 -05:00
// Includes both theme/static & /static
2016-01-13 11:42:43 -05:00
staticSourceFs := getStaticSourceFs ( )
2014-11-01 11:57:29 -04:00
2016-01-13 11:42:43 -05:00
if staticSourceFs == nil {
jww . WARN . Println ( "No static directories found to sync" )
return nil
2015-02-11 14:24:56 -05:00
}
2014-04-10 08:10:12 -04:00
2016-01-13 11:42:43 -05:00
syncer := fsync . NewSyncer ( )
syncer . NoTimes = viper . GetBool ( "notimes" )
syncer . SrcFs = staticSourceFs
2016-03-21 19:28:42 -04:00
syncer . DestFs = hugofs . Destination ( )
2016-01-13 11:42:43 -05:00
// Now that we are using a unionFs for the static directories
// We can effectively clean the publishDir on initial sync
2016-01-29 16:14:23 -05:00
syncer . Delete = viper . GetBool ( "cleanDestinationDir" )
2016-01-29 16:54:33 -05:00
if syncer . Delete {
jww . INFO . Println ( "removing all files from destination that don't exist in static dirs" )
}
2016-01-13 11:42:43 -05:00
jww . INFO . Println ( "syncing static files to" , publishDir )
2014-04-10 08:10:12 -04:00
2016-01-25 14:40:44 -05:00
// because we are using a baseFs (to get the union right).
// set sync src to root
err := syncer . Sync ( publishDir , helpers . FilePathSeparator )
if err != nil {
return err
}
2015-10-15 03:36:27 -04:00
return nil
2013-09-29 02:09:03 -04:00
}
2015-03-10 18:55:23 -04:00
// getDirList provides NewWatcher() with a list of directories to watch for changes.
2013-09-29 02:09:03 -04:00
func getDirList ( ) [ ] string {
2014-02-17 05:54:15 -05:00
var a [ ] string
2015-03-12 16:44:36 -04:00
dataDir := helpers . AbsPathify ( viper . GetString ( "DataDir" ) )
2016-05-14 00:35:16 -04:00
i18nDir := helpers . AbsPathify ( viper . GetString ( "I18nDir" ) )
2015-07-18 09:14:39 -04:00
layoutDir := helpers . AbsPathify ( viper . GetString ( "LayoutDir" ) )
2016-08-13 18:35:43 -04:00
staticDir := helpers . AbsPathify ( viper . GetString ( "StaticDir" ) )
2014-02-17 05:54:15 -05:00
walker := func ( path string , fi os . FileInfo , err error ) error {
if err != nil {
2015-03-12 16:44:36 -04:00
if path == dataDir && os . IsNotExist ( err ) {
jww . WARN . Println ( "Skip DataDir:" , err )
return nil
2016-05-14 00:35:16 -04:00
}
2015-03-12 16:44:36 -04:00
2016-05-14 00:35:16 -04:00
if path == i18nDir && os . IsNotExist ( err ) {
jww . WARN . Println ( "Skip I18nDir:" , err )
return nil
2015-07-18 09:14:39 -04:00
}
2016-05-14 00:35:16 -04:00
2015-07-18 09:14:39 -04:00
if path == layoutDir && os . IsNotExist ( err ) {
jww . WARN . Println ( "Skip LayoutDir:" , err )
return nil
2016-08-13 18:35:43 -04:00
}
2015-07-18 09:14:39 -04:00
2016-08-13 18:35:43 -04:00
if path == staticDir && os . IsNotExist ( err ) {
jww . WARN . Println ( "Skip StaticDir:" , err )
return nil
2015-03-12 16:44:36 -04:00
}
2016-08-13 18:35:43 -04:00
2014-03-31 13:23:34 -04:00
jww . ERROR . Println ( "Walker: " , err )
2014-02-17 05:54:15 -05:00
return nil
}
2014-12-10 10:48:51 -05:00
if fi . Mode ( ) & os . ModeSymlink == os . ModeSymlink {
2015-02-17 16:21:37 -05:00
link , err := filepath . EvalSymlinks ( path )
if err != nil {
jww . ERROR . Printf ( "Cannot read symbolic link '%s', error was: %s" , path , err )
return nil
}
linkfi , err := os . Stat ( link )
if err != nil {
jww . ERROR . Printf ( "Cannot stat '%s', error was: %s" , link , err )
return nil
}
if ! linkfi . Mode ( ) . IsRegular ( ) {
jww . ERROR . Printf ( "Symbolic links for directories not supported, skipping '%s'" , path )
}
2014-12-10 10:48:51 -05:00
return nil
}
2014-02-17 05:54:15 -05:00
if fi . IsDir ( ) {
2015-03-10 18:55:23 -04:00
if fi . Name ( ) == ".git" ||
fi . Name ( ) == "node_modules" || fi . Name ( ) == "bower_components" {
return filepath . SkipDir
}
2014-02-17 05:54:15 -05:00
a = append ( a , path )
}
return nil
}
2016-03-21 19:28:42 -04:00
helpers . SymbolicWalk ( hugofs . Source ( ) , dataDir , walker )
helpers . SymbolicWalk ( hugofs . Source ( ) , helpers . AbsPathify ( viper . GetString ( "ContentDir" ) ) , walker )
2016-05-14 00:35:16 -04:00
helpers . SymbolicWalk ( hugofs . Source ( ) , i18nDir , walker )
2016-03-21 19:28:42 -04:00
helpers . SymbolicWalk ( hugofs . Source ( ) , helpers . AbsPathify ( viper . GetString ( "LayoutDir" ) ) , walker )
2016-08-13 18:35:43 -04:00
helpers . SymbolicWalk ( hugofs . Source ( ) , staticDir , walker )
2015-02-11 14:24:56 -05:00
if helpers . ThemeSet ( ) {
2016-03-21 19:28:42 -04:00
helpers . SymbolicWalk ( hugofs . Source ( ) , helpers . AbsPathify ( viper . GetString ( "themesDir" ) + "/" + viper . GetString ( "theme" ) ) , walker )
2014-04-10 08:10:12 -04:00
}
2014-02-17 05:54:15 -05:00
return a
2013-09-29 02:09:03 -04:00
}
2016-08-06 08:51:50 -04:00
func reCreateAndbuildSites ( watching bool ) ( err error ) {
2016-08-07 18:12:06 -04:00
if err := initSites ( ) ; err != nil {
return err
}
2016-08-06 08:51:50 -04:00
fmt . Println ( "Started building sites ..." )
return Hugo . Build ( hugolib . BuildCfg { CreateSitesFromConfig : true , Watching : watching , PrintStats : true } )
}
func resetAndbuildSites ( watching bool ) ( err error ) {
2016-08-07 18:12:06 -04:00
if err := initSites ( ) ; err != nil {
return err
}
2016-08-06 08:51:50 -04:00
fmt . Println ( "Started building sites ..." )
return Hugo . Build ( hugolib . BuildCfg { ResetState : true , Watching : watching , PrintStats : true } )
}
func initSites ( ) error {
if Hugo != nil {
return nil
}
h , err := hugolib . NewHugoSitesFromConfiguration ( )
if err != nil {
return err
}
Hugo = h
return nil
}
func buildSites ( watching bool ) ( err error ) {
2016-08-07 18:12:06 -04:00
if err := initSites ( ) ; err != nil {
return err
}
2016-07-27 04:49:42 -04:00
fmt . Println ( "Started building sites ..." )
2016-08-06 08:51:50 -04:00
return Hugo . Build ( hugolib . BuildCfg { Watching : watching , PrintStats : true } )
2015-12-22 00:10:01 -05:00
}
2016-07-26 04:24:27 -04:00
func rebuildSites ( events [ ] fsnotify . Event ) error {
2016-08-07 18:12:06 -04:00
if err := initSites ( ) ; err != nil {
return err
}
2016-08-07 16:41:48 -04:00
return Hugo . Rebuild ( hugolib . BuildCfg { PrintStats : true , Watching : true } , events ... )
2013-09-29 02:09:03 -04:00
}
2013-09-30 22:38:32 -04:00
2015-03-10 11:59:55 -04:00
// NewWatcher creates a new watcher to watch filesystem events.
2013-09-30 22:38:32 -04:00
func NewWatcher ( port int ) error {
2014-02-17 05:54:15 -05:00
if runtime . GOOS == "darwin" {
tweakLimit ( )
}
watcher , err := watcher . New ( 1 * time . Second )
var wg sync . WaitGroup
if err != nil {
return err
}
defer watcher . Close ( )
wg . Add ( 1 )
for _ , d := range getDirList ( ) {
if d != "" {
2015-03-10 11:59:55 -04:00
_ = watcher . Add ( d )
2014-02-17 05:54:15 -05:00
}
}
go func ( ) {
for {
select {
2015-03-10 11:59:55 -04:00
case evs := <- watcher . Events :
2016-01-30 03:46:48 -05:00
jww . INFO . Println ( "Received System Events:" , evs )
2014-02-17 05:54:15 -05:00
2016-01-26 14:12:18 -05:00
staticEvents := [ ] fsnotify . Event { }
dynamicEvents := [ ] fsnotify . Event { }
2014-02-17 05:54:15 -05:00
for _ , ev := range evs {
ext := filepath . Ext ( ev . Name )
2016-07-10 20:01:45 -04:00
baseName := filepath . Base ( ev . Name )
2016-02-12 05:54:37 -05:00
istemp := strings . HasSuffix ( ext , "~" ) ||
2016-06-02 15:25:19 -04:00
( ext == ".swp" ) || // vim
( ext == ".swx" ) || // vim
( ext == ".tmp" ) || // generic temp file
( ext == ".DS_Store" ) || // OSX Thumbnail
2016-07-10 20:01:45 -04:00
baseName == "4913" || // vim
2016-06-02 15:25:19 -04:00
strings . HasPrefix ( ext , ".goutputstream" ) || // gnome
strings . HasSuffix ( ext , "jb_old___" ) || // intelliJ
2016-06-02 15:29:52 -04:00
strings . HasSuffix ( ext , "jb_tmp___" ) || // intelliJ
2016-06-02 15:25:19 -04:00
strings . HasSuffix ( ext , "jb_bak___" ) || // intelliJ
2016-07-10 20:01:45 -04:00
strings . HasPrefix ( ext , ".sb-" ) || // byword
strings . HasPrefix ( baseName , ".#" ) || // emacs
strings . HasPrefix ( baseName , "#" ) // emacs
2014-02-17 05:54:15 -05:00
if istemp {
continue
}
2016-01-13 11:42:43 -05:00
// Sometimes during rm -rf operations a '"": REMOVE' is triggered. Just ignore these
if ev . Name == "" {
continue
}
2015-11-18 16:59:32 -05:00
// Write and rename operations are often followed by CHMOD.
// There may be valid use cases for rebuilding the site on CHMOD,
// but that will require more complex logic than this simple conditional.
// On OS X this seems to be related to Spotlight, see:
// https://github.com/go-fsnotify/fsnotify/issues/15
// A workaround is to put your site(s) on the Spotlight exception list,
// but that may be a little mysterious for most end users.
// So, for now, we skip reload on CHMOD.
2016-07-26 16:18:15 -04:00
// We do have to check for WRITE though. On slower laptops a Chmod
// could be aggregated with other important events, and we still want
// to rebuild on those
if ev . Op & ( fsnotify . Chmod | fsnotify . Write | fsnotify . Create ) == fsnotify . Chmod {
2015-11-18 16:59:32 -05:00
continue
}
2016-01-28 09:31:25 -05:00
walkAdder := func ( path string , f os . FileInfo , err error ) error {
2016-01-13 11:42:43 -05:00
if f . IsDir ( ) {
jww . FEEDBACK . Println ( "adding created directory to watchlist" , path )
watcher . Add ( path )
}
return nil
}
// recursively add new directories to watch list
// When mkdir -p is used, only the top directory triggers an event (at least on OSX)
if ev . Op & fsnotify . Create == fsnotify . Create {
2016-03-21 19:28:42 -04:00
if s , err := hugofs . Source ( ) . Stat ( ev . Name ) ; err == nil && s . Mode ( ) . IsDir ( ) {
helpers . SymbolicWalk ( hugofs . Source ( ) , ev . Name , walkAdder )
2014-02-17 05:54:15 -05:00
}
}
2016-01-11 12:06:52 -05:00
isstatic := strings . HasPrefix ( ev . Name , helpers . GetStaticDirPath ( ) ) || ( len ( helpers . GetThemesDirPath ( ) ) > 0 && strings . HasPrefix ( ev . Name , helpers . GetThemesDirPath ( ) ) )
if isstatic {
staticEvents = append ( staticEvents , ev )
} else {
dynamicEvents = append ( dynamicEvents , ev )
}
2014-02-17 05:54:15 -05:00
}
2016-01-11 12:06:52 -05:00
if len ( staticEvents ) > 0 {
2016-01-13 11:42:43 -05:00
publishDir := helpers . AbsPathify ( viper . GetString ( "PublishDir" ) ) + helpers . FilePathSeparator
// If root, remove the second '/'
if publishDir == "//" {
publishDir = helpers . FilePathSeparator
}
2016-01-28 09:31:25 -05:00
jww . FEEDBACK . Println ( "\nStatic file changes detected" )
2016-01-13 11:42:43 -05:00
const layout = "2006-01-02 15:04 -0700"
fmt . Println ( time . Now ( ) . Format ( layout ) )
2015-12-04 08:17:48 -05:00
if viper . GetBool ( "ForceSyncStatic" ) {
jww . FEEDBACK . Printf ( "Syncing all static files\n" )
err := copyStatic ( )
if err != nil {
utils . StopOnErr ( err , fmt . Sprintf ( "Error copying static files to %s" , helpers . AbsPathify ( viper . GetString ( "PublishDir" ) ) ) )
}
} else {
2016-01-13 11:42:43 -05:00
staticSourceFs := getStaticSourceFs ( )
2015-12-04 08:17:48 -05:00
2016-01-13 11:42:43 -05:00
if staticSourceFs == nil {
jww . WARN . Println ( "No static directories found to sync" )
return
2015-12-04 08:17:48 -05:00
}
2016-01-13 11:42:43 -05:00
syncer := fsync . NewSyncer ( )
syncer . NoTimes = viper . GetBool ( "notimes" )
syncer . SrcFs = staticSourceFs
2016-03-21 19:28:42 -04:00
syncer . DestFs = hugofs . Destination ( )
2016-01-02 11:44:57 -05:00
2016-01-28 09:31:25 -05:00
// prevent spamming the log on changes
logger := helpers . NewDistinctFeedbackLogger ( )
2016-01-02 11:44:57 -05:00
2016-01-11 12:06:52 -05:00
for _ , ev := range staticEvents {
2016-01-13 11:42:43 -05:00
// Due to our approach of layering both directories and the content's rendered output
// into one we can't accurately remove a file not in one of the source directories.
// If a file is in the local static dir and also in the theme static dir and we remove
// it from one of those locations we expect it to still exist in the destination
//
2016-01-26 14:12:18 -05:00
// If Hugo generates a file (from the content dir) over a static file
// the content generated file should take precedence.
//
// Because we are now watching and handling individual events it is possible that a static
// event that occupies the same path as a content generated file will take precedence
// until a regeneration of the content takes places.
//
// Hugo assumes that these cases are very rare and will permit this bad behavior
// The alternative is to track every single file and which pipeline rendered it
// and then to handle conflict resolution on every event.
2016-01-13 11:42:43 -05:00
2016-01-11 12:06:52 -05:00
fromPath := ev . Name
2016-01-02 11:44:57 -05:00
2016-01-11 12:06:52 -05:00
// If we are here we already know the event took place in a static dir
relPath , err := helpers . MakeStaticPathRelative ( fromPath )
if err != nil {
fmt . Println ( err )
continue
2016-01-02 11:44:57 -05:00
}
2016-01-11 12:06:52 -05:00
2016-01-26 14:12:18 -05:00
// Remove || rename is harder and will require an assumption.
// Hugo takes the following approach:
// If the static file exists in any of the static source directories after this event
// Hugo will re-sync it.
// If it does not exist in all of the static directories Hugo will remove it.
//
// This assumes that Hugo has not generated content on top of a static file and then removed
// the source of that static file. In this case Hugo will incorrectly remove that file
// from the published directory.
2016-01-11 12:06:52 -05:00
if ev . Op & fsnotify . Rename == fsnotify . Rename || ev . Op & fsnotify . Remove == fsnotify . Remove {
2016-01-13 11:42:43 -05:00
if _ , err := staticSourceFs . Stat ( relPath ) ; os . IsNotExist ( err ) {
// If file doesn't exist in any static dir, remove it
2016-01-28 09:31:25 -05:00
toRemove := filepath . Join ( publishDir , relPath )
logger . Println ( "File no longer exists in static dir, removing" , toRemove )
2016-03-21 19:28:42 -04:00
hugofs . Destination ( ) . RemoveAll ( toRemove )
2016-01-13 11:42:43 -05:00
} else if err == nil {
// If file still exists, sync it
2016-01-28 09:31:25 -05:00
logger . Println ( "Syncing" , relPath , "to" , publishDir )
2016-03-01 10:38:56 -05:00
if err := syncer . Sync ( filepath . Join ( publishDir , relPath ) , relPath ) ; err != nil {
jww . ERROR . Println ( err )
}
2016-01-13 11:42:43 -05:00
} else {
jww . ERROR . Println ( err )
}
2016-01-11 12:06:52 -05:00
continue
}
2016-01-26 14:12:18 -05:00
// For all other event operations Hugo will sync static.
2016-01-28 09:31:25 -05:00
logger . Println ( "Syncing" , relPath , "to" , publishDir )
2016-03-01 10:38:56 -05:00
if err := syncer . Sync ( filepath . Join ( publishDir , relPath ) , relPath ) ; err != nil {
jww . ERROR . Println ( err )
}
2015-12-04 08:17:48 -05:00
}
2015-11-16 21:52:37 -05:00
}
2014-05-16 11:48:59 -04:00
2016-02-05 12:41:40 -05:00
if ! buildWatch && ! viper . GetBool ( "DisableLiveReload" ) {
2016-01-13 01:34:29 -05:00
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
2014-09-10 06:21:22 -04:00
// force refresh when more than one file
2016-01-13 11:42:43 -05:00
if len ( staticEvents ) > 0 {
2016-01-11 12:06:52 -05:00
for _ , ev := range staticEvents {
path , _ := helpers . MakeStaticPathRelative ( ev . Name )
2014-09-10 06:21:22 -04:00
livereload . RefreshPath ( path )
}
} else {
livereload . ForceRefresh ( )
}
2014-08-25 13:49:53 -04:00
}
2014-02-17 05:54:15 -05:00
}
2016-01-13 11:42:43 -05:00
if len ( dynamicEvents ) > 0 {
2014-10-27 17:23:54 -04:00
fmt . Print ( "\nChange detected, rebuilding site\n" )
const layout = "2006-01-02 15:04 -0700"
fmt . Println ( time . Now ( ) . Format ( layout ) )
2015-12-22 00:10:01 -05:00
2016-07-26 04:24:27 -04:00
rebuildSites ( dynamicEvents )
2014-05-16 11:48:59 -04:00
2016-02-05 12:41:40 -05:00
if ! buildWatch && ! viper . GetBool ( "DisableLiveReload" ) {
2016-01-13 01:34:29 -05:00
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
2014-08-25 13:49:53 -04:00
livereload . ForceRefresh ( )
}
2014-02-17 05:54:15 -05:00
}
2015-03-10 11:59:55 -04:00
case err := <- watcher . Errors :
2014-02-17 05:54:15 -05:00
if err != nil {
fmt . Println ( "error:" , err )
}
}
}
} ( )
if port > 0 {
2014-05-16 17:49:27 -04:00
if ! viper . GetBool ( "DisableLiveReload" ) {
livereload . Initialize ( )
http . HandleFunc ( "/livereload.js" , livereload . ServeJS )
http . HandleFunc ( "/livereload" , livereload . Handler )
}
2014-02-17 05:54:15 -05:00
go serve ( port )
}
wg . Wait ( )
return nil
2013-09-30 22:38:32 -04:00
}
2015-09-14 11:31:39 -04:00
2015-12-02 13:56:36 -05:00
// isThemeVsHugoVersionMismatch returns whether the current Hugo version is
// less than the theme's min_version.
2015-09-14 11:31:39 -04:00
func isThemeVsHugoVersionMismatch ( ) ( mismatch bool , requiredMinVersion string ) {
if ! helpers . ThemeSet ( ) {
return
}
themeDir := helpers . GetThemeDir ( )
2016-03-21 19:28:42 -04:00
fs := hugofs . Source ( )
2015-09-14 11:31:39 -04:00
path := filepath . Join ( themeDir , "theme.toml" )
exists , err := helpers . Exists ( path , fs )
if err != nil || ! exists {
return
}
2016-07-30 09:37:03 -04:00
b , err := afero . ReadFile ( fs , path )
2015-09-14 11:31:39 -04:00
c , err := parser . HandleTOMLMetaData ( b )
if err != nil {
return
}
config := c . ( map [ string ] interface { } )
if minVersion , ok := config [ "min_version" ] ; ok {
switch minVersion . ( type ) {
case float32 :
return helpers . HugoVersionNumber < minVersion . ( float32 ) , fmt . Sprint ( minVersion )
case float64 :
return helpers . HugoVersionNumber < minVersion . ( float64 ) , fmt . Sprint ( minVersion )
default :
return
}
}
return
}