Fix partial rebuilds for SCSS fetched with GetMatch and similar

Fixes #12395
This commit is contained in:
Bjørn Erik Pedersen 2024-04-20 11:05:35 +02:00
parent da6112fc65
commit 004b694390
6 changed files with 114 additions and 33 deletions

View file

@ -140,16 +140,25 @@ func (c *Cache) DrainEvictedIdentities() []identity.Identity {
} }
// ClearMatching clears all partition for which the predicate returns true. // ClearMatching clears all partition for which the predicate returns true.
func (c *Cache) ClearMatching(predicate func(k, v any) bool) { func (c *Cache) ClearMatching(predicatePartition func(k string, p PartitionManager) bool, predicateValue func(k, v any) bool) {
if predicatePartition == nil {
predicatePartition = func(k string, p PartitionManager) bool { return true }
}
if predicateValue == nil {
panic("nil predicateValue")
}
g := rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{ g := rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{
NumWorkers: len(c.partitions), NumWorkers: len(c.partitions),
Handle: func(ctx context.Context, partition PartitionManager) error { Handle: func(ctx context.Context, partition PartitionManager) error {
partition.clearMatching(predicate) partition.clearMatching(predicateValue)
return nil return nil
}, },
}) })
for _, p := range c.partitions { for k, p := range c.partitions {
if !predicatePartition(k, p) {
continue
}
g.Enqueue(p) g.Enqueue(p)
} }
@ -356,6 +365,7 @@ func GetOrCreatePartition[K comparable, V any](c *Cache, name string, opts Optio
trace: c.opts.Log.Logger().WithLevel(logg.LevelTrace).WithField("partition", name), trace: c.opts.Log.Logger().WithLevel(logg.LevelTrace).WithField("partition", name),
opts: opts, opts: opts,
} }
c.partitions[name] = partition c.partitions[name] = partition
return partition return partition

View file

@ -156,7 +156,7 @@ func TestClear(t *testing.T) {
cache = newTestCache(t) cache = newTestCache(t)
cache.ClearMatching(func(k, v any) bool { cache.ClearMatching(nil, func(k, v any) bool {
return k.(string) == "clearOnRebuild" return k.(string) == "clearOnRebuild"
}) })

View file

@ -128,6 +128,7 @@ type rootCommand struct {
verbose bool verbose bool
debug bool debug bool
quiet bool quiet bool
devMode bool // Hidden flag.
renderToMemory bool renderToMemory bool
@ -423,6 +424,9 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) { func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
level := logg.LevelWarn level := logg.LevelWarn
if r.devMode {
level = logg.LevelTrace
} else {
if r.logLevel != "" { if r.logLevel != "" {
switch strings.ToLower(r.logLevel) { switch strings.ToLower(r.logLevel) {
case "debug": case "debug":
@ -448,6 +452,7 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
level = logg.LevelDebug level = logg.LevelDebug
} }
} }
}
optsLogger := loggers.Options{ optsLogger := loggers.Options{
DistinctLevel: logg.LevelWarn, DistinctLevel: logg.LevelWarn,
@ -505,10 +510,13 @@ Complete documentation is available at https://gohugo.io/.`
cmd.PersistentFlags().BoolVarP(&r.verbose, "verbose", "v", false, "verbose output") cmd.PersistentFlags().BoolVarP(&r.verbose, "verbose", "v", false, "verbose output")
cmd.PersistentFlags().BoolVarP(&r.debug, "debug", "", false, "debug output") cmd.PersistentFlags().BoolVarP(&r.debug, "debug", "", false, "debug output")
cmd.PersistentFlags().BoolVarP(&r.devMode, "devMode", "", false, "only used for internal testing, flag hidden.")
cmd.PersistentFlags().StringVar(&r.logLevel, "logLevel", "", "log level (debug|info|warn|error)") cmd.PersistentFlags().StringVar(&r.logLevel, "logLevel", "", "log level (debug|info|warn|error)")
_ = cmd.RegisterFlagCompletionFunc("logLevel", cobra.FixedCompletions([]string{"debug", "info", "warn", "error"}, cobra.ShellCompDirectiveNoFileComp)) _ = cmd.RegisterFlagCompletionFunc("logLevel", cobra.FixedCompletions([]string{"debug", "info", "warn", "error"}, cobra.ShellCompDirectiveNoFileComp))
cmd.Flags().BoolVarP(&r.buildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed") cmd.Flags().BoolVarP(&r.buildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
cmd.PersistentFlags().MarkHidden("devMode")
// Configure local flags // Configure local flags
applyLocalFlagsBuild(cmd, r) applyLocalFlagsBuild(cmd, r)

View file

@ -1084,7 +1084,7 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
return b return b
} }
h.MemCache.ClearMatching(shouldDelete) h.MemCache.ClearMatching(nil, shouldDelete)
return ll, nil return ll, nil
}); err != nil { }); err != nil {

View file

@ -23,6 +23,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"sync"
"time" "time"
"github.com/bep/logg" "github.com/bep/logg"
@ -46,6 +47,7 @@ import (
"github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/page/siteidentities" "github.com/gohugoio/hugo/resources/page/siteidentities"
"github.com/gohugoio/hugo/resources/postpub" "github.com/gohugoio/hugo/resources/postpub"
"github.com/gohugoio/hugo/resources/resource"
"github.com/spf13/afero" "github.com/spf13/afero"
@ -758,15 +760,45 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
} }
} }
case files.ComponentFolderAssets: case files.ComponentFolderAssets:
logger.Println("Asset changed", pathInfo.Path()) p := pathInfo.Path()
logger.Println("Asset changed", p)
var matches []any
var mu sync.Mutex
h.MemCache.ClearMatching(
func(k string, pm dynacache.PartitionManager) bool {
// Avoid going through everything.
return strings.HasPrefix(k, "/res")
},
func(k, v any) bool {
if strings.Contains(k.(string), p) {
mu.Lock()
defer mu.Unlock()
switch vv := v.(type) {
case resource.Resources:
// GetMatch/Match.
for _, r := range vv {
matches = append(matches, r)
}
return true
default:
matches = append(matches, vv)
return true
}
}
return false
})
var hasID bool var hasID bool
r, _ := h.ResourceSpec.ResourceCache.Get(context.Background(), dynacache.CleanKey(pathInfo.Base())) for _, r := range matches {
identity.WalkIdentitiesShallow(r, func(level int, rid identity.Identity) bool { identity.WalkIdentitiesShallow(r, func(level int, rid identity.Identity) bool {
hasID = true hasID = true
changes = append(changes, rid) changes = append(changes, rid)
return false return false
}) })
}
if !hasID { if !hasID {
changes = append(changes, pathInfo) changes = append(changes, pathInfo)
} }

View file

@ -327,3 +327,34 @@ Styles: {{ $r.RelPermalink }}
b.AssertFileContent("public/index.html", "Styles: /scss/main.css") b.AssertFileContent("public/index.html", "Styles: /scss/main.css")
} }
func TestRebuildAssetGetMatch(t *testing.T) {
t.Parallel()
if !scss.Supports() {
t.Skip()
}
files := `
-- assets/scss/main.scss --
b {
color: red;
}
-- layouts/index.html --
{{ $r := resources.GetMatch "scss/main.scss" | toCSS }}
T1: {{ $r.Content }}
`
b := hugolib.NewIntegrationTestBuilder(
hugolib.IntegrationTestConfig{
T: t,
TxtarString: files,
NeedsOsFS: true,
Running: true,
}).Build()
b.AssertFileContent("public/index.html", `color: red`)
b.EditFiles("assets/scss/main.scss", `b { color: blue; }`).Build()
b.AssertFileContent("public/index.html", `color: blue`)
}