Skip to content

Conversation

@Jack251970
Copy link
Member

@Jack251970 Jack251970 commented Feb 7, 2026

Summary by cubic

Make plugin directory deletion more reliable by adding robust retry logic and recreating a deletion marker when cleanup is incomplete. This reduces uninstall failures caused by locked or read-only files and ensures cleanup completes on the next startup.

  • Bug Fixes
    • Added FilesFolders.TryDeleteDirectoryRobust with retries, read-only attribute removal, and deep directory cleanup; used in PluginConfig during plugin cleanup.
    • When deletion is incomplete, recreate the marker file and log a clear warning so deletion retriggers on next launch.
    • Increased retry delay to 200ms and added unit tests for non-existent, empty, nested, and read-only file cases.

Written for commit 0acda2c. Summary will update on new commits.

Copilot AI and others added 6 commits February 4, 2026 04:19
Refactored the process for recreating the marker file when a plugin directory is not fully deleted. Removed unnecessary try-catch and nested checks, now directly checking for file existence and creating it if missing. This streamlines the code and removes redundant error handling.
Raised retry delay for directory deletion from 100ms to 200ms to improve reliability when files are locked or in use.
Updated the log message for incomplete directory deletion to be clearer and more concise. The warning is now logged before recreating the marker file, and no longer mentions retrying deletion or remaining files/folders.
Copilot AI review requested due to automatic review settings February 7, 2026 10:28
@github-actions github-actions bot added this to the 2.1.0 milestone Feb 7, 2026
@Jack251970 Jack251970 closed this Feb 7, 2026
@Jack251970 Jack251970 deleted the copilot/fix-plugin-directory-deletion branch February 7, 2026 10:28
@Jack251970 Jack251970 restored the copilot/fix-plugin-directory-deletion branch February 7, 2026 10:29
@prlabeler prlabeler bot added the bug Something isn't working label Feb 7, 2026
@gitstream-cm
Copy link

gitstream-cm bot commented Feb 7, 2026

🥷 Code experts: no user but you matched threshold 10

Jack251970 has most 👩‍💻 activity in the files.
Jack251970 has most 🧠 knowledge in the files.

See details

Flow.Launcher.Core/Plugin/PluginConfig.cs

Activity based on git-commit:

Jack251970
FEB
JAN
DEC
NOV
OCT
SEP 9 additions & 12 deletions

Knowledge based on git-blame:
Jack251970: 100%

Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs

Activity based on git-commit:

Jack251970
FEB
JAN
DEC
NOV
OCT
SEP 10 additions & 0 deletions

Knowledge based on git-blame:
Jack251970: 100%

Flow.Launcher.Test/FilesFoldersTest.cs

Activity based on git-commit:

Jack251970
FEB
JAN
DEC
NOV
OCT
SEP

Knowledge based on git-blame:
Jack251970: 100%

✨ Comment /gs review for LinearB AI review. Learn how to automate it here.

@gitstream-cm
Copy link

gitstream-cm bot commented Feb 7, 2026

Be a legend 🏆 by adding a before and after screenshot of the changes you made, especially if they are around UI/UX.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 7, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

The PR introduces a robust directory deletion utility method with retry logic and fallback mechanism, replacing a simple direct deletion call in plugin cleanup. On failure, pending deletions are marked for retry at next startup via a marker file.

Changes

Cohort / File(s) Summary
Robust Directory Deletion
Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
Added TryDeleteDirectoryRobust() method implementing retry logic with configurable delays, read-only attribute handling, exception recovery, and bottom-up directory traversal for nested structures.
Plugin Cleanup Integration
Flow.Launcher.Core/Plugin/PluginConfig.cs
Replaced direct Directory.Delete() with TryDeleteDirectoryRobust() and added failure handling that logs warnings and writes deletion marker file for deferred retry.
Test Coverage
Flow.Launcher.Test/FilesFoldersTest.cs
Added five unit tests covering non-existent directories, empty directories, populated directories, nested structures, and read-only file attribute handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • jjw24
  • taooceros
  • VictoriousRaptor
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

@Jack251970 Jack251970 deleted the copilot/fix-plugin-directory-deletion branch February 7, 2026 10:31
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a more resilient directory deletion helper and switches plugin cleanup to use it, adding unit tests to validate common deletion scenarios.

Changes:

  • Added FilesFolders.TryDeleteDirectoryRobust(...) with per-file retry behavior and read-only attribute handling.
  • Updated plugin deletion flow to use the robust delete method and re-create the “delete marker” when deletion is incomplete.
  • Added NUnit tests covering deletion of non-existent, empty, nested, and read-only-file directories.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
Flow.Launcher.Test/FilesFoldersTest.cs Adds new NUnit tests for the new robust directory deletion helper.
Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs Implements TryDeleteDirectoryRobust with retry logic and recursive cleanup.
Flow.Launcher.Core/Plugin/PluginConfig.cs Uses the new robust delete for plugin directory cleanup and attempts to re-create deletion marker on partial failure.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +155 to +157
bool fileDeleted = false;
for (int attempt = 0; attempt <= maxRetries; attempt++)
{
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The retry loop uses attempt <= maxRetries, which results in maxRetries + 1 total attempts. This conflicts with the XML doc wording (“maximum number of retry attempts”). Either change the loop to attempt < maxRetries (treating maxRetries as total attempts) or update the doc to clarify it means “retries after the first attempt.”

Copilot uses AI. Check for mistakes.
Comment on lines +151 to +153
// First, try to delete all files in the directory tree
var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
foreach (var file in files)
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directory.GetFiles(..., AllDirectories) eagerly materializes the entire file list and can be very memory-heavy for large directories. Consider switching to Directory.EnumerateFiles (and similarly for directories) to stream entries and reduce allocations during deletion.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +44
if (!File.Exists(markerFilePath))
{
File.WriteAllText(markerFilePath, string.Empty);
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When deletion is not fully successful, the code attempts to recreate the marker file under directory without first ensuring the directory still exists. If the root directory was actually deleted (or moved) despite fullyDeleted being false (e.g., due to an exception after deletion), File.WriteAllText will throw DirectoryNotFoundException and the marker won’t be recreated. Guard with Directory.Exists(directory) before writing the marker (and skip/re-log if it no longer exists).

Suggested change
if (!File.Exists(markerFilePath))
{
File.WriteAllText(markerFilePath, string.Empty);
}
if (Directory.Exists(directory) && !File.Exists(markerFilePath))
{
File.WriteAllText(markerFilePath, string.Empty);
}
else if (!Directory.Exists(directory))
{
PublicApi.Instance.LogWarn(ClassName,
$"Directory <{directory}> no longer exists when attempting to recreate delete marker file.");
}

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +81
public void TryDeleteDirectoryRobust_WhenDirectoryIsEmpty_DeletesSuccessfully()
{
// Arrange
string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(tempDir);

// Act
bool result = FilesFolders.TryDeleteDirectoryRobust(tempDir);

// Assert
ClassicAssert.IsTrue(result);
ClassicAssert.IsFalse(Directory.Exists(tempDir));
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests create temp directories/files but don’t have a finally cleanup path if an assertion fails or an unexpected exception is thrown. That can leave artifacts in the temp folder and make subsequent runs flaky. Consider wrapping each test body with try/finally (or using [TearDown]) to delete the temp directory if it still exists, and for the read-only case restore attributes before cleanup if needed.

Copilot uses AI. Check for mistakes.
@jjw24 jjw24 removed this from the 2.1.0 milestone Feb 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants