Skip to content

Commit

Permalink
which handles broken symlink & unit test added (actions#2150) (action…
Browse files Browse the repository at this point in the history
…s#2196)

* which handles broken symlink & unit test added (actions#2150)

* Update src/Runner.Sdk/Util/WhichUtil.cs

Co-authored-by: Ferenc Hammerl <[email protected]>

* fix pr comments

* trace log added,  in case we find a symlink that is broken

* add check in case the target of the symlink is a relative path; added test that check symlink that targets a full path; added test that check symlink that targets a relative path;

* fix tests

* fix tests for linux

---------

Co-authored-by: Eli Entelis <[email protected]>
Co-authored-by: Eli Entelis <[email protected]>
Co-authored-by: Ferenc Hammerl <[email protected]>
  • Loading branch information
4 people authored Apr 7, 2023
1 parent 0484afe commit 2ecd7d2
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 4 deletions.
16 changes: 13 additions & 3 deletions src/Runner.Sdk/Util/WhichUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static string Which(string command, bool require = false, ITraceWriter tr
trace?.Verbose(ex.ToString());
}

if (matches != null && matches.Length > 0)
if (matches != null && matches.Length > 0 && IsPathValid(matches.First(), trace))
{
trace?.Info($"Location: '{matches.First()}'");
return matches.First();
Expand All @@ -86,7 +86,7 @@ public static string Which(string command, bool require = false, ITraceWriter tr
for (int i = 0; i < pathExtSegments.Length; i++)
{
string fullPath = Path.Combine(pathSegment, $"{command}{pathExtSegments[i]}");
if (matches.Any(p => p.Equals(fullPath, StringComparison.OrdinalIgnoreCase)))
if (matches.Any(p => p.Equals(fullPath, StringComparison.OrdinalIgnoreCase)) && IsPathValid(fullPath, trace))
{
trace?.Info($"Location: '{fullPath}'");
return fullPath;
Expand All @@ -105,7 +105,7 @@ public static string Which(string command, bool require = false, ITraceWriter tr
trace?.Verbose(ex.ToString());
}

if (matches != null && matches.Length > 0)
if (matches != null && matches.Length > 0 && IsPathValid(matches.First(), trace))
{
trace?.Info($"Location: '{matches.First()}'");
return matches.First();
Expand All @@ -128,5 +128,15 @@ public static string Which(string command, bool require = false, ITraceWriter tr

return null;
}

// checks if the file is a symlink and if the symlink`s target exists.
private static bool IsPathValid(string path, ITraceWriter trace = null)
{
var fileInfo = new FileInfo(path);
var linkTargetFullPath = fileInfo.Directory?.FullName + Path.DirectorySeparatorChar + fileInfo.LinkTarget;
if(fileInfo.LinkTarget == null || File.Exists(linkTargetFullPath) || File.Exists(fileInfo.LinkTarget)) return true;
trace?.Info($"the target '{fileInfo.LinkTarget}' of the symbolic link '{path}', does not exist");
return false;
}
}
}
125 changes: 124 additions & 1 deletion src/Test/L0/Util/WhichUtilL0.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using GitHub.Runner.Common.Util;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using System;
using System.IO;
Expand Down Expand Up @@ -89,5 +89,128 @@ public void WhichHandleFullyQualifiedPath()
Assert.Equal(gitPath, gitPath2);
}
}

[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void WhichHandlesSymlinkToTargetFullPath()
{
// Arrange
using TestHostContext hc = new TestHostContext(this);
Tracing trace = hc.GetTrace();
string oldValue = Environment.GetEnvironmentVariable(PathUtil.PathVariable);
#if OS_WINDOWS
string newValue = oldValue + @$";{Path.GetTempPath()}";
string symlinkName = $"symlink-{Guid.NewGuid()}";
string symlink = Path.GetTempPath() + $"{symlinkName}.exe";
string target = Path.GetTempPath() + $"target-{Guid.NewGuid()}.exe";
#else
string newValue = oldValue + @$":{Path.GetTempPath()}";
string symlinkName = $"symlink-{Guid.NewGuid()}";
string symlink = Path.GetTempPath() + $"{symlinkName}";
string target = Path.GetTempPath() + $"target-{Guid.NewGuid()}";
#endif

Environment.SetEnvironmentVariable(PathUtil.PathVariable, newValue);


using (File.Create(target))
{
File.CreateSymbolicLink(symlink, target);

// Act.
var result = WhichUtil.Which(symlinkName, require: true, trace: trace);

// Assert
Assert.True(!string.IsNullOrEmpty(result) && File.Exists(result), $"Unable to find symlink through: {nameof(WhichUtil.Which)}");

}


// Cleanup
File.Delete(symlink);
File.Delete(target);
Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue);

}

[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void WhichHandlesSymlinkToTargetRelativePath()
{
// Arrange
using TestHostContext hc = new TestHostContext(this);
Tracing trace = hc.GetTrace();
string oldValue = Environment.GetEnvironmentVariable(PathUtil.PathVariable);
#if OS_WINDOWS
string newValue = oldValue + @$";{Path.GetTempPath()}";
string symlinkName = $"symlink-{Guid.NewGuid()}";
string symlink = Path.GetTempPath() + $"{symlinkName}.exe";
string targetName = $"target-{Guid.NewGuid()}.exe";
string target = Path.GetTempPath() + targetName;
#else
string newValue = oldValue + @$":{Path.GetTempPath()}";
string symlinkName = $"symlink-{Guid.NewGuid()}";
string symlink = Path.GetTempPath() + $"{symlinkName}";
string targetName = $"target-{Guid.NewGuid()}";
string target = Path.GetTempPath() + targetName;
#endif
Environment.SetEnvironmentVariable(PathUtil.PathVariable, newValue);


using (File.Create(target))
{
File.CreateSymbolicLink(symlink, targetName);

// Act.
var result = WhichUtil.Which(symlinkName, require: true, trace: trace);

// Assert
Assert.True(!string.IsNullOrEmpty(result) && File.Exists(result), $"Unable to find {symlinkName} through: {nameof(WhichUtil.Which)}");
}

// Cleanup
File.Delete(symlink);
File.Delete(target);
Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue);

}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void WhichThrowsWhenSymlinkBroken()
{
// Arrange
using TestHostContext hc = new TestHostContext(this);
Tracing trace = hc.GetTrace();
string oldValue = Environment.GetEnvironmentVariable(PathUtil.PathVariable);

#if OS_WINDOWS
string newValue = oldValue + @$";{Path.GetTempPath()}";
string brokenSymlinkName = $"broken-symlink-{Guid.NewGuid()}";
string brokenSymlink = Path.GetTempPath() + $"{brokenSymlinkName}.exe";
#else
string newValue = oldValue + @$":{Path.GetTempPath()}";
string brokenSymlinkName = $"broken-symlink-{Guid.NewGuid()}";
string brokenSymlink = Path.GetTempPath() + $"{brokenSymlinkName}";
#endif


string target = "no-such-file-cf7e351f";
Environment.SetEnvironmentVariable(PathUtil.PathVariable, newValue);

File.CreateSymbolicLink(brokenSymlink, target);

// Act.
var exception = Assert.Throws<FileNotFoundException>(()=>WhichUtil.Which(brokenSymlinkName, require: true, trace: trace));

// Assert
Assert.Equal(brokenSymlinkName, exception.FileName);

// Cleanup
File.Delete(brokenSymlink);
Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue);
}
}
}

0 comments on commit 2ecd7d2

Please sign in to comment.