feat: Enhance Folder.Clean() and CopyTo() methods with additional options#1693
Conversation
SummaryEnhances Folder.Clean() with read-only attribute handling and error resilience, and Folder.CopyTo() with attribute/timestamp preservation. Critical Issues1. Inefficient Read-Only Attribute Removal (Performance Bug)Location: The foreach (var file in directory.EnumerateFiles("*", SearchOption.AllDirectories))
{
// Removes read-only from all files in entire subtree
}
foreach (var subDirectory in directory.EnumerateDirectories("*", SearchOption.AllDirectories))
{
// Removes read-only from all subdirectories in entire subtree
}For a directory structure like: This will enumerate directories as: [a, a/b, a/b/c] and process each one, even though we're already at the root. The complexity is unnecessarily high. Fix: Use private static void RemoveReadOnlyAttributeRecursively(DirectoryInfo directory)
{
if ((directory.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
directory.Attributes &= ~FileAttributes.ReadOnly;
}
foreach (var file in directory.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
{
if ((file.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
file.Attributes &= ~FileAttributes.ReadOnly;
}
}
foreach (var subDirectory in directory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly))
{
RemoveReadOnlyAttributeRecursively(subDirectory);
}
}2. Logging Position MismatchLocation: The log statement appears at line 198 (after the copy completes) but uses present continuous tense "Copying Folder". This should either:
3. Behavioral Breaking Change in Clean()Location: The original Recommendation: Consider adding a third overload to preserve backward compatibility: public void Clean() => Clean(removeReadOnlyAttribute: true, throwOnError: true);
public void Clean(bool removeReadOnlyAttribute, bool throwOnError = false)
{
// ... existing implementation with conditional throw
}Or document this as a breaking change in release notes if the swallowing behavior is intentional. SuggestionsError Handling ConsistencyThe Attribute Preservation Edge CaseIn Verdict |
SummaryAdds overloads to Folder.Clean() and CopyTo() for handling read-only files and preserving timestamps. Critical Issues1. Contradictory CopyTo() default behaviorLocation: src/ModularPipelines/FileSystem/Folder.cs:99 The PR description states: "preserves file/folder timestamps by default" But the code shows: public Folder CopyTo(string targetPath)
{
return CopyTo(targetPath, preserveTimestamps: false); // <- defaults to FALSE
}Impact: Documentation doesn't match implementation. Based on the extensive timestamp preservation logic added, it appears the intent was Fix: Change line 99 to 2. Missing error handling in Clean()Location: src/ModularPipelines/FileSystem/Folder.cs:108-134 The PR description states: "Uses try-catch blocks with warning logging for individual file/folder failures during clean" But there are no try-catch blocks in the Clean() method. If a file/directory deletion fails after removing the read-only attribute (e.g., file in use, permission denied), the entire operation will throw an exception. Impact: The method is not resilient to individual file failures as claimed. Fix: Add try-catch blocks around the deletion operations with warning logging as described, or remove the claim from the PR description. 3. Breaking change: Clean() now removes read-only by defaultLocation: src/ModularPipelines/FileSystem/Folder.cs:93-95 The original Impact: Existing code that expected exceptions on read-only files will now silently remove those attributes. Suggestion: Consider:
Suggestions4. CopyTo() log message placement inconsistencyThe log now appears at the end after all work completes ("Copied"), but other methods like Consider moving the log to the start for consistency, or changing all methods to log at the end with past tense. Verdict |
- Change Clean() default to removeReadOnlyAttribute: false for backward compat - Move CopyTo log to start of method for consistency with other operations - Add XML remarks documenting the backward-compatible behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
bf06434 to
86e0440
Compare
SummaryThis PR enhances the Critical IssuesBLOCKING: Inconsistency between PR description and implementation The PR description states:
However, examining the current repository code at Clean(removeReadOnlyAttribute: true);But the PR diff shows this should be changed to: Clean(removeReadOnlyAttribute: false);The current code contradicts the stated backward compatibility goal. If the original
The XML documentation added in lines 98-101 also states this preserves backward compatibility by NOT removing read-only attributes, which supports option 1. Suggestions
Previous Review StatusNo previous review comments found. Verdict |
Response to ReviewThe PR diff correctly shows the change from - Clean(removeReadOnlyAttribute: true);
+ Clean(removeReadOnlyAttribute: false);Clarification:
The implementation now matches the PR description:
The diff in the Files Changed tab should show this change clearly. |
There was a problem hiding this comment.
Pull request overview
This PR aims to enhance the Folder.Clean() and Folder.CopyTo() methods by adding overloads with additional options for handling read-only files and preserving timestamps. However, there is a critical discrepancy between the PR description and the implementation regarding the default behavior of Clean().
Key Changes:
- Modified
Clean()default behavior by changing the internal call fromremoveReadOnlyAttribute: truetofalse - Added XML documentation explaining the new
Clean(bool)overload behavior - Repositioned logging in
CopyTo()from after completion to before operation start
| LogFolderOperationWithDestination("Copying Folder: {Source} > {Destination} [Module: {ModuleName}, Activity: {ActivityId}]", this, targetPath); | ||
|
|
There was a problem hiding this comment.
The log message was moved from after the copy operation completes (line 214 in old code) to before it starts (line 160 in new code). This changes the semantics of the message from "Copied Folder" (past tense, indicating completion) to "Copying Folder" (present tense, indicating start). However, the existing logging at line 160 says "Copying" which is appropriate for the new position. Consider whether the completion logging should be restored, or if starting-only logging is sufficient for this operation. Other operations like MoveTo log at the start, so this change may be intentional for consistency.
| public void Clean() | ||
| { | ||
| Clean(removeReadOnlyAttribute: true); | ||
| Clean(removeReadOnlyAttribute: false); |
There was a problem hiding this comment.
The change from Clean(removeReadOnlyAttribute: true) to Clean(removeReadOnlyAttribute: false) is a breaking change that contradicts both the PR description and the XML documentation. The PR states "removeReadOnlyAttribute=true" should be the default for "safe behavior," and the documentation claims to "preserve backward compatibility." However, the previous default was true, so changing it to false breaks existing behavior. This will cause Clean() to fail on read-only files where it previously succeeded. Either revert to true to maintain the stated goals, or acknowledge this is a breaking change and update the documentation accordingly.
| /// <remarks> | ||
| /// This method preserves backward compatibility by not removing read-only attributes. | ||
| /// Use <see cref="Clean(bool)"/> with <c>removeReadOnlyAttribute: true</c> to handle read-only files. | ||
| /// </remarks> | ||
| public void Clean() | ||
| { | ||
| Clean(removeReadOnlyAttribute: true); | ||
| Clean(removeReadOnlyAttribute: false); | ||
| } |
There was a problem hiding this comment.
The documentation claims "This method preserves backward compatibility by not removing read-only attributes," but the implementation was changed from Clean(removeReadOnlyAttribute: true) to Clean(removeReadOnlyAttribute: false). This is actually introducing a breaking change, not preserving backward compatibility. The previous behavior was to remove read-only attributes by default, and this change inverts that behavior. Either revert the default to true to maintain backward compatibility, or update the documentation to acknowledge this is a breaking change.
Summary
Clean(bool removeReadOnlyAttribute)overload to handle read-only filesCopyTo(string targetPath, bool preserveTimestamps)overload for timestamp controlClean()defaults toremoveReadOnlyAttribute: false(preserves original throwing behavior)CopyTo()defaults topreserveTimestamps: false(preserves original OS-default behavior)Changes
Folder.Clean()
removeReadOnlyAttributeparametertrue, removes read-only attributes before deletion to prevent access errorsClean()preserves backward compatibility by NOT removing read-only attributesFolder.CopyTo()
preserveTimestampsparametertrue, preserves CreationTimeUtc, LastWriteTimeUtc, and LastAccessTimeUtcCopyTo()preserves backward compatibility by NOT preserving timestampsRemoveReadOnlyAttributeRecursively
TopDirectoryOnlywith recursion for efficient traversalTest Plan
🤖 Generated with Claude Code