From 96144dcea268521ee975aeecf3091b48485e25db Mon Sep 17 00:00:00 2001 From: pedrofrxncx Date: Mon, 10 Aug 2026 14:20:44 -0300 Subject: [PATCH] fix(sandbox): read the golden's own mtime for TTL pruning, not its lockDir's --- .../daemon-go/internal/setup/golden.go | 9 ++++-- .../daemon-go/internal/setup/golden_test.go | 32 +++++++++++++++++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/packages/sandbox/daemon-go/internal/setup/golden.go b/packages/sandbox/daemon-go/internal/setup/golden.go index 0f4c0fd159..12000646e4 100644 --- a/packages/sandbox/daemon-go/internal/setup/golden.go +++ b/packages/sandbox/daemon-go/internal/setup/golden.go @@ -275,11 +275,16 @@ func pruneGoldens(cacheRoot string, ttl time.Duration, maxPerRepo int, now time. if strings.HasPrefix(name.Name(), goldenTmpPrefix) { continue // in-flight publish } - info, err := name.Info() + lockDir := filepath.Join(repoDir, name.Name()) + // TryRestoreGolden touches the mtime of /node_modules, not the + // lockDir itself — a rename never touches it again after publish, so + // reading the lockDir's own mtime here would make the TTL reap an + // actively-restored golden right out from under a running fleet. + info, err := os.Stat(filepath.Join(lockDir, "node_modules")) if err != nil { continue } - entries = append(entries, entry{filepath.Join(repoDir, name.Name()), info.ModTime()}) + entries = append(entries, entry{lockDir, info.ModTime()}) } // Newest first; anything past the cap or older than the TTL is pruned. sort.Slice(entries, func(i, j int) bool { return entries[i].mtime.After(entries[j].mtime) }) diff --git a/packages/sandbox/daemon-go/internal/setup/golden_test.go b/packages/sandbox/daemon-go/internal/setup/golden_test.go index 2bd2111337..338fa79c35 100644 --- a/packages/sandbox/daemon-go/internal/setup/golden_test.go +++ b/packages/sandbox/daemon-go/internal/setup/golden_test.go @@ -152,13 +152,15 @@ func TestSameFilesystem(t *testing.T) { } func TestPruneGoldens(t *testing.T) { - // A golden dir /golden// with a given mtime. + // A golden dir /golden///node_modules with a given mtime — + // the mtime TryRestoreGolden actually touches, not the lockDir's own. mkGolden := func(t *testing.T, root, repo, name string, mtime time.Time) string { dir := filepath.Join(root, "golden", repo, name) - if err := os.MkdirAll(dir, 0o755); err != nil { + nodeModules := filepath.Join(dir, "node_modules") + if err := os.MkdirAll(nodeModules, 0o755); err != nil { t.Fatal(err) } - if err := os.Chtimes(dir, mtime, mtime); err != nil { + if err := os.Chtimes(nodeModules, mtime, mtime); err != nil { t.Fatal(err) } return dir @@ -210,6 +212,30 @@ func TestPruneGoldens(t *testing.T) { } }) + t.Run("a restore touching only node_modules' mtime still protects the golden", func(t *testing.T) { + // Mirrors TryRestoreGolden: the lockDir is created (and its own mtime set) + // long ago at publish time, then only node_modules is bumped by a later + // restore — exactly what os.Chtimes(paths.golden, ...) does in production. + root := t.TempDir() + lockDir := filepath.Join(root, "golden", "repoD", "bun-1") + nodeModules := filepath.Join(lockDir, "node_modules") + old := now.Add(-999 * day) + if err := os.MkdirAll(nodeModules, 0o755); err != nil { + t.Fatal(err) + } + if err := os.Chtimes(lockDir, old, old); err != nil { + t.Fatal(err) + } + recent := now.Add(-time.Second) + if err := os.Chtimes(nodeModules, recent, recent); err != nil { + t.Fatal(err) + } + pruneGoldens(root, 7*day, 99, now) + if !exists(lockDir) { + t.Error("reaped a golden a restore just marked as in-use") + } + }) + t.Run("prunes each repo independently", func(t *testing.T) { root := t.TempDir() a := mkGolden(t, root, "repoA", "bun-1", now)