handleEvent decides whether the event concerns one of the watched files (or could promote an ancestor watch) and, if so, schedules a debounced fire.
(ctx context.Context, evt fsnotify.Event)
| 239 | // watched files (or could promote an ancestor watch) and, if so, |
| 240 | // schedules a debounced fire. |
| 241 | func (cw *configWatcher) handleEvent(ctx context.Context, evt fsnotify.Event) { |
| 242 | cw.mu.Lock() |
| 243 | if cw.closed { |
| 244 | cw.mu.Unlock() |
| 245 | return |
| 246 | } |
| 247 | |
| 248 | // Match against any watched file. fsnotify event names are |
| 249 | // already absolute when the watched directory is absolute, |
| 250 | // which it is because armAncestorLocked called filepath.Dir |
| 251 | // on a path resolved to absolute. The filepath.Abs call below |
| 252 | // is a defensive normalization. |
| 253 | evtAbs, err := filepath.Abs(evt.Name) |
| 254 | if err != nil { |
| 255 | cw.mu.Unlock() |
| 256 | return |
| 257 | } |
| 258 | |
| 259 | matchedFile := "" |
| 260 | for rp := range cw.files { |
| 261 | if rp == evtAbs { |
| 262 | matchedFile = rp |
| 263 | break |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | // If a directory we are watching for an ancestor of an |
| 268 | // unrealized path just gained a new child, try to re-arm |
| 269 | // deeper. This handles `mkdir ~/.config; touch |
| 270 | // ~/.config/.mcp.json` cases. |
| 271 | if matchedFile == "" && evt.Has(fsnotify.Create) { |
| 272 | for rp, dir := range cw.files { |
| 273 | // Only re-arm files whose final parent is not yet |
| 274 | // being watched directly. |
| 275 | expected := filepath.Dir(rp) |
| 276 | if dir == expected { |
| 277 | continue |
| 278 | } |
| 279 | // If this event is a directory inside our currently |
| 280 | // watched ancestor that lies on the way to rp, |
| 281 | // re-arm. |
| 282 | if isAncestorPathSegment(evtAbs, rp) { |
| 283 | cw.releaseDirLocked(dir) |
| 284 | newDir, armErr := cw.armAncestorLocked(rp) |
| 285 | if armErr != nil { |
| 286 | cw.logger.Debug(ctx, |
| 287 | "failed to re-arm config file watch on ancestor create", |
| 288 | slog.F("path", rp), slog.Error(armErr)) |
| 289 | // Leave the file unarmed for now; |
| 290 | // next Sync will retry. |
| 291 | delete(cw.files, rp) |
| 292 | continue |
| 293 | } |
| 294 | cw.files[rp] = newDir |
| 295 | // The new dir may already contain the |
| 296 | // target file. Treat that as a match. |
| 297 | matchedFile = rp |
| 298 | } |
no test coverage detected