1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-12-11 21:29:38 +01:00
gojekyll/cache/cache.go

85 lines
2.2 KiB
Go
Raw Normal View History

2017-07-10 00:19:22 +02:00
package cache
2017-07-01 23:16:46 +02:00
import (
2017-07-02 00:11:35 +02:00
"crypto/md5" // nolint: gas
2017-07-01 23:16:46 +02:00
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
2017-07-14 01:01:44 +02:00
"sync"
2017-07-01 23:16:46 +02:00
)
2017-07-21 22:28:42 +02:00
var enabled = true
2017-07-14 01:01:44 +02:00
var cacheMx sync.Mutex
func init() {
s := os.Getenv("GOJEKYLL_DISABLE_CACHE")
if s != "" && s != "0" && s != "false" {
2017-07-21 22:28:42 +02:00
enabled = false
}
}
2017-07-20 17:32:52 +02:00
func cacheDir() string {
return filepath.Join(os.TempDir(), os.ExpandEnv("gojekyll-$USER"))
}
2017-07-21 22:28:42 +02:00
// Clear clears the cache. It's used for testing.
func Clear() error {
2017-07-20 17:32:52 +02:00
return os.RemoveAll(cacheDir())
}
2017-07-21 22:28:42 +02:00
// Enable enables the cache; for testing.
func Enable() {
enabled = true
}
// Disable disables the cache; for testing.
func Disable() {
enabled = false
}
2017-07-10 00:19:22 +02:00
// WithFile looks (header, content) up in a user-specific file cache.
2017-07-01 23:16:46 +02:00
// If found, it writes the file contents. Else it calls fn to write to
// both the writer and the file system.
//
// header and content are distinct parameters to relieve the caller from
// having to concatenate them.
2017-07-10 00:19:22 +02:00
func WithFile(header string, content string, fn func() (string, error)) (string, error) {
2021-06-16 09:48:07 +02:00
h := md5.New() // nolint: gas
2017-07-02 00:11:35 +02:00
io.WriteString(h, content) // nolint: errcheck, gas
io.WriteString(h, "\n") // nolint: errcheck, gas
io.WriteString(h, header) // nolint: errcheck, gas
2017-07-01 23:16:46 +02:00
sum := h.Sum(nil)
// don't use ioutil.TempDir, because we want this to last across invocations
2017-07-20 17:32:52 +02:00
cachedir := cacheDir()
2017-07-03 03:08:09 +02:00
cachefile := filepath.Join(cachedir, fmt.Sprintf("%x%c%x", sum[:1], filepath.Separator, sum[1:]))
2017-07-01 23:16:46 +02:00
// ignore errors; if there's a missing file we don't care, and if it's
2017-07-21 22:28:42 +02:00
// another error we'll pick it up during write.
//
2017-07-01 23:16:46 +02:00
// WriteFile truncates the file before writing it, so ignore empty files.
// If the writer actually wrote an empty file, we'll end up gratuitously
// re-running it, which is okay.
2017-07-21 22:28:42 +02:00
//
// Do as much work as possible before checking if the cache is enabled, to
// minimize code paths and timing differences.
if b, err := ioutil.ReadFile(cachefile); err == nil && len(b) > 0 && enabled {
2017-07-04 14:06:34 +02:00
return string(b), err
2017-07-01 23:16:46 +02:00
}
2017-07-04 14:06:34 +02:00
s, err := fn()
if err != nil {
return "", err
2017-07-01 23:16:46 +02:00
}
2017-07-03 03:08:09 +02:00
if err := os.MkdirAll(filepath.Dir(cachefile), 0700); err != nil {
2017-07-04 14:06:34 +02:00
return "", err
2017-07-01 23:16:46 +02:00
}
2017-07-14 01:01:44 +02:00
defer cacheMx.Unlock()
cacheMx.Lock()
2017-07-04 14:06:34 +02:00
if err := ioutil.WriteFile(cachefile, []byte(s), 0600); err != nil {
return "", err
2017-07-01 23:16:46 +02:00
}
2017-07-04 14:06:34 +02:00
return s, nil
2017-07-01 23:16:46 +02:00
}