all: Refactor to nonglobal file systems

Updates #2701
Fixes #2951
This commit is contained in:
Bjørn Erik Pedersen
2017-01-10 10:55:03 +01:00
parent 0ada405912
commit c71e1b106e
71 changed files with 2219 additions and 1731 deletions

View File

@@ -49,10 +49,13 @@ func init() {
func benchmark(cmd *cobra.Command, args []string) error {
cfg, err := InitializeConfig(benchmarkCmd)
if err != nil {
return err
}
c := commandeer{cfg}
var memProf *os.File
if memProfileFile != "" {
memProf, err = os.Create(memProfileFile)
@@ -79,7 +82,7 @@ func benchmark(cmd *cobra.Command, args []string) error {
t := time.Now()
for i := 0; i < benchmarkTimes; i++ {
if err = resetAndBuildSites(cfg, false); err != nil {
if err = c.resetAndBuildSites(false); err != nil {
return err
}
}

View File

@@ -51,9 +51,9 @@ for rendering in Hugo.`,
if !strings.HasSuffix(gendocdir, helpers.FilePathSeparator) {
gendocdir += helpers.FilePathSeparator
}
if found, _ := helpers.Exists(gendocdir, hugofs.Os()); !found {
if found, _ := helpers.Exists(gendocdir, hugofs.Os); !found {
jww.FEEDBACK.Println("Directory", gendocdir, "does not exist, creating...")
hugofs.Os().MkdirAll(gendocdir, 0777)
hugofs.Os.MkdirAll(gendocdir, 0777)
}
now := time.Now().Format(time.RFC3339)
prepender := func(filename string) string {

View File

@@ -41,9 +41,9 @@ in the "man" directory under the current directory.`,
if !strings.HasSuffix(genmandir, helpers.FilePathSeparator) {
genmandir += helpers.FilePathSeparator
}
if found, _ := helpers.Exists(genmandir, hugofs.Os()); !found {
if found, _ := helpers.Exists(genmandir, hugofs.Os); !found {
jww.FEEDBACK.Println("Directory", genmandir, "does not exist, creating...")
hugofs.Os().MkdirAll(genmandir, 0777)
hugofs.Os.MkdirAll(genmandir, 0777)
}
cmd.Root().DisableAutoGenTag = true

View File

@@ -40,6 +40,7 @@ import (
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/fsync"
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugolib"
"github.com/spf13/hugo/livereload"
@@ -50,6 +51,10 @@ import (
"github.com/spf13/viper"
)
type commandeer struct {
deps.DepsCfg
}
// Hugo represents the Hugo sites to build. This variable is exported as it
// 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.
@@ -119,12 +124,14 @@ Complete documentation is available at http://gohugo.io/.`,
return err
}
c := commandeer{cfg}
if buildWatch {
viper.Set("disableLiveReload", true)
watchConfig(cfg)
c.watchConfig()
}
return build(cfg)
return c.build()
},
}
@@ -268,9 +275,9 @@ func init() {
}
// InitializeConfig initializes a config file with sensible default configuration flags.
func InitializeConfig(subCmdVs ...*cobra.Command) (hugolib.DepsCfg, error) {
func InitializeConfig(subCmdVs ...*cobra.Command) (deps.DepsCfg, error) {
var cfg hugolib.DepsCfg
var cfg deps.DepsCfg
if err := hugolib.LoadGlobalConfig(source, cfgFile); err != nil {
return cfg, err
@@ -323,34 +330,34 @@ func InitializeConfig(subCmdVs ...*cobra.Command) (hugolib.DepsCfg, error) {
viper.Set("cacheDir", cacheDir)
}
// Init file systems. This may be changed at a later point.
cfg.Fs = hugofs.NewDefault()
cacheDir = viper.GetString("cacheDir")
if cacheDir != "" {
if helpers.FilePathSeparator != cacheDir[len(cacheDir)-1:] {
cacheDir = cacheDir + helpers.FilePathSeparator
}
isDir, err := helpers.DirExists(cacheDir, hugofs.Source())
isDir, err := helpers.DirExists(cacheDir, cfg.Fs.Source)
utils.CheckErr(err)
if !isDir {
mkdir(cacheDir)
}
viper.Set("cacheDir", cacheDir)
} else {
viper.Set("cacheDir", helpers.GetTempDir("hugo_cache", hugofs.Source()))
viper.Set("cacheDir", helpers.GetTempDir("hugo_cache", cfg.Fs.Source))
}
jww.INFO.Println("Using config file:", viper.ConfigFileUsed())
// Init file systems. This may be changed at a later point.
hugofs.InitDefaultFs()
themeDir := helpers.GetThemeDir()
if themeDir != "" {
if _, err := hugofs.Source().Stat(themeDir); os.IsNotExist(err) {
if _, err := cfg.Fs.Source.Stat(themeDir); os.IsNotExist(err) {
return cfg, newSystemError("Unable to find theme Directory:", themeDir)
}
}
themeVersionMismatch, minVersion := isThemeVsHugoVersionMismatch()
themeVersionMismatch, minVersion := isThemeVsHugoVersionMismatch(cfg.Fs.Source)
if themeVersionMismatch {
jww.ERROR.Printf("Current theme does not support Hugo version %s. Minimum version required is %s\n",
@@ -447,12 +454,12 @@ func flagChanged(flags *flag.FlagSet, key string) bool {
return flag.Changed
}
func watchConfig(cfg hugolib.DepsCfg) {
func (c commandeer) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
jww.FEEDBACK.Println("Config file changed:", e.Name)
// Force a full rebuild
utils.CheckErr(recreateAndBuildSites(cfg, true))
utils.CheckErr(c.recreateAndBuildSites(true))
if !viper.GetBool("disableLiveReload") {
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
livereload.ForceRefresh()
@@ -460,39 +467,40 @@ func watchConfig(cfg hugolib.DepsCfg) {
})
}
func build(cfg hugolib.DepsCfg, watches ...bool) error {
func (c commandeer) build(watches ...bool) error {
// 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 {
hugofs.SetDestination(new(afero.MemMapFs))
c.Fs.Destination = new(afero.MemMapFs)
// Rendering to memoryFS, publish to Root regardless of publishDir.
viper.Set("publishDir", "/")
}
if err := copyStatic(); err != nil {
if err := c.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
}
if err := buildSites(cfg, buildWatch || watch); err != nil {
if err := c.buildSites(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(cfg, 0))
utils.CheckErr(c.newWatcher(0))
}
return nil
}
func getStaticSourceFs() afero.Fs {
source := hugofs.Source()
themeDir, err := helpers.GetThemeStaticDirPath()
func (c commandeer) getStaticSourceFs() afero.Fs {
source := c.Fs.Source
pathSpec := helpers.NewPathSpec(c.Fs, viper.GetViper())
themeDir, err := pathSpec.GetThemeStaticDirPath()
staticDir := helpers.GetStaticDirPath() + helpers.FilePathSeparator
useTheme := true
@@ -532,12 +540,12 @@ func getStaticSourceFs() afero.Fs {
jww.INFO.Println("using a UnionFS for static directory comprised of:")
jww.INFO.Println("Base:", themeDir)
jww.INFO.Println("Overlay:", staticDir)
base := afero.NewReadOnlyFs(afero.NewBasePathFs(hugofs.Source(), themeDir))
overlay := afero.NewReadOnlyFs(afero.NewBasePathFs(hugofs.Source(), staticDir))
base := afero.NewReadOnlyFs(afero.NewBasePathFs(source, themeDir))
overlay := afero.NewReadOnlyFs(afero.NewBasePathFs(source, staticDir))
return afero.NewCopyOnWriteFs(base, overlay)
}
func copyStatic() error {
func (c commandeer) copyStatic() error {
publishDir := helpers.AbsPathify(viper.GetString("publishDir")) + helpers.FilePathSeparator
// If root, remove the second '/'
@@ -546,7 +554,7 @@ func copyStatic() error {
}
// Includes both theme/static & /static
staticSourceFs := getStaticSourceFs()
staticSourceFs := c.getStaticSourceFs()
if staticSourceFs == nil {
jww.WARN.Println("No static directories found to sync")
@@ -557,7 +565,7 @@ func copyStatic() error {
syncer.NoTimes = viper.GetBool("noTimes")
syncer.NoChmod = viper.GetBool("noChmod")
syncer.SrcFs = staticSourceFs
syncer.DestFs = hugofs.Destination()
syncer.DestFs = c.Fs.Destination
// Now that we are using a unionFs for the static directories
// We can effectively clean the publishDir on initial sync
syncer.Delete = viper.GetBool("cleanDestinationDir")
@@ -572,7 +580,7 @@ func copyStatic() error {
}
// getDirList provides NewWatcher() with a list of directories to watch for changes.
func getDirList() []string {
func (c commandeer) getDirList() []string {
var a []string
dataDir := helpers.AbsPathify(viper.GetString("dataDir"))
i18nDir := helpers.AbsPathify(viper.GetString("i18nDir"))
@@ -621,7 +629,7 @@ func getDirList() []string {
jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", path, err)
return nil
}
linkfi, err := hugofs.Source().Stat(link)
linkfi, err := c.Fs.Source.Stat(link)
if err != nil {
jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
return nil
@@ -642,25 +650,25 @@ func getDirList() []string {
return nil
}
helpers.SymbolicWalk(hugofs.Source(), dataDir, walker)
helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("contentDir")), walker)
helpers.SymbolicWalk(hugofs.Source(), i18nDir, walker)
helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("layoutDir")), walker)
helpers.SymbolicWalk(c.Fs.Source, dataDir, walker)
helpers.SymbolicWalk(c.Fs.Source, helpers.AbsPathify(viper.GetString("contentDir")), walker)
helpers.SymbolicWalk(c.Fs.Source, i18nDir, walker)
helpers.SymbolicWalk(c.Fs.Source, helpers.AbsPathify(viper.GetString("layoutDir")), walker)
helpers.SymbolicWalk(hugofs.Source(), staticDir, walker)
helpers.SymbolicWalk(c.Fs.Source, staticDir, walker)
if helpers.ThemeSet() {
helpers.SymbolicWalk(hugofs.Source(), filepath.Join(themesDir, "layouts"), walker)
helpers.SymbolicWalk(hugofs.Source(), filepath.Join(themesDir, "static"), walker)
helpers.SymbolicWalk(hugofs.Source(), filepath.Join(themesDir, "i18n"), walker)
helpers.SymbolicWalk(hugofs.Source(), filepath.Join(themesDir, "data"), walker)
helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "layouts"), walker)
helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "static"), walker)
helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "i18n"), walker)
helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "data"), walker)
}
return a
}
func recreateAndBuildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
if err := initSites(cfg); err != nil {
func (c commandeer) recreateAndBuildSites(watching bool) (err error) {
if err := c.initSites(); err != nil {
return err
}
if !quiet {
@@ -669,9 +677,9 @@ func recreateAndBuildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
return Hugo.Build(hugolib.BuildCfg{CreateSitesFromConfig: true, Watching: watching, PrintStats: !quiet})
}
func resetAndBuildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
if err := initSites(cfg); err != nil {
return err
func (c commandeer) resetAndBuildSites(watching bool) (err error) {
if err = c.initSites(); err != nil {
return
}
if !quiet {
jww.FEEDBACK.Println("Started building sites ...")
@@ -679,12 +687,12 @@ func resetAndBuildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
return Hugo.Build(hugolib.BuildCfg{ResetState: true, Watching: watching, PrintStats: !quiet})
}
func initSites(cfg hugolib.DepsCfg) error {
func (c commandeer) initSites() error {
if Hugo != nil {
return nil
}
h, err := hugolib.NewHugoSitesFromConfiguration(cfg)
h, err := hugolib.NewHugoSitesFromConfiguration(c.DepsCfg)
if err != nil {
return err
@@ -694,8 +702,8 @@ func initSites(cfg hugolib.DepsCfg) error {
return nil
}
func buildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
if err := initSites(cfg); err != nil {
func (c commandeer) buildSites(watching bool) (err error) {
if err := c.initSites(); err != nil {
return err
}
if !quiet {
@@ -704,19 +712,21 @@ func buildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
return Hugo.Build(hugolib.BuildCfg{Watching: watching, PrintStats: !quiet})
}
func rebuildSites(cfg hugolib.DepsCfg, events []fsnotify.Event) error {
if err := initSites(cfg); err != nil {
func (c commandeer) rebuildSites(events []fsnotify.Event) error {
if err := c.initSites(); err != nil {
return err
}
return Hugo.Build(hugolib.BuildCfg{PrintStats: !quiet, Watching: true}, events...)
}
// newWatcher creates a new watcher to watch filesystem events.
func newWatcher(cfg hugolib.DepsCfg, port int) error {
func (c commandeer) newWatcher(port int) error {
if runtime.GOOS == "darwin" {
tweakLimit()
}
pathSpec := helpers.NewPathSpec(c.Fs, viper.GetViper())
watcher, err := watcher.New(1 * time.Second)
var wg sync.WaitGroup
@@ -728,7 +738,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
wg.Add(1)
for _, d := range getDirList() {
for _, d := range c.getDirList() {
if d != "" {
_ = watcher.Add(d)
}
@@ -793,12 +803,12 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
// 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 {
if s, err := hugofs.Source().Stat(ev.Name); err == nil && s.Mode().IsDir() {
helpers.SymbolicWalk(hugofs.Source(), ev.Name, walkAdder)
if s, err := c.Fs.Source.Stat(ev.Name); err == nil && s.Mode().IsDir() {
helpers.SymbolicWalk(c.Fs.Source, ev.Name, walkAdder)
}
}
isstatic := strings.HasPrefix(ev.Name, helpers.GetStaticDirPath()) || (len(helpers.GetThemesDirPath()) > 0 && strings.HasPrefix(ev.Name, helpers.GetThemesDirPath()))
isstatic := strings.HasPrefix(ev.Name, helpers.GetStaticDirPath()) || (len(pathSpec.GetThemesDirPath()) > 0 && strings.HasPrefix(ev.Name, pathSpec.GetThemesDirPath()))
if isstatic {
staticEvents = append(staticEvents, ev)
@@ -821,12 +831,12 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
if viper.GetBool("forceSyncStatic") {
jww.FEEDBACK.Printf("Syncing all static files\n")
err := copyStatic()
err := c.copyStatic()
if err != nil {
utils.StopOnErr(err, fmt.Sprintf("Error copying static files to %s", publishDir))
}
} else {
staticSourceFs := getStaticSourceFs()
staticSourceFs := c.getStaticSourceFs()
if staticSourceFs == nil {
jww.WARN.Println("No static directories found to sync")
@@ -837,7 +847,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
syncer.NoTimes = viper.GetBool("noTimes")
syncer.NoChmod = viper.GetBool("noChmod")
syncer.SrcFs = staticSourceFs
syncer.DestFs = hugofs.Destination()
syncer.DestFs = c.Fs.Destination
// prevent spamming the log on changes
logger := helpers.NewDistinctFeedbackLogger()
@@ -862,7 +872,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
fromPath := ev.Name
// If we are here we already know the event took place in a static dir
relPath, err := helpers.MakeStaticPathRelative(fromPath)
relPath, err := pathSpec.MakeStaticPathRelative(fromPath)
if err != nil {
jww.ERROR.Println(err)
continue
@@ -882,7 +892,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
// If file doesn't exist in any static dir, remove it
toRemove := filepath.Join(publishDir, relPath)
logger.Println("File no longer exists in static dir, removing", toRemove)
hugofs.Destination().RemoveAll(toRemove)
c.Fs.Destination.RemoveAll(toRemove)
} else if err == nil {
// If file still exists, sync it
logger.Println("Syncing", relPath, "to", publishDir)
@@ -910,7 +920,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
// force refresh when more than one file
if len(staticEvents) > 0 {
for _, ev := range staticEvents {
path, _ := helpers.MakeStaticPathRelative(ev.Name)
path, _ := pathSpec.MakeStaticPathRelative(ev.Name)
livereload.RefreshPath(path)
}
@@ -925,7 +935,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
const layout = "2006-01-02 15:04 -0700"
jww.FEEDBACK.Println(time.Now().Format(layout))
rebuildSites(cfg, dynamicEvents)
c.rebuildSites(dynamicEvents)
if !buildWatch && !viper.GetBool("disableLiveReload") {
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
@@ -947,7 +957,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
http.HandleFunc("/livereload", livereload.Handler)
}
go serve(port)
go c.serve(port)
}
wg.Wait()
@@ -956,14 +966,13 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
// isThemeVsHugoVersionMismatch returns whether the current Hugo version is
// less than the theme's min_version.
func isThemeVsHugoVersionMismatch() (mismatch bool, requiredMinVersion string) {
func isThemeVsHugoVersionMismatch(fs afero.Fs) (mismatch bool, requiredMinVersion string) {
if !helpers.ThemeSet() {
return
}
themeDir := helpers.GetThemeDir()
fs := hugofs.Source()
path := filepath.Join(themeDir, "theme.toml")
exists, err := helpers.Exists(path, fs)

View File

@@ -25,6 +25,7 @@ import (
"strings"
"time"
"github.com/spf13/afero"
"github.com/spf13/cast"
"github.com/spf13/cobra"
"github.com/spf13/hugo/helpers"
@@ -122,7 +123,7 @@ func importFromJekyll(cmd *cobra.Command, args []string) error {
return convertJekyllPost(site, path, relPath, targetDir, draft)
}
err = helpers.SymbolicWalk(hugofs.Os(), jekyllRoot, callback)
err = helpers.SymbolicWalk(hugofs.Os, jekyllRoot, callback)
if err != nil {
return err
@@ -137,7 +138,13 @@ func importFromJekyll(cmd *cobra.Command, args []string) error {
// TODO: Consider calling doNewSite() instead?
func createSiteFromJekyll(jekyllRoot, targetDir string, force bool) (*hugolib.Site, error) {
fs := hugofs.Source()
s, err := hugolib.NewSiteDefaultLang()
if err != nil {
return nil, err
}
fs := s.Fs.Source
if exists, _ := helpers.Exists(targetDir, fs); exists {
if isDir, _ := helpers.IsDir(targetDir, fs); !isDir {
return nil, errors.New("Target path \"" + targetDir + "\" already exists but not a directory")
@@ -150,7 +157,7 @@ func createSiteFromJekyll(jekyllRoot, targetDir string, force bool) (*hugolib.Si
}
}
jekyllConfig := loadJekyllConfig(jekyllRoot)
jekyllConfig := loadJekyllConfig(fs, jekyllRoot)
// Crude test to make sure at least one of _drafts/ and _posts/ exists
// and is not empty.
@@ -177,16 +184,14 @@ func createSiteFromJekyll(jekyllRoot, targetDir string, force bool) (*hugolib.Si
mkdir(targetDir, "data")
mkdir(targetDir, "themes")
createConfigFromJekyll(targetDir, "yaml", jekyllConfig)
createConfigFromJekyll(fs, targetDir, "yaml", jekyllConfig)
copyJekyllFilesAndFolders(jekyllRoot, filepath.Join(targetDir, "static"))
site := hugolib.NewSiteDefaultLang()
return site, nil
return s, nil
}
func loadJekyllConfig(jekyllRoot string) map[string]interface{} {
fs := hugofs.Source()
func loadJekyllConfig(fs afero.Fs, jekyllRoot string) map[string]interface{} {
path := filepath.Join(jekyllRoot, "_config.yml")
exists, err := helpers.Exists(path, fs)
@@ -218,7 +223,7 @@ func loadJekyllConfig(jekyllRoot string) map[string]interface{} {
return c.(map[string]interface{})
}
func createConfigFromJekyll(inpath string, kind string, jekyllConfig map[string]interface{}) (err error) {
func createConfigFromJekyll(fs afero.Fs, inpath string, kind string, jekyllConfig map[string]interface{}) (err error) {
title := "My New Hugo Site"
baseURL := "http://example.org/"
@@ -251,7 +256,7 @@ func createConfigFromJekyll(inpath string, kind string, jekyllConfig map[string]
return err
}
err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), hugofs.Source())
err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), fs)
if err != nil {
return
}

View File

@@ -16,15 +16,18 @@ package commands
import (
"bytes"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/hugo/create"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/hugolib"
"github.com/spf13/hugo/parser"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
@@ -84,7 +87,9 @@ as you see fit.`,
// NewContent adds new content to a Hugo site.
func NewContent(cmd *cobra.Command, args []string) error {
if _, err := InitializeConfig(); err != nil {
cfg, err := InitializeConfig()
if err != nil {
return err
}
@@ -110,10 +115,16 @@ func NewContent(cmd *cobra.Command, args []string) error {
kind = contentType
}
return create.NewContent(hugofs.Source(), kind, createpath)
s, err := hugolib.NewSite(cfg)
if err != nil {
return newSystemError(err)
}
return create.NewContent(s, kind, createpath)
}
func doNewSite(basepath string, force bool) error {
func doNewSite(fs *hugofs.Fs, basepath string, force bool) error {
dirs := []string{
filepath.Join(basepath, "layouts"),
filepath.Join(basepath, "content"),
@@ -123,12 +134,12 @@ func doNewSite(basepath string, force bool) error {
filepath.Join(basepath, "themes"),
}
if exists, _ := helpers.Exists(basepath, hugofs.Source()); exists {
if isDir, _ := helpers.IsDir(basepath, hugofs.Source()); !isDir {
if exists, _ := helpers.Exists(basepath, fs.Source); exists {
if isDir, _ := helpers.IsDir(basepath, fs.Source); !isDir {
return errors.New(basepath + " already exists but not a directory")
}
isEmpty, _ := helpers.IsEmpty(basepath, hugofs.Source())
isEmpty, _ := helpers.IsEmpty(basepath, fs.Source)
switch {
case !isEmpty && !force:
@@ -137,7 +148,7 @@ func doNewSite(basepath string, force bool) error {
case !isEmpty && force:
all := append(dirs, filepath.Join(basepath, "config."+configFormat))
for _, path := range all {
if exists, _ := helpers.Exists(path, hugofs.Source()); exists {
if exists, _ := helpers.Exists(path, fs.Source); exists {
return errors.New(path + " already exists")
}
}
@@ -145,10 +156,12 @@ func doNewSite(basepath string, force bool) error {
}
for _, dir := range dirs {
hugofs.Source().MkdirAll(dir, 0777)
if err := fs.Source.MkdirAll(dir, 0777); err != nil {
return fmt.Errorf("Failed to create dir: %s", err)
}
}
createConfig(basepath, configFormat)
createConfig(fs, basepath, configFormat)
jww.FEEDBACK.Printf("Congratulations! Your new Hugo site is created in %s.\n\n", basepath)
jww.FEEDBACK.Println(nextStepsText())
@@ -190,12 +203,14 @@ func NewSite(cmd *cobra.Command, args []string) error {
forceNew, _ := cmd.Flags().GetBool("force")
return doNewSite(createpath, forceNew)
return doNewSite(hugofs.NewDefault(), createpath, forceNew)
}
// NewTheme creates a new Hugo theme.
func NewTheme(cmd *cobra.Command, args []string) error {
if _, err := InitializeConfig(); err != nil {
cfg, err := InitializeConfig()
if err != nil {
return err
}
@@ -207,26 +222,26 @@ func NewTheme(cmd *cobra.Command, args []string) error {
createpath := helpers.AbsPathify(filepath.Join(viper.GetString("themesDir"), args[0]))
jww.INFO.Println("creating theme at", createpath)
if x, _ := helpers.Exists(createpath, hugofs.Source()); x {
if x, _ := helpers.Exists(createpath, cfg.Fs.Source); x {
return newUserError(createpath, "already exists")
}
mkdir(createpath, "layouts", "_default")
mkdir(createpath, "layouts", "partials")
touchFile(createpath, "layouts", "index.html")
touchFile(createpath, "layouts", "404.html")
touchFile(createpath, "layouts", "_default", "list.html")
touchFile(createpath, "layouts", "_default", "single.html")
touchFile(cfg.Fs.Source, createpath, "layouts", "index.html")
touchFile(cfg.Fs.Source, createpath, "layouts", "404.html")
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "list.html")
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "single.html")
touchFile(createpath, "layouts", "partials", "header.html")
touchFile(createpath, "layouts", "partials", "footer.html")
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "header.html")
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "footer.html")
mkdir(createpath, "archetypes")
archDefault := []byte("+++\n+++\n")
err := helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), hugofs.Source())
err = helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), cfg.Fs.Source)
if err != nil {
return err
}
@@ -256,12 +271,12 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
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.Source())
err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), cfg.Fs.Source)
if err != nil {
return err
}
createThemeMD(createpath)
createThemeMD(cfg.Fs, createpath)
return nil
}
@@ -275,16 +290,16 @@ func mkdir(x ...string) {
}
}
func touchFile(x ...string) {
func touchFile(fs afero.Fs, x ...string) {
inpath := filepath.Join(x...)
mkdir(filepath.Dir(inpath))
err := helpers.WriteToDisk(inpath, bytes.NewReader([]byte{}), hugofs.Source())
err := helpers.WriteToDisk(inpath, bytes.NewReader([]byte{}), fs)
if err != nil {
jww.FATAL.Fatalln(err)
}
}
func createThemeMD(inpath string) (err error) {
func createThemeMD(fs *hugofs.Fs, inpath string) (err error) {
by := []byte(`# theme.toml template for a Hugo theme
# See https://github.com/spf13/hugoThemes#themetoml for an example
@@ -309,7 +324,7 @@ min_version = 0.18
repo = ""
`)
err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), hugofs.Source())
err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), fs.Source)
if err != nil {
return
}
@@ -320,7 +335,7 @@ min_version = 0.18
func newContentPathSection(path string) (string, string) {
// Forward slashes is used in all examples. Convert if needed.
// Issue #1133
createpath := strings.Replace(path, "/", helpers.FilePathSeparator, -1)
createpath := filepath.FromSlash(path)
var section string
// assume the first directory is the section (kind)
if strings.Contains(createpath[1:], helpers.FilePathSeparator) {
@@ -330,7 +345,7 @@ func newContentPathSection(path string) (string, string) {
return createpath, section
}
func createConfig(inpath string, kind string) (err error) {
func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) {
in := map[string]interface{}{
"baseURL": "http://example.org/",
"title": "My New Hugo Site",
@@ -343,7 +358,7 @@ func createConfig(inpath string, kind string) (err error) {
return err
}
err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), hugofs.Source())
err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), fs.Source)
if err != nil {
return
}

View File

@@ -14,12 +14,12 @@
package commands
import (
"os"
"path/filepath"
"testing"
"github.com/spf13/hugo/hugofs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Issue #1133
@@ -29,7 +29,8 @@ func TestNewContentPathSectionWithForwardSlashes(t *testing.T) {
assert.Equal(t, "post", s)
}
func checkNewSiteInited(basepath string, t *testing.T) {
func checkNewSiteInited(fs *hugofs.Fs, basepath string, t *testing.T) {
paths := []string{
filepath.Join(basepath, "layouts"),
filepath.Join(basepath, "content"),
@@ -40,63 +41,70 @@ func checkNewSiteInited(basepath string, t *testing.T) {
}
for _, path := range paths {
_, err := hugofs.Source().Stat(path)
assert.Nil(t, err)
_, err := fs.Source.Stat(path)
require.NoError(t, err)
}
}
func TestDoNewSite(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
hugofs.InitMemFs()
err := doNewSite(basepath, false)
assert.Nil(t, err)
basepath := filepath.Join("base", "blog")
fs := hugofs.NewMem()
checkNewSiteInited(basepath, t)
require.NoError(t, doNewSite(fs, basepath, false))
checkNewSiteInited(fs, basepath, t)
}
func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
hugofs.InitMemFs()
hugofs.Source().MkdirAll(basepath, 777)
err := doNewSite(basepath, false)
assert.Nil(t, err)
basepath := filepath.Join("base", "blog")
fs := hugofs.NewMem()
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
require.NoError(t, doNewSite(fs, basepath, false))
}
func TestDoNewSite_error_base_exists(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
hugofs.InitMemFs()
hugofs.Source().MkdirAll(basepath, 777)
hugofs.Source().Create(filepath.Join(basepath, "foo"))
basepath := filepath.Join("base", "blog")
fs := hugofs.NewMem()
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
_, err := fs.Source.Create(filepath.Join(basepath, "foo"))
require.NoError(t, err)
// Since the directory already exists and isn't empty, expect an error
err := doNewSite(basepath, false)
assert.NotNil(t, err)
require.Error(t, doNewSite(fs, basepath, false))
}
func TestDoNewSite_force_empty_dir(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
hugofs.InitMemFs()
hugofs.Source().MkdirAll(basepath, 777)
err := doNewSite(basepath, true)
assert.Nil(t, err)
basepath := filepath.Join("base", "blog")
fs := hugofs.NewMem()
checkNewSiteInited(basepath, t)
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
require.NoError(t, doNewSite(fs, basepath, true))
checkNewSiteInited(fs, basepath, t)
}
func TestDoNewSite_error_force_dir_inside_exists(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
basepath := filepath.Join("base", "blog")
fs := hugofs.NewMem()
contentPath := filepath.Join(basepath, "content")
hugofs.InitMemFs()
hugofs.Source().MkdirAll(contentPath, 777)
err := doNewSite(basepath, true)
assert.NotNil(t, err)
require.NoError(t, fs.Source.MkdirAll(contentPath, 777))
require.Error(t, doNewSite(fs, basepath, true))
}
func TestDoNewSite_error_force_config_inside_exists(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
basepath := filepath.Join("base", "blog")
fs := hugofs.NewMem()
configPath := filepath.Join(basepath, "config.toml")
hugofs.InitMemFs()
hugofs.Source().MkdirAll(basepath, 777)
hugofs.Source().Create(configPath)
err := doNewSite(basepath, true)
assert.NotNil(t, err)
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
_, err := fs.Source.Create(configPath)
require.NoError(t, err)
require.Error(t, doNewSite(fs, basepath, true))
}

View File

@@ -29,7 +29,6 @@ import (
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
)
@@ -109,6 +108,8 @@ func server(cmd *cobra.Command, args []string) error {
return err
}
c := commandeer{cfg}
if flagChanged(cmd.Flags(), "disableLiveReload") {
viper.Set("disableLiveReload", disableLiveReload)
}
@@ -119,7 +120,7 @@ func server(cmd *cobra.Command, args []string) error {
if viper.GetBool("watch") {
serverWatch = true
watchConfig(cfg)
c.watchConfig()
}
l, err := net.Listen("tcp", net.JoinHostPort(serverInterface, strconv.Itoa(serverPort)))
@@ -157,18 +158,18 @@ func server(cmd *cobra.Command, args []string) error {
// Hugo writes the output to memory instead of the disk
if !renderToDisk {
hugofs.SetDestination(new(afero.MemMapFs))
cfg.Fs.Destination = new(afero.MemMapFs)
// Rendering to memoryFS, publish to Root regardless of publishDir.
viper.Set("publishDir", "/")
}
if err := build(cfg, serverWatch); err != nil {
if err := c.build(serverWatch); err != nil {
return err
}
// Watch runs its own server as part of the routine
if serverWatch {
watchDirs := getDirList()
watchDirs := c.getDirList()
baseWatchDir := viper.GetString("workingDir")
for i, dir := range watchDirs {
watchDirs[i], _ = helpers.GetRelativePath(dir, baseWatchDir)
@@ -177,26 +178,26 @@ func server(cmd *cobra.Command, args []string) error {
rootWatchDirs := strings.Join(helpers.UniqueStrings(helpers.ExtractRootPaths(watchDirs)), ",")
jww.FEEDBACK.Printf("Watching for changes in %s%s{%s}\n", baseWatchDir, helpers.FilePathSeparator, rootWatchDirs)
err := newWatcher(cfg, serverPort)
err := c.newWatcher(serverPort)
if err != nil {
return err
}
}
serve(serverPort)
c.serve(serverPort)
return nil
}
func serve(port int) {
func (c commandeer) serve(port int) {
if renderToDisk {
jww.FEEDBACK.Println("Serving pages from " + helpers.AbsPathify(viper.GetString("publishDir")))
} else {
jww.FEEDBACK.Println("Serving pages from memory")
}
httpFs := afero.NewHttpFs(hugofs.Destination())
httpFs := afero.NewHttpFs(c.Fs.Destination)
fs := filesOnlyFs{httpFs.Dir(helpers.AbsPathify(viper.GetString("publishDir")))}
fileserver := http.FileServer(fs)

View File

@@ -20,7 +20,6 @@ import (
"time"
"github.com/spf13/cobra"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/parser"
)
@@ -37,7 +36,9 @@ If the content's draft status is 'False', nothing is done.`,
// 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) error {
if _, err := InitializeConfig(); err != nil {
cfg, err := InitializeConfig()
if err != nil {
return err
}
@@ -47,7 +48,7 @@ func Undraft(cmd *cobra.Command, args []string) error {
location := args[0]
// open the file
f, err := hugofs.Source().Open(location)
f, err := cfg.Fs.Source.Open(location)
if err != nil {
return err
}
@@ -64,7 +65,7 @@ func Undraft(cmd *cobra.Command, args []string) error {
return newSystemErrorF("an error occurred while undrafting %q: %s", location, err)
}
f, err = hugofs.Source().OpenFile(location, os.O_WRONLY|os.O_TRUNC, 0644)
f, err = cfg.Fs.Source.OpenFile(location, os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return newSystemErrorF("%q not be undrafted due to error opening file to save changes: %q\n", location, err)
}