mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-19 21:21:39 +02:00
Add a consolidated file cache
This commits reworks how file caching is performed in Hugo. Now there is only one way, and it can be configured.
This is the default configuration:
```toml
[caches]
[caches.getjson]
dir = ":cacheDir"
maxAge = -1
[caches.getcsv]
dir = ":cacheDir"
maxAge = -1
[caches.images]
dir = ":resourceDir/_gen"
maxAge = -1
[caches.assets]
dir = ":resourceDir/_gen"
maxAge = -1
```
You can override any of these cache setting in your own `config.toml`.
The placeholders explained:
`: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.
`:resourceDir`: This is the value of the `resourceDir` config option.
`maxAge` is the time in seconds before a cache entry will be evicted, -1 means forever and 0 effectively turns that particular cache off.
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 6c3960a8f4/.circleci/config.yml
Fixes #5404
This commit is contained in:
108
tpl/data/data.go
108
tpl/data/data.go
@@ -20,17 +20,20 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/cache/filecache"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
_errors "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// New returns a new instance of the data-namespaced template functions.
|
||||
func New(deps *deps.Deps) *Namespace {
|
||||
|
||||
return &Namespace{
|
||||
deps: deps,
|
||||
client: http.DefaultClient,
|
||||
deps: deps,
|
||||
cacheGetCSV: deps.FileCaches.GetCSVCache(),
|
||||
cacheGetJSON: deps.FileCaches.GetJSONCache(),
|
||||
client: http.DefaultClient,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +41,9 @@ func New(deps *deps.Deps) *Namespace {
|
||||
type Namespace struct {
|
||||
deps *deps.Deps
|
||||
|
||||
cacheGetJSON *filecache.Cache
|
||||
cacheGetCSV *filecache.Cache
|
||||
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
@@ -48,40 +54,34 @@ type Namespace struct {
|
||||
// GetCSV returns nil or a slice slice to use in a short code.
|
||||
func (ns *Namespace) GetCSV(sep string, urlParts ...string) (d [][]string, err error) {
|
||||
url := strings.Join(urlParts, "")
|
||||
cache := ns.cacheGetCSV
|
||||
|
||||
var clearCacheSleep = func(i int, u string) {
|
||||
ns.deps.Log.INFO.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
|
||||
time.Sleep(resSleep)
|
||||
deleteCache(url, ns.deps.Fs.Source, ns.deps.Cfg)
|
||||
}
|
||||
|
||||
for i := 0; i <= resRetries; i++ {
|
||||
var req *http.Request
|
||||
req, err = http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, _errors.Wrapf(err, "failed to create request for getCSV for resource %s", url)
|
||||
unmarshal := func(b []byte) (error, bool) {
|
||||
if !bytes.Contains(b, []byte(sep)) {
|
||||
return _errors.Errorf("cannot find separator %s in CSV for %s", sep, url), false
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", "text/csv")
|
||||
req.Header.Add("Accept", "text/plain")
|
||||
|
||||
var c []byte
|
||||
c, err = ns.getResource(req)
|
||||
if err != nil {
|
||||
return nil, _errors.Wrapf(err, "failed to read CSV resource %q", url)
|
||||
}
|
||||
|
||||
if !bytes.Contains(c, []byte(sep)) {
|
||||
return nil, _errors.Errorf("cannot find separator %s in CSV for %s", sep, url)
|
||||
}
|
||||
|
||||
if d, err = parseCSV(c, sep); err != nil {
|
||||
if d, err = parseCSV(b, sep); err != nil {
|
||||
err = _errors.Wrapf(err, "failed to parse CSV file %s", url)
|
||||
|
||||
clearCacheSleep(i, url)
|
||||
continue
|
||||
return err, true
|
||||
}
|
||||
break
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
var req *http.Request
|
||||
req, err = http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, _errors.Wrapf(err, "failed to create request for getCSV for resource %s", url)
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", "text/csv")
|
||||
req.Header.Add("Accept", "text/plain")
|
||||
|
||||
err = ns.getResource(cache, unmarshal, req)
|
||||
if err != nil {
|
||||
return nil, _errors.Wrapf(err, "failed to read CSV resource %q", url)
|
||||
}
|
||||
|
||||
return
|
||||
@@ -90,38 +90,34 @@ func (ns *Namespace) GetCSV(sep string, urlParts ...string) (d [][]string, err e
|
||||
// GetJSON expects one or n-parts of a URL to a resource which can either be a local or a remote one.
|
||||
// If you provide multiple parts they will be joined together to the final URL.
|
||||
// GetJSON returns nil or parsed JSON to use in a short code.
|
||||
func (ns *Namespace) GetJSON(urlParts ...string) (v interface{}, err error) {
|
||||
func (ns *Namespace) GetJSON(urlParts ...string) (interface{}, error) {
|
||||
var v interface{}
|
||||
url := strings.Join(urlParts, "")
|
||||
cache := ns.cacheGetJSON
|
||||
|
||||
for i := 0; i <= resRetries; i++ {
|
||||
var req *http.Request
|
||||
req, err = http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, _errors.Wrapf(err, "Failed to create request for getJSON resource %s", url)
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", "application/json")
|
||||
|
||||
var c []byte
|
||||
c, err = ns.getResource(req)
|
||||
if err != nil {
|
||||
return nil, _errors.Wrapf(err, "failed to get getJSON resource %q", url)
|
||||
}
|
||||
err = json.Unmarshal(c, &v)
|
||||
if err != nil {
|
||||
ns.deps.Log.INFO.Printf("Cannot read JSON from resource %s: %s", url, err)
|
||||
ns.deps.Log.INFO.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
|
||||
time.Sleep(resSleep)
|
||||
deleteCache(url, ns.deps.Fs.Source, ns.deps.Cfg)
|
||||
continue
|
||||
}
|
||||
break
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, _errors.Wrapf(err, "Failed to create request for getJSON resource %s", url)
|
||||
}
|
||||
|
||||
unmarshal := func(b []byte) (error, bool) {
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", "application/json")
|
||||
|
||||
err = ns.getResource(cache, unmarshal, req)
|
||||
|
||||
if err != nil {
|
||||
return nil, _errors.Wrapf(err, "failed to get getJSON resource %q", url)
|
||||
}
|
||||
return
|
||||
|
||||
return v, nil
|
||||
|
||||
}
|
||||
|
||||
// parseCSV parses bytes of CSV data into a slice slice string or an error
|
||||
|
Reference in New Issue
Block a user