mirror of
https://github.com/gohugoio/hugo.git
synced 2024-12-27 21:32:21 +00:00
cache/filecache: Add a :project placeholder
This allows for "cache per Hugo project", making `hugo --gc` work as expected, even if you have several Hugo projects running on the same PC. See #5439
This commit is contained in:
parent
3c29c5af8e
commit
94f0f7e597
5 changed files with 66 additions and 30 deletions
5
cache/filecache/filecache.go
vendored
5
cache/filecache/filecache.go
vendored
|
@ -272,7 +272,10 @@ func (c *Cache) getOrRemove(id string) hugio.ReadSeekCloser {
|
|||
}
|
||||
|
||||
func (c *Cache) isExpired(modTime time.Time) bool {
|
||||
return c.maxAge >= 0 && time.Now().Sub(modTime) > c.maxAge
|
||||
if c.maxAge < 0 {
|
||||
return false
|
||||
}
|
||||
return c.maxAge == 0 || time.Now().Sub(modTime) > c.maxAge
|
||||
}
|
||||
|
||||
// For testing
|
||||
|
|
41
cache/filecache/filecache_config.go
vendored
41
cache/filecache/filecache_config.go
vendored
|
@ -35,7 +35,7 @@ const (
|
|||
|
||||
var defaultCacheConfig = cacheConfig{
|
||||
MaxAge: -1, // Never expire
|
||||
Dir: ":cacheDir",
|
||||
Dir: ":cacheDir/:project",
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -139,26 +139,33 @@ func decodeConfig(p *paths.Paths) (cachesConfig, error) {
|
|||
disabled := cfg.GetBool("ignoreCache")
|
||||
|
||||
for k, v := range c {
|
||||
v.Dir = filepath.Clean(v.Dir)
|
||||
dir := filepath.ToSlash(v.Dir)
|
||||
dir := filepath.ToSlash(filepath.Clean(v.Dir))
|
||||
hadSlash := strings.HasPrefix(dir, "/")
|
||||
parts := strings.Split(dir, "/")
|
||||
first := parts[0]
|
||||
|
||||
if strings.HasPrefix(first, ":") {
|
||||
resolved, err := resolveDirPlaceholder(p, first)
|
||||
if err != nil {
|
||||
return c, err
|
||||
for i, part := range parts {
|
||||
if strings.HasPrefix(part, ":") {
|
||||
resolved, err := resolveDirPlaceholder(p, part)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
parts[i] = resolved
|
||||
}
|
||||
resolved = filepath.ToSlash(resolved)
|
||||
|
||||
v.Dir = filepath.FromSlash(path.Join((append([]string{resolved}, parts[1:]...))...))
|
||||
|
||||
} else if isOsFs && !path.IsAbs(dir) {
|
||||
return c, errors.Errorf("%q must either start with a placeholder (e.g. :cacheDir, :resourceDir) or be absolute", v.Dir)
|
||||
}
|
||||
|
||||
if len(v.Dir) < 5 {
|
||||
return c, errors.Errorf("%q is not a valid cache dir", v.Dir)
|
||||
dir = path.Join(parts...)
|
||||
if hadSlash {
|
||||
dir = "/" + dir
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -178,6 +185,8 @@ func resolveDirPlaceholder(p *paths.Paths, placeholder string) (string, error) {
|
|||
return p.AbsResourcesDir, nil
|
||||
case ":cachedir":
|
||||
return helpers.GetCacheDir(p.Fs.Source, p.Cfg)
|
||||
case ":project":
|
||||
return filepath.Base(p.WorkingDir), nil
|
||||
}
|
||||
|
||||
return "", errors.Errorf("%q is not a valid placeholder (valid values are :cacheDir or :resourceDir)", placeholder)
|
||||
|
|
32
cache/filecache/filecache_config_test.go
vendored
32
cache/filecache/filecache_config_test.go
vendored
|
@ -16,6 +16,7 @@ package filecache
|
|||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -107,6 +108,8 @@ dir = "/path/to/c3"
|
|||
func TestDecodeConfigDefault(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
cfg := viper.New()
|
||||
cfg.Set("workingDir", filepath.FromSlash("/my/cool/hugoproject"))
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
cfg.Set("resourceDir", "c:\\cache\\resources")
|
||||
cfg.Set("cacheDir", "c:\\cache\\thecache")
|
||||
|
@ -130,5 +133,34 @@ func TestDecodeConfigDefault(t *testing.T) {
|
|||
assert.Equal("c:\\cache\\resources\\_gen", decoded[cacheKeyImages].Dir)
|
||||
} else {
|
||||
assert.Equal("/cache/resources/_gen", decoded[cacheKeyImages].Dir)
|
||||
assert.Equal("/cache/thecache/hugoproject", decoded[cacheKeyGetJSON].Dir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeConfigInvalidDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := require.New(t)
|
||||
|
||||
configStr := `
|
||||
resourceDir = "myresources"
|
||||
[caches]
|
||||
[caches.getJSON]
|
||||
maxAge = "10m"
|
||||
dir = "/"
|
||||
|
||||
`
|
||||
if runtime.GOOS == "windows" {
|
||||
configStr = strings.Replace(configStr, "/", "c:\\\\", 1)
|
||||
}
|
||||
|
||||
cfg, err := config.FromConfigString(configStr, "toml")
|
||||
assert.NoError(err)
|
||||
fs := hugofs.NewMem(cfg)
|
||||
p, err := paths.New(fs, cfg)
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = decodeConfig(p)
|
||||
assert.Error(err)
|
||||
|
||||
}
|
||||
|
|
|
@ -413,10 +413,10 @@ Since Hugo 0.52 you can configure more than just the `cacheDir`. This is the def
|
|||
```toml
|
||||
[caches]
|
||||
[caches.getjson]
|
||||
dir = ":cacheDir"
|
||||
dir = ":cacheDir/:project"
|
||||
maxAge = -1
|
||||
[caches.getcsv]
|
||||
dir = ":cacheDir"
|
||||
dir = ":cacheDir/:project"
|
||||
maxAge = -1
|
||||
[caches.images]
|
||||
dir = ":resourceDir/_gen"
|
||||
|
@ -434,6 +434,9 @@ You can override any of these cache setting in your own `config.toml`.
|
|||
:cacheDir
|
||||
: This is the value of the `cacheDir` config option if set (can also be set via OS env variable `HUGO_CACHEDIR`). It will fall back to `/opt/build/cache/hugo_cache/` on Netlify, or a `hugo_cache` directory below the OS temp dir for the others. This means that if you run your builds on Netlify, all caches configured with `:cacheDir` will be saved and restored on the next build. For other CI vendors, please read their documentation. For an CircleCI example, see [this configuration](https://github.com/bep/hugo-sass-test/blob/6c3960a8f4b90e8938228688bc49bdcdd6b2d99e/.circleci/config.yml).
|
||||
|
||||
:project
|
||||
|
||||
The base directory name of the current Hugo project. This means that, in its default setting, every project will have separated file caches, which means that when you do `hugo --gc` you will not touch files related to other Hugo projects running on the same PC.
|
||||
|
||||
:resourceDir
|
||||
: This is the value of the `resourceDir` config option.
|
||||
|
|
|
@ -15,7 +15,6 @@ package hugolib
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -25,7 +24,6 @@ import (
|
|||
"github.com/gohugoio/hugo/helpers"
|
||||
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -351,15 +349,6 @@ func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
|
|||
|
||||
}
|
||||
|
||||
func TestNewSiteDefaultLang(t *testing.T) {
|
||||
t.Parallel()
|
||||
defer os.Remove("resources")
|
||||
s, err := NewSiteDefaultLang()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hugofs.Os, s.Fs.Source)
|
||||
require.Equal(t, hugofs.Os, s.Fs.Destination)
|
||||
}
|
||||
|
||||
// Issue #3355
|
||||
func TestShouldNotWriteZeroLengthFilesToDestination(t *testing.T) {
|
||||
cfg, fs := newTestCfg()
|
||||
|
|
Loading…
Reference in a new issue