Skip to content

Releases: cocoar-dev/Cocoar.FileSystem

v2.3.0

Choose a tag to compare

@windischb windischb released this 02 Jun 19:06

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 underlying FileSystemWatcher drops 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 Linux File.ResolveLinkTarget does not canonicalize intermediate directory symlinks, so the
    monitor also resolves the target's parent directory — the same idea as Go viper's EvalSymlinks
    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

Choose a tag to compare

@windischb windischb released this 15 Nov 23:15
ff6a409

🎯 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-stagingkid2) 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

Choose a tag to compare

@windischb windischb released this 13 Nov 16:03
5c95536

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 as FileSearcher) 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

Choose a tag to compare

@windischb windischb released this 12 Nov 19:37
13ee188

⚠️ 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 subdirectories

After (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 -1

Use Cases:

  • Monitor only /config/*.json without descending into subdirectories
  • Watch /certs/ and /certs/prod/ but not /certs/prod/archived/
  • Avoid expensive monitoring of node_modules, .git, bin, obj folders

📚 Documentation


🧹 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

Choose a tag to compare

@windischb windischb released this 05 Nov 06:11

🎉 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