From 693616b2a97f946c3fd721cfe9858c4f4d83eeb7 Mon Sep 17 00:00:00 2001 From: Yuan Teoh Date: Fri, 13 Feb 2026 10:48:58 -0800 Subject: [PATCH] resolve gemini coments --- cmd/root.go | 77 +++++++++++++++++++++++++++++++--------- docs/en/reference/cli.md | 6 ++-- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index ccd9c68d74..0572bb4954 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -225,17 +225,6 @@ func watchChanges(ctx context.Context, watchDirs map[string]bool, watchedFiles m defer w.Close() - var pollTickerChan <-chan time.Time - if pollTickerSecond > 0 { - ticker := time.NewTicker(time.Duration(pollTickerSecond) * time.Second) - defer ticker.Stop() - pollTickerChan = ticker.C // Assign the channel - logger.DebugContext(ctx, fmt.Sprintf("NFS polling enabled every %v", pollTickerSecond)) - } else { - logger.DebugContext(ctx, "NFS polling disabled (interval is 0)") - } - - lastSeen := make(map[string]time.Time) watchingFolder := false var folderToWatch string @@ -264,6 +253,42 @@ func watchChanges(ctx context.Context, watchDirs map[string]bool, watchedFiles m logger.DebugContext(ctx, fmt.Sprintf("Added directory %s to watcher.", dir)) } + lastSeen := make(map[string]time.Time) + var pollTickerChan <-chan time.Time + if pollTickerSecond > 0 { + ticker := time.NewTicker(time.Duration(pollTickerSecond) * time.Second) + defer ticker.Stop() + pollTickerChan = ticker.C // Assign the channel + logger.DebugContext(ctx, fmt.Sprintf("NFS polling enabled every %v", pollTickerSecond)) + + // Pre-populate lastSeen to avoid an initial spurious reload + if watchingFolder { + files, err := os.ReadDir(folderToWatch) + if err != nil { + logger.WarnContext(ctx, "error reading tools folder on initial scan %s", err) + } else { + for _, f := range files { + if !f.IsDir() && (strings.HasSuffix(f.Name(), ".yaml") || strings.HasSuffix(f.Name(), ".yml")) { + fullPath := filepath.Join(folderToWatch, f.Name()) + info, err := os.Stat(fullPath) + if err == nil { + lastSeen[fullPath] = info.ModTime() + } + } + } + } + } else { + for f := range watchedFiles { + info, err := os.Stat(f) + if err == nil { + lastSeen[f] = info.ModTime() + } + } + } + } else { + logger.DebugContext(ctx, "NFS polling disabled (interval is 0)") + } + // debounce timer is used to prevent multiple writes triggering multiple reloads debounceDelay := 100 * time.Millisecond debounce := time.NewTimer(1 * time.Minute) @@ -276,8 +301,9 @@ func watchChanges(ctx context.Context, watchDirs map[string]bool, watchedFiles m return case <-pollTickerChan: changed := false + currentDiskFiles := make(map[string]bool) + // Get files that are currently on disk if watchingFolder { - // Scan directory for any .yaml/.yml file changes files, err := os.ReadDir(folderToWatch) if err != nil { logger.WarnContext(ctx, "error reading tools folder %s", err) @@ -286,20 +312,37 @@ func watchChanges(ctx context.Context, watchDirs map[string]bool, watchedFiles m for _, f := range files { if !f.IsDir() && (strings.HasSuffix(f.Name(), ".yaml") || strings.HasSuffix(f.Name(), ".yml")) { fullPath := filepath.Join(folderToWatch, f.Name()) + currentDiskFiles[fullPath] = true + if checkModTime(fullPath, lastSeen) { changed = true } } } } else { - files := slices.Collect(maps.Keys(watchedFiles)) - // Check specific files in watchedFiles map - for _, f := range files { - if checkModTime(f, lastSeen) { - changed = true + for f := range watchedFiles { + // We must explicitly check existence here because checkModTime + // swallows errors (returns false), making it impossible to + // distinguish between "no change" and "file deleted". + if _, err := os.Stat(f); err == nil { + currentDiskFiles[f] = true + if checkModTime(f, lastSeen) { + changed = true + } } } } + + // Check for Deletions + // If it was in lastSeen but is NOT in currentDiskFiles, it's + // deleted; we will need to reload the server. + for path := range lastSeen { + if !currentDiskFiles[path] { + logger.DebugContext(ctx, fmt.Sprintf("File deleted (detected via polling): %s", path)) + delete(lastSeen, path) + changed = true + } + } if changed { logger.DebugContext(ctx, "NFS remote change detected via polling") // once this timer runs out, it will trigger debounce.C diff --git a/docs/en/reference/cli.md b/docs/en/reference/cli.md index c84f5dd8d4..559a9f2e41 100644 --- a/docs/en/reference/cli.md +++ b/docs/en/reference/cli.md @@ -137,11 +137,11 @@ used at a time. Toolbox enables dynamic reloading by default. To disable, use the `--disable-reload` flag. -Use the `--polling-interval` flag to manually detect configuration file updates. -When the polling interval is `0`, the polling system is disabled. +Use the `--poll-interval` flag to manually detect configuration file updates. +When the poll interval is `0`, the polling system is disabled. {{< notice tip >}} -FOr polling to be effective when running Kubernetes, PersistentVolume or +For polling system to be effective when running Kubernetes, PersistentVolume or StorageClass must be set to refresh attributes rapidly by setting `actimeo=1`. Actimeo setting determines the duration for which a client trusts its local cache for file attributes.