Releases: cocoar-dev/Cocoar.FileSystem
Release list
v2.3.0
Cocoar.FileSystem v2.3.0
🎯 What's New
Symlink Target Tracking (opt-in)
ResilientFileSystemMonitor can now detect when a watched symlink's resolved target changes, even
though the symlink itself is never modified. This unblocks Kubernetes ConfigMap/Secret hot-reload:
a mounted ConfigMap key is a symlink, and Kubernetes updates it by an atomic swap of the ..data
symlink — not by rewriting the file — so the change was previously invisible to file watchers.
// A pod mounts a ConfigMap at /etc/config. "config.json" is a symlink that Kubernetes
// updates via an atomic "..data" swap — the file itself is never rewritten.
var monitor = ResilientFileSystemMonitor
.Watch("/etc/config", "config.json")
.WithSymlinkTargetTracking() // opt-in: detect the atomic symlink-target swap
.OnChanged((sender, e) =>
{
// Fires on a ConfigMap update — keyed on the user-visible path (config.json).
Console.WriteLine($"Config changed: {e.FullPath}");
ReloadConfig();
})
.Build();Off by default. When enabled, the monitor follows the watched symlink to its resolved final target
and folds that target into its change fingerprint. The update is surfaced as a Changed event on the
user-visible path (e.g. config.json) — so your handler/filter stays simple and unchanged.
How it works
- Detected on both the periodic audit (watching state) and the polling-fallback path, so the
reactive guarantee holds even if the underlyingFileSystemWatcherdrops out. - Only the final target is resolved (no recursion into it) — loop-safety is preserved.
- Resolution runs only for symlink/reparse-point entries, so ordinary files incur no extra cost.
- Works consistently on Linux/containers (the production target for ConfigMaps) as well as Windows:
on LinuxFile.ResolveLinkTargetdoes not canonicalize intermediate directory symlinks, so the
monitor also resolves the target's parent directory — the same idea as Goviper'sEvalSymlinks
compare.
🔄 Backward Compatibility
✅ Fully backward compatible. The feature is opt-in via .WithSymlinkTargetTracking(); without it,
behavior is unchanged (symlinks/reparse points remain skipped, as before).
📦 Upgrade
<PackageReference Include="Cocoar.FileSystem" Version="2.3.0" />📊 Testing
143 tests passing across Windows, Linux, and macOS — including end-to-end ConfigMap-style swap tests
verified on Windows and in a real Linux container.
v2.2.0
🎯 What's New
Folder Rename Detection
ResilientFileSystemMonitor now automatically detects folder renames containing matching files. No configuration needed—works out of the box.
var monitor = ResilientFileSystemMonitor
.Watch(@"C:\certs")
.WithFilter("*.pfx")
.IncludeSubdirectories()
.OnRenamed((sender, e) =>
{
// Fires for BOTH file AND folder renames!
Console.WriteLine($"Renamed: {e.OldFullPath} -> {e.FullPath}");
})
.Build();Perfect for certificate rotation: Atomic folder swaps (kid2-staging → kid2) are now properly detected.
FileSearcher API Harmonization
Search with multiple patterns and depth control, matching the ResilientFileSystemMonitor API.
var files = FileSearcher
.InDirectory(@"C:\repos\myapp")
.WithFilter("*.cs", "*.csproj", "*.json")
.IncludeSubdirectories(2) // Search 2 levels deep
.ToList();🔄 Backward Compatibility
✅ Fully backward compatible—all existing code continues to work without changes.
📦 Upgrade
<PackageReference Include="Cocoar.FileSystem" Version="2.2.0" />📊 Testing
139 tests passing across Windows, Linux, and macOS.
v2.1.0
What's New
Multiple File Pattern Support for ResilientFileSystemMonitor — Monitor multiple file extensions and patterns simultaneously with a clean, additive API.
✨ Features
Multiple Pattern Monitoring
Monitor multiple file patterns at once — perfect for scenarios like certificate management where you need to watch multiple formats:
var monitor = ResilientFileSystemMonitor
.Watch(@"C:\certs")
.WithFilter("*.pfx", "*.p12", "*.cer") // Watch all certificate formats
.OnChanged((sender, e) => ReloadCertificate(e.FullPath))
.Build();Additive Pattern Building
Build up patterns incrementally across multiple calls:
var monitor = ResilientFileSystemMonitor
.Watch(@"C:\logs")
.WithFilter("*.log") // Add log files
.WithFilter("*.txt") // Add text files
.WithFilter("error-*.json") // Add error JSON files
.Build();Pattern Management
Clear and reset patterns when needed:
builder.WithFilter("*.tmp");
builder.ClearFilters(); // Remove all patterns
builder.WithFilter("*.dat", "*.bin"); // Start fresh🔧 Technical Details
- Pattern Matching: Uses
FileSystemName.MatchesSimpleExpression(same asFileSearcher) for consistency - Wildcards: Supports DOS-style wildcards (
*for any characters,?for single character) - Performance: Filename-only matching (not full path) for optimal performance
- Behavior: Multiple
WithFilter()calls are additive — patterns accumulate - API:
params string[]signature supports flexible syntax:.WithFilter("*.pfx", "*.p12")or.WithFilter(["*.pfx", "*.p12"])
📚 Documentation
- Updated README with multiple pattern examples
- Comprehensive guide in resilient-file-system-monitor.md
- Real-world certificate monitoring example in examples.md
🧪 Testing
- 9 new comprehensive tests for multiple pattern functionality
- All 117 tests passing across Windows, Linux, and macOS
- Validation for edge cases (empty arrays, null patterns, whitespace)
🔄 Backward Compatibility
✅ Fully backward compatible — existing code continues to work without changes. This is a non-breaking addition (MINOR version bump per SemVer).
🚀 Future Plans
Multiple patterns lay the groundwork for advanced glob pattern support (e.g., src/**/*.cs) in future releases via an opt-in FilterMode API.
Full Changelog: https://github.com/cocoar-dev/Cocoar.FileSystem/blob/develop/CHANGELOG.md
v2.0.0
⚠️ Breaking Changes
Default monitoring behavior has changed from recursive to non-recursive.
In v1.0.0, ResilientFileSystemMonitor monitored subdirectories by default. Starting with v2.0.0, it only monitors the root directory unless explicitly configured otherwise.
Migration Guide
Before (v1.0.0):
var monitor = ResilientFileSystemMonitor
.Watch(@"C:\config")
.Build(); // Automatically monitored subdirectoriesAfter (v2.0.0):
// Option 1: Explicitly enable subdirectories (unlimited depth)
var monitor = ResilientFileSystemMonitor
.Watch(@"C:\config")
.IncludeSubdirectories() // Required for recursive monitoring
.Build();
// Option 2: Control the depth precisely
var monitor = ResilientFileSystemMonitor
.Watch(@"C:\config")
.IncludeSubdirectories(2) // Only monitor 2 levels deep
.Build();Why this change?
- Security: Prevents unintended monitoring of sensitive subdirectories
- Performance: Reduces overhead by default, especially in large directory trees
- Explicitness: Makes recursive behavior opt-in and more predictable
✨ New Features
Subdirectory Depth Control
Fine-grained control over how deeply the monitor recurses into subdirectories:
// Root directory only (default)
.Build(); // No .IncludeSubdirectories() call
// Direct children only (depth 1)
.IncludeSubdirectories(1)
// Specific depth (2 levels)
.IncludeSubdirectories(2)
// Unlimited depth (recursive)
.IncludeSubdirectories() // or .IncludeSubdirectories(true)
.IncludeSubdirectories(-1) // or explicit -1Use Cases:
- Monitor only
/config/*.jsonwithout descending into subdirectories - Watch
/certs/and/certs/prod/but not/certs/prod/archived/ - Avoid expensive monitoring of
node_modules,.git,bin,objfolders
📚 Documentation
- Subdirectory Depth Control Guide - Comprehensive examples and best practices
- API Reference - Updated with depth control configuration
- README - Quick start examples with depth control
🧹 Code Quality Improvements
- Cleaner XML Documentation: Removed redundant docs, kept only behavioral explanations
- AGENTS.md: Added guidance for AI assistants working with this codebase
- Simplified Architecture: Single source of truth for depth configuration
🧪 Testing
- 108 tests passing (10 new depth control tests)
- Full coverage across Windows, Linux, and macOS
- Validated edge cases: depth 0, 1, 2, unlimited, validation
🔗 Links
Questions or issues? Please open an issue
v1.0.0 - Initial Stable Release
🎉 First Stable Release
Production-ready file system utilities for .NET with automatic error recovery and multi-platform support.
✨ Features
ResilientFileSystemMonitor
- Auto-Recovery: Handles directory disappearing/reappearing, permission errors, watcher crashes
- Smart Fallback: Automatic switching between FileSystemWatcher and polling
- Debouncing: Built-in support to reduce noise from rapid file changes
- Health Checks: Periodic monitoring to detect silent watcher failures (especially on macOS)
- Reactive Streams: Unified event stream via
ChannelReader<FileSystemEvent> - Fluent API: Clean, discoverable configuration
FileSearcher
- Fast, lazy-evaluated directory traversal
- Depth limits and exclusion patterns
- LINQ support for flexible querying
- Efficient handling of large directory trees
FileReader
- Shared read/write access (works when files are open by other processes)
- Optional UTF-8 BOM stripping
- Try-pattern for graceful handling of missing files
- Byte array zeroing for sensitive content
🌍 Platform Support
- ✅ Windows
- ✅ Linux
- ✅ macOS
📦 Installation
dotnet add package Cocoar.FileSystem📚 Documentation
🧪 Testing
98 tests across all major platforms with comprehensive coverage.
Full Changelog: https://github.com/cocoar-dev/Cocoar.FileSystem/blob/main/CHANGELOG.md