Skip to content

Commit

Permalink
Handle themes in the new file cache (for images, assets)
Browse files Browse the repository at this point in the history
In the newly consolidated file cache implementation, we forgot that we also look in the theme(s) for assets (SCSS transformations etc.), which is not good for Netlify and the demo sites.

Fixes gohugoio#5460
  • Loading branch information
bep committed Nov 23, 2018
1 parent 5df2b79 commit 3e29898
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 87 deletions.
23 changes: 15 additions & 8 deletions cache/filecache/filecache.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"bytes"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
Expand All @@ -26,8 +27,6 @@ import (

"github.com/gohugoio/hugo/helpers"

"github.com/gohugoio/hugo/hugolib/paths"

"github.com/BurntSushi/locker"
"github.com/spf13/afero"
)
Expand Down Expand Up @@ -307,20 +306,26 @@ func (f Caches) Get(name string) *Cache {

// NewCachesFromPaths creates a new set of file caches from the given
// configuration.
func NewCachesFromPaths(p *paths.Paths) (Caches, error) {
func NewCachesFromPaths(p *helpers.PathSpec) (Caches, error) {
dcfg, err := decodeConfig(p)
if err != nil {
return nil, err
}

genDir := filepath.FromSlash("/_gen")

fs := p.Fs.Source

m := make(Caches)
for k, v := range dcfg {
var cfs afero.Fs

if v.isResourceDir {
cfs = p.BaseFs.Resources.Fs
} else {
cfs = fs
}

var baseDir string
if !strings.Contains(v.Dir, genDir) {
if !strings.HasPrefix(v.Dir, "_gen") {
// We do cache eviction (file removes) and since the user can set
// his/hers own cache directory, we really want to make sure
// we do not delete any files that do not belong to this cache.
Expand All @@ -331,10 +336,12 @@ func NewCachesFromPaths(p *paths.Paths) (Caches, error) {
} else {
baseDir = filepath.Join(v.Dir, k)
}
if err = fs.MkdirAll(baseDir, 0777); err != nil {
if err = cfs.MkdirAll(baseDir, 0777); err != nil && !os.IsExist(err) {
return nil, err
}
bfs := afero.NewBasePathFs(fs, baseDir)

bfs := afero.NewBasePathFs(cfs, baseDir)

m[k] = NewCache(bfs, v.MaxAge)
}

Expand Down
37 changes: 23 additions & 14 deletions cache/filecache/filecache_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"time"

"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugolib/paths"

"github.com/bep/mapstructure"
"github.com/pkg/errors"
Expand Down Expand Up @@ -68,6 +67,10 @@ type cacheConfig struct {

// The directory where files are stored.
Dir string

// Will resources/_gen will get its own composite filesystem that
// also checks any theme.
isResourceDir bool
}

// GetJSONCache gets the file cache for getJSON.
Expand All @@ -90,7 +93,7 @@ func (f Caches) AssetsCache() *Cache {
return f[cacheKeyAssets]
}

func decodeConfig(p *paths.Paths) (cachesConfig, error) {
func decodeConfig(p *helpers.PathSpec) (cachesConfig, error) {
c := make(cachesConfig)
valid := make(map[string]bool)
// Add defaults
Expand Down Expand Up @@ -145,10 +148,13 @@ func decodeConfig(p *paths.Paths) (cachesConfig, error) {

for i, part := range parts {
if strings.HasPrefix(part, ":") {
resolved, err := resolveDirPlaceholder(p, part)
resolved, isResource, err := resolveDirPlaceholder(p, part)
if err != nil {
return c, err
}
if isResource {
v.isResourceDir = true
}
parts[i] = resolved
}
}
Expand All @@ -159,13 +165,15 @@ func decodeConfig(p *paths.Paths) (cachesConfig, error) {
}
v.Dir = filepath.Clean(filepath.FromSlash(dir))

if isOsFs && !filepath.IsAbs(v.Dir) {
return c, errors.Errorf("%q must resolve to an absolute directory", v.Dir)
}
if !v.isResourceDir {
if isOsFs && !filepath.IsAbs(v.Dir) {
return c, errors.Errorf("%q must resolve to an absolute directory", v.Dir)
}

// Avoid cache in root, e.g. / (Unix) or c:\ (Windows)
if len(strings.TrimPrefix(v.Dir, filepath.VolumeName(v.Dir))) == 1 {
return c, errors.Errorf("%q is a root folder and not allowed as cache dir", v.Dir)
// Avoid cache in root, e.g. / (Unix) or c:\ (Windows)
if len(strings.TrimPrefix(v.Dir, filepath.VolumeName(v.Dir))) == 1 {
return c, errors.Errorf("%q is a root folder and not allowed as cache dir", v.Dir)
}
}

if disabled {
Expand All @@ -179,15 +187,16 @@ func decodeConfig(p *paths.Paths) (cachesConfig, error) {
}

// Resolves :resourceDir => /myproject/resources etc., :cacheDir => ...
func resolveDirPlaceholder(p *paths.Paths, placeholder string) (string, error) {
func resolveDirPlaceholder(p *helpers.PathSpec, placeholder string) (cacheDir string, isResource bool, err error) {
switch strings.ToLower(placeholder) {
case ":resourcedir":
return p.AbsResourcesDir, nil
return "", true, nil
case ":cachedir":
return helpers.GetCacheDir(p.Fs.Source, p.Cfg)
d, err := helpers.GetCacheDir(p.Fs.Source, p.Cfg)
return d, false, err
case ":project":
return filepath.Base(p.WorkingDir), nil
return filepath.Base(p.WorkingDir), false, nil
}

return "", errors.Errorf("%q is not a valid placeholder (valid values are :cacheDir or :resourceDir)", placeholder)
return "", false, errors.Errorf("%q is not a valid placeholder (valid values are :cacheDir or :resourceDir)", placeholder)
}
61 changes: 51 additions & 10 deletions cache/filecache/filecache_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import (
"testing"
"time"

"github.com/gohugoio/hugo/helpers"

"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib/paths"

"github.com/spf13/viper"
"github.com/stretchr/testify/require"
Expand All @@ -35,6 +36,13 @@ func TestDecodeConfig(t *testing.T) {

configStr := `
resourceDir = "myresources"
contentDir = "content"
dataDir = "data"
i18nDir = "i18n"
layoutDir = "layouts"
assetDir = "assets"
archetypeDir = "archetypes"
[caches]
[caches.getJSON]
maxAge = "10m"
Expand All @@ -50,7 +58,7 @@ dir = "/path/to/c3"
cfg, err := config.FromConfigString(configStr, "toml")
assert.NoError(err)
fs := hugofs.NewMem(cfg)
p, err := paths.New(fs, cfg)
p, err := helpers.NewPathSpec(fs, cfg)
assert.NoError(err)

decoded, err := decodeConfig(p)
Expand All @@ -75,6 +83,13 @@ func TestDecodeConfigIgnoreCache(t *testing.T) {

configStr := `
resourceDir = "myresources"
contentDir = "content"
dataDir = "data"
i18nDir = "i18n"
layoutDir = "layouts"
assetDir = "assets"
archeTypedir = "archetypes"
ignoreCache = true
[caches]
[caches.getJSON]
Expand All @@ -91,7 +106,7 @@ dir = "/path/to/c3"
cfg, err := config.FromConfigString(configStr, "toml")
assert.NoError(err)
fs := hugofs.NewMem(cfg)
p, err := paths.New(fs, cfg)
p, err := helpers.NewPathSpec(fs, cfg)
assert.NoError(err)

decoded, err := decodeConfig(p)
Expand All @@ -107,8 +122,7 @@ dir = "/path/to/c3"

func TestDecodeConfigDefault(t *testing.T) {
assert := require.New(t)
cfg := viper.New()
cfg.Set("workingDir", filepath.FromSlash("/my/cool/hugoproject"))
cfg := newTestConfig()

if runtime.GOOS == "windows" {
cfg.Set("resourceDir", "c:\\cache\\resources")
Expand All @@ -120,7 +134,7 @@ func TestDecodeConfigDefault(t *testing.T) {
}

fs := hugofs.NewMem(cfg)
p, err := paths.New(fs, cfg)
p, err := helpers.NewPathSpec(fs, cfg)
assert.NoError(err)

decoded, err := decodeConfig(p)
Expand All @@ -129,12 +143,18 @@ func TestDecodeConfigDefault(t *testing.T) {

assert.Equal(4, len(decoded))

imgConfig := decoded[cacheKeyImages]
jsonConfig := decoded[cacheKeyGetJSON]

if runtime.GOOS == "windows" {
assert.Equal("c:\\cache\\resources\\_gen", decoded[cacheKeyImages].Dir)
assert.Equal("_gen", imgConfig.Dir)
} else {
assert.Equal("/cache/resources/_gen", decoded[cacheKeyImages].Dir)
assert.Equal("/cache/thecache/hugoproject", decoded[cacheKeyGetJSON].Dir)
assert.Equal("_gen", imgConfig.Dir)
assert.Equal("/cache/thecache/hugoproject", jsonConfig.Dir)
}

assert.True(imgConfig.isResourceDir)
assert.False(jsonConfig.isResourceDir)
}

func TestDecodeConfigInvalidDir(t *testing.T) {
Expand All @@ -144,6 +164,13 @@ func TestDecodeConfigInvalidDir(t *testing.T) {

configStr := `
resourceDir = "myresources"
contentDir = "content"
dataDir = "data"
i18nDir = "i18n"
layoutDir = "layouts"
assetDir = "assets"
archeTypedir = "archetypes"
[caches]
[caches.getJSON]
maxAge = "10m"
Expand All @@ -157,10 +184,24 @@ dir = "/"
cfg, err := config.FromConfigString(configStr, "toml")
assert.NoError(err)
fs := hugofs.NewMem(cfg)
p, err := paths.New(fs, cfg)
p, err := helpers.NewPathSpec(fs, cfg)
assert.NoError(err)

_, err = decodeConfig(p)
assert.Error(err)

}

func newTestConfig() *viper.Viper {
cfg := viper.New()
cfg.Set("workingDir", filepath.FromSlash("/my/cool/hugoproject"))
cfg.Set("contentDir", "content")
cfg.Set("dataDir", "data")
cfg.Set("resourceDir", "resources")
cfg.Set("i18nDir", "i18n")
cfg.Set("layoutDir", "layouts")
cfg.Set("archetypeDir", "archetypes")
cfg.Set("assetDir", "assets")

return cfg
}
Loading

0 comments on commit 3e29898

Please sign in to comment.