-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Release 1.0.0 * Add linting * Refactor tests and improve logging messages
- Loading branch information
Showing
29 changed files
with
387 additions
and
276 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<Project> | ||
<PropertyGroup> | ||
<Description>By hook or by crook, perform operations on files and directories. If they are | ||
in use by a process, kill the process.</Description> | ||
<Version>1.0.0</Version> | ||
<PackageProjectUrl>https://github.com/domsleee/forceops</PackageProjectUrl> | ||
<RepositoryUrl>https://github.com/domsleee/forceops</RepositoryUrl> | ||
<RepositoryType>git</RepositoryType> | ||
<Authors>Dom Slee</Authors> | ||
<PackageTags>lock file directory force delete</PackageTags> | ||
<PackageReleaseNotes>https://github.com/domsleee/forceops/blob/main/CHANGELOG.md</PackageReleaseNotes> | ||
|
||
<PackageReadmeFile>README.md</PackageReadmeFile> | ||
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile> | ||
<PackageOutputPath>./nupkg</PackageOutputPath> | ||
</PropertyGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
namespace ForceOps; | ||
namespace ForceOps.Lib; | ||
|
||
public static class DirectoryUtils | ||
{ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
using System.Runtime.Versioning; | ||
using Serilog; | ||
using static ForceOps.Lib.DirectoryUtils; | ||
using static LockCheck.LockManager; | ||
|
||
namespace ForceOps.Lib; | ||
|
||
[SupportedOSPlatform("windows")] | ||
public class FileAndDirectoryDeleter | ||
{ | ||
readonly ILogger logger; | ||
readonly ForceOpsContext forceOpsContext; | ||
|
||
public FileAndDirectoryDeleter(ForceOpsContext forceOpsContext, ILogger? logger = null) | ||
{ | ||
this.forceOpsContext = forceOpsContext; | ||
this.logger = logger ?? forceOpsContext.loggerFactory.CreateLogger<FileAndDirectoryDeleter>(); | ||
} | ||
|
||
/// <summary> | ||
/// Delete a file or a folder, not following symlinks. | ||
/// If the delete fails, it will attempt to find processes using the file or directory | ||
/// </summary> | ||
/// <param name="fileOrDirectory">File or directory to delete.</param> | ||
public void DeleteFileOrDirectory(string fileOrDirectory) | ||
{ | ||
fileOrDirectory = CombineWithCWDAndGetAbsolutePath(fileOrDirectory); | ||
if (File.Exists(fileOrDirectory)) | ||
{ | ||
DeleteFile(new FileInfo(fileOrDirectory)); | ||
return; | ||
} | ||
if (Directory.Exists(fileOrDirectory)) | ||
{ | ||
DeleteDirectory(new DirectoryInfo(fileOrDirectory)); | ||
return; | ||
} | ||
// if the file/folder doesn't exist, it has already been deleted | ||
logger.Debug($"{fileOrDirectory} already deleted."); | ||
return; | ||
} | ||
|
||
internal void DeleteFile(FileInfo file) | ||
{ | ||
for (var attempt = 1; attempt <= forceOpsContext.maxAttempts; attempt++) | ||
{ | ||
try | ||
{ | ||
file.IsReadOnly = false; | ||
file.Delete(); | ||
break; | ||
} | ||
catch when (!file.Exists) { } | ||
catch (Exception ex) when (ex is IOException || ex is System.UnauthorizedAccessException) | ||
{ | ||
var getProcessesLockingFileFunc = () => GetLockingProcessInfos(new[] { file.FullName }); | ||
var shouldThrow = KillProcessesAndLogInfo("DeleteFile", attempt, file.FullName, getProcessesLockingFileFunc); | ||
if (shouldThrow) throw; | ||
} | ||
} | ||
} | ||
|
||
bool KillProcessesAndLogInfo(string actionName, int attemptNumber, string fileOrDirectoryPath, Func<IEnumerable<LockCheck.ProcessInfo>> getProcessesLockingFileFunc) | ||
{ | ||
var isProcessElevated = forceOpsContext.elevateUtils.IsProcessElevated(); | ||
var processElevatedMessage = isProcessElevated | ||
? "ForceOps process is elevated" | ||
: "ForceOps process is not elevated"; | ||
var messagePrefix = $"{actionName} failed attempt {attemptNumber}/{forceOpsContext.maxAttempts} for [{fileOrDirectoryPath}]. {processElevatedMessage}."; | ||
|
||
if (attemptNumber == forceOpsContext.maxAttempts) | ||
{ | ||
logger.Information($"{messagePrefix} No attempts remain, so the exception will be thrown."); | ||
return true; | ||
} | ||
|
||
var processes = getProcessesLockingFileFunc().ToList(); | ||
var processPlural = processes.Count == 1 ? "process" : "processes"; | ||
var processLogString = string.Join(", ", processes.Select(process => ProcessInfoToString(process))); | ||
logger.Information($"{messagePrefix} Found {processes.Count} {processPlural} to try to kill: [{processLogString}]"); | ||
forceOpsContext.processKiller.KillProcesses(processes); | ||
return false; | ||
} | ||
|
||
static string ProcessInfoToString(LockCheck.ProcessInfo? process) | ||
{ | ||
if (process == null) | ||
{ | ||
return "<null>"; | ||
} | ||
return $"{process?.ProcessId} - {process?.ExecutableName}"; | ||
} | ||
|
||
internal void DeleteDirectory(DirectoryInfo directory) | ||
{ | ||
if (!IsSymLink(directory)) | ||
{ | ||
DeleteFilesInFolder(directory); | ||
} | ||
|
||
for (var attempt = 1; attempt <= forceOpsContext.maxAttempts; attempt++) | ||
{ | ||
try | ||
{ | ||
directory.Delete(); | ||
break; | ||
} | ||
catch when (!directory.Exists) { } | ||
catch (Exception ex) when (ex is IOException) | ||
{ | ||
var getProcessesLockingFileFunc = () => GetLockingProcessInfos(new[] { directory.FullName }, LockCheck.LockManagerFeatures.UseLowLevelApi); | ||
var shouldThrow = KillProcessesAndLogInfo("DeleteDirectory", attempt, directory.FullName, getProcessesLockingFileFunc); | ||
if (shouldThrow) throw; | ||
} | ||
} | ||
} | ||
|
||
void DeleteFilesInFolder(DirectoryInfo directory) | ||
{ | ||
foreach (var file in directory.GetFiles()) | ||
{ | ||
DeleteFile(file); | ||
} | ||
foreach (var subDirectory in directory.GetDirectories()) | ||
{ | ||
DeleteDirectory(subDirectory); | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
namespace ForceOps.Lib; | ||
|
||
public class ForceOpsContext | ||
{ | ||
public int maxAttempts = 5; | ||
public IProcessKiller processKiller; | ||
public IElevateUtils elevateUtils; | ||
public IRelaunchAsElevated relaunchAsElevated; | ||
public ILoggerFactory loggerFactory; | ||
|
||
public ForceOpsContext( | ||
IProcessKiller? processKiller = null, | ||
IElevateUtils? elevateUtils = null, | ||
IRelaunchAsElevated? relaunchAsElevated = null, | ||
ILoggerFactory? loggerFactory = null | ||
) | ||
{ | ||
this.processKiller = processKiller ?? new ProcessKiller(); | ||
this.elevateUtils = elevateUtils ?? new ElevateUtils(); | ||
this.relaunchAsElevated = relaunchAsElevated ?? new RelaunchAsElevated(); | ||
this.loggerFactory = loggerFactory ?? new LoggerFactory(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace ForceOps.Lib; | ||
|
||
|
||
public interface IElevateUtils | ||
{ | ||
public bool IsProcessElevated(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using Serilog; | ||
|
||
namespace ForceOps.Lib; | ||
|
||
public interface ILoggerFactory | ||
{ | ||
public ILogger CreateLogger<T>(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace ForceOps; | ||
|
||
public interface IProcessKiller | ||
{ | ||
void KillProcesses(IEnumerable<LockCheck.ProcessInfo?> processes); | ||
} |
Oops, something went wrong.