Merge pull request #2864 from dotnet/ajbaaska/fix-mappings
Maintain folder structure in mappings
This commit is contained in:
commit
2837db914c
5 changed files with 165 additions and 32 deletions
|
@ -7,6 +7,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.DotNet.ProjectModel.Utilities;
|
using Microsoft.DotNet.ProjectModel.Utilities;
|
||||||
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing;
|
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing;
|
||||||
|
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.ProjectModel.Files
|
namespace Microsoft.DotNet.ProjectModel.Files
|
||||||
{
|
{
|
||||||
|
@ -39,7 +40,7 @@ namespace Microsoft.DotNet.ProjectModel.Files
|
||||||
sourceBasePath,
|
sourceBasePath,
|
||||||
DiagnosticMessageSeverity.Error));
|
DiagnosticMessageSeverity.Error));
|
||||||
}
|
}
|
||||||
else if (targetBasePath.Split('/').Any(s => s.Equals(".") || s.Equals("..")))
|
else if (targetBasePath.Split(Path.DirectorySeparatorChar).Any(s => s.Equals(".") || s.Equals("..")))
|
||||||
{
|
{
|
||||||
diagnostics?.Add(new DiagnosticMessage(
|
diagnostics?.Add(new DiagnosticMessage(
|
||||||
ErrorCodes.NU1004,
|
ErrorCodes.NU1004,
|
||||||
|
@ -56,12 +57,11 @@ namespace Microsoft.DotNet.ProjectModel.Files
|
||||||
context.IncludePatterns,
|
context.IncludePatterns,
|
||||||
context.ExcludePatterns,
|
context.ExcludePatterns,
|
||||||
context.IncludeFiles,
|
context.IncludeFiles,
|
||||||
context.ExcludeFiles,
|
|
||||||
context.BuiltInsInclude,
|
context.BuiltInsInclude,
|
||||||
context.BuiltInsExclude);
|
context.BuiltInsExclude).ToList();
|
||||||
|
|
||||||
var isFile = targetBasePath[targetBasePath.Length - 1] != Path.DirectorySeparatorChar;
|
var isFile = targetBasePath[targetBasePath.Length - 1] != Path.DirectorySeparatorChar;
|
||||||
if (isFile && files.Count() > 1)
|
if (isFile && files.Count > 1)
|
||||||
{
|
{
|
||||||
// It's a file. But the glob matched multiple things
|
// It's a file. But the glob matched multiple things
|
||||||
diagnostics?.Add(new DiagnosticMessage(
|
diagnostics?.Add(new DiagnosticMessage(
|
||||||
|
@ -72,30 +72,70 @@ namespace Microsoft.DotNet.ProjectModel.Files
|
||||||
sourceBasePath,
|
sourceBasePath,
|
||||||
DiagnosticMessageSeverity.Error));
|
DiagnosticMessageSeverity.Error));
|
||||||
}
|
}
|
||||||
else if (isFile && files.Any())
|
else if (isFile && files.Count > 0)
|
||||||
{
|
{
|
||||||
includeEntries.Add(new IncludeEntry(targetBasePath, files.First()));
|
var filePath = Path.GetFullPath(
|
||||||
|
Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator(files[0].Path)));
|
||||||
|
|
||||||
|
includeEntries.Add(new IncludeEntry(targetBasePath, filePath));
|
||||||
}
|
}
|
||||||
else
|
else if (!isFile)
|
||||||
{
|
{
|
||||||
targetBasePath = targetBasePath.Substring(0, targetBasePath.Length - 1);
|
targetBasePath = targetBasePath.Substring(0, targetBasePath.Length - 1);
|
||||||
|
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
string targetPath = null;
|
var fullPath = Path.GetFullPath(
|
||||||
|
Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator(file.Path)));
|
||||||
|
string targetPath;
|
||||||
|
|
||||||
if (flatten)
|
if (flatten)
|
||||||
{
|
{
|
||||||
targetPath = Path.Combine(targetBasePath, Path.GetFileName(file));
|
targetPath = Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator(file.Stem));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
targetPath = Path.Combine(targetBasePath, PathUtility.GetRelativePath(sourceBasePath, file));
|
targetPath = Path.Combine(
|
||||||
|
targetBasePath,
|
||||||
|
PathUtility.GetRelativePathIgnoringDirectoryTraversals(sourceBasePath, fullPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
includeEntries.Add(new IncludeEntry(targetPath, file));
|
includeEntries.Add(new IncludeEntry(targetPath, fullPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.IncludeFiles != null)
|
||||||
|
{
|
||||||
|
foreach (var literalRelativePath in context.IncludeFiles)
|
||||||
|
{
|
||||||
|
var fullPath = Path.GetFullPath(Path.Combine(sourceBasePath, literalRelativePath));
|
||||||
|
string targetPath;
|
||||||
|
|
||||||
|
if (isFile)
|
||||||
|
{
|
||||||
|
targetPath = targetBasePath;
|
||||||
|
}
|
||||||
|
else if (flatten)
|
||||||
|
{
|
||||||
|
targetPath = Path.Combine(targetBasePath, Path.GetFileName(fullPath));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetPath = Path.Combine(targetBasePath, PathUtility.GetRelativePath(sourceBasePath, fullPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
includeEntries.Add(new IncludeEntry(targetPath, fullPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.ExcludeFiles != null)
|
||||||
|
{
|
||||||
|
var literalExcludedFiles = new HashSet<string>(
|
||||||
|
context.ExcludeFiles.Select(file => Path.GetFullPath(Path.Combine(sourceBasePath, file))),
|
||||||
|
StringComparer.Ordinal);
|
||||||
|
|
||||||
|
includeEntries.RemoveWhere(entry => literalExcludedFiles.Contains(entry.SourcePath));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Mappings != null)
|
if (context.Mappings != null)
|
||||||
|
@ -105,7 +145,7 @@ namespace Microsoft.DotNet.ProjectModel.Files
|
||||||
{
|
{
|
||||||
var targetPath = Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator(map.Key));
|
var targetPath = Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator(map.Key));
|
||||||
|
|
||||||
foreach (var file in GetIncludeFiles(map.Value, targetPath, diagnostics, flatten))
|
foreach (var file in GetIncludeFiles(map.Value, targetPath, diagnostics, flatten: true))
|
||||||
{
|
{
|
||||||
file.IsCustomTarget = true;
|
file.IsCustomTarget = true;
|
||||||
|
|
||||||
|
@ -119,12 +159,11 @@ namespace Microsoft.DotNet.ProjectModel.Files
|
||||||
return includeEntries;
|
return includeEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<string> GetIncludeFilesCore(
|
private static IEnumerable<FilePatternMatch> GetIncludeFilesCore(
|
||||||
string sourceBasePath,
|
string sourceBasePath,
|
||||||
List<string> includePatterns,
|
List<string> includePatterns,
|
||||||
List<string> excludePatterns,
|
List<string> excludePatterns,
|
||||||
List<string> includeFiles,
|
List<string> includeFiles,
|
||||||
List<string> excludeFiles,
|
|
||||||
List<string> builtInsInclude,
|
List<string> builtInsInclude,
|
||||||
List<string> builtInsExclude)
|
List<string> builtInsExclude)
|
||||||
{
|
{
|
||||||
|
@ -167,16 +206,7 @@ namespace Microsoft.DotNet.ProjectModel.Files
|
||||||
matcher.AddExcludePatterns(excludePatterns);
|
matcher.AddExcludePatterns(excludePatterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
var files = matcher.GetResultsInFullPath(sourceBasePath);
|
return matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(sourceBasePath))).Files;
|
||||||
files = files.Concat(literalIncludedFiles).Distinct();
|
|
||||||
|
|
||||||
if (files.Any() && excludeFiles != null)
|
|
||||||
{
|
|
||||||
var literalExcludedFiles = excludeFiles.Select(file => Path.GetFullPath(Path.Combine(sourceBasePath, file)));
|
|
||||||
files = files.Except(literalExcludedFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
return files;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,17 +61,26 @@ namespace Microsoft.DotNet.ProjectModel.Utilities
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns path2 relative to path1, with Path.DirectorySeparatorChar as separator
|
/// Returns <paramref name="path2"/> relative to <paramref name="path1"/>, with Path.DirectorySeparatorChar as separator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetRelativePath(string path1, string path2)
|
public static string GetRelativePath(string path1, string path2)
|
||||||
{
|
{
|
||||||
return GetRelativePath(path1, path2, Path.DirectorySeparatorChar);
|
return GetRelativePath(path1, path2, Path.DirectorySeparatorChar, includeDirectoryTraversals: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns path2 relative to path1, with given path separator
|
/// Returns <paramref name="path2"/> relative to <paramref name="path1"/>, with <c>Path.DirectorySeparatorChar</c>
|
||||||
|
/// as separator but ignoring directory traversals.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetRelativePath(string path1, string path2, char separator)
|
public static string GetRelativePathIgnoringDirectoryTraversals(string path1, string path2)
|
||||||
|
{
|
||||||
|
return GetRelativePath(path1, path2, Path.DirectorySeparatorChar, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <paramref name="path2"/> relative to <paramref name="path1"/>, with given path separator
|
||||||
|
/// </summary>
|
||||||
|
public static string GetRelativePath(string path1, string path2, char separator, bool includeDirectoryTraversals)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(path1))
|
if (string.IsNullOrEmpty(path1))
|
||||||
{
|
{
|
||||||
|
@ -133,10 +142,14 @@ namespace Microsoft.DotNet.ProjectModel.Utilities
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (includeDirectoryTraversals)
|
||||||
|
{
|
||||||
for (var i = index; len1 > i; ++i)
|
for (var i = index; len1 > i; ++i)
|
||||||
{
|
{
|
||||||
path += ".." + separator;
|
path += ".." + separator;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = index; len2 - 1 > i; ++i)
|
for (var i = index; len2 - 1 > i; ++i)
|
||||||
{
|
{
|
||||||
path += path2Segments[i] + separator;
|
path += path2Segments[i] + separator;
|
||||||
|
|
|
@ -123,7 +123,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
||||||
|
|
||||||
if (Project.PackOptions.PackInclude != null)
|
if (Project.PackOptions.PackInclude != null)
|
||||||
{
|
{
|
||||||
var files = IncludeFilesResolver.GetIncludeFiles(Project.PackOptions.PackInclude, "/", diagnostics: packDiagnostics, flatten: true);
|
var files = IncludeFilesResolver.GetIncludeFiles(Project.PackOptions.PackInclude, "/", diagnostics: packDiagnostics);
|
||||||
PackageBuilder.Files.AddRange(GetPackageFiles(files, packDiagnostics));
|
PackageBuilder.Files.AddRange(GetPackageFiles(files, packDiagnostics));
|
||||||
}
|
}
|
||||||
else if (Project.Files.PackInclude != null && Project.Files.PackInclude.Any())
|
else if (Project.Files.PackInclude != null && Project.Files.PackInclude.Any())
|
||||||
|
|
|
@ -173,6 +173,96 @@ namespace Microsoft.DotNet.ProjectModel.Tests
|
||||||
entry.SourcePath.Contains(PathUtility.GetPathWithDirectorySeparator("files/pfile2ex.txt")));
|
entry.SourcePath.Contains(PathUtility.GetPathWithDirectorySeparator("files/pfile2ex.txt")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void It_maintains_folder_structure()
|
||||||
|
{
|
||||||
|
var json = JObject.Parse(@"{
|
||||||
|
'packOptions': {
|
||||||
|
'files': {
|
||||||
|
'include': 'packfiles/**/*.txt',
|
||||||
|
'excludeFiles': 'packfiles/morefiles/file2.txt',
|
||||||
|
'mappings': {
|
||||||
|
'somepath/': 'mappackfiles/**/*.txt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}");
|
||||||
|
|
||||||
|
CreateFile("packfiles/morefiles/file1.txt");
|
||||||
|
CreateFile("packfiles/morefiles/file2.txt");
|
||||||
|
CreateFile("packfiles/file3.txt");
|
||||||
|
CreateFile("mappackfiles/morefiles/file4.txt");
|
||||||
|
CreateFile("mappackfiles/morefiles/file5.txt");
|
||||||
|
CreateFile("mappackfiles/file6.txt");
|
||||||
|
|
||||||
|
var project = GetProject(json);
|
||||||
|
var sourceBasePath = project.PackOptions.PackInclude.SourceBasePath;
|
||||||
|
var targetBasePath = PathUtility.GetPathWithDirectorySeparator("basepath/");
|
||||||
|
|
||||||
|
var packIncludeEntries = GetIncludeFiles(project.PackOptions.PackInclude, targetBasePath);
|
||||||
|
|
||||||
|
packIncludeEntries.Should().HaveCount(5);
|
||||||
|
|
||||||
|
packIncludeEntries.Should().Contain(entry =>
|
||||||
|
entry.SourcePath == Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator("packfiles/morefiles/file1.txt")) &&
|
||||||
|
entry.TargetPath == Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator("packfiles/morefiles/file1.txt")));
|
||||||
|
packIncludeEntries.Should().Contain(entry =>
|
||||||
|
entry.SourcePath == Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator("packfiles/file3.txt")) &&
|
||||||
|
entry.TargetPath == Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator("packfiles/file3.txt")));
|
||||||
|
packIncludeEntries.Should().Contain(entry =>
|
||||||
|
entry.SourcePath == Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator("mappackfiles/morefiles/file4.txt")) &&
|
||||||
|
entry.TargetPath == Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator("somepath/morefiles/file4.txt")));
|
||||||
|
packIncludeEntries.Should().Contain(entry =>
|
||||||
|
entry.SourcePath == Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator("mappackfiles/morefiles/file5.txt")) &&
|
||||||
|
entry.TargetPath == Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator("somepath/morefiles/file5.txt")));
|
||||||
|
packIncludeEntries.Should().Contain(entry =>
|
||||||
|
entry.SourcePath == Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator("mappackfiles/file6.txt")) &&
|
||||||
|
entry.TargetPath == Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator("somepath/file6.txt")));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void It_handles_paths_with_directory_traversals_properly()
|
||||||
|
{
|
||||||
|
var json = JObject.Parse(@"{
|
||||||
|
'publishOptions': {
|
||||||
|
'include': '../pubfiles/**/*.txt',
|
||||||
|
'excludeFiles': '../pubfiles/morefiles/file2.txt',
|
||||||
|
'mappings': {
|
||||||
|
'somepath/': '../mappubfiles/**/*.txt'
|
||||||
|
}
|
||||||
|
}}");
|
||||||
|
|
||||||
|
CreateFile("../pubfiles/morefiles/file1.txt");
|
||||||
|
CreateFile("../pubfiles/morefiles/file2.txt");
|
||||||
|
CreateFile("../pubfiles/file3.txt");
|
||||||
|
CreateFile("../mappubfiles/morefiles/file4.txt");
|
||||||
|
CreateFile("../mappubfiles/morefiles/file5.txt");
|
||||||
|
CreateFile("../mappubfiles/file6.txt");
|
||||||
|
|
||||||
|
var project = GetProject(json);
|
||||||
|
var sourceBasePath = project.PublishOptions.SourceBasePath;
|
||||||
|
var targetBasePath = PathUtility.GetPathWithDirectorySeparator("basepath/");
|
||||||
|
|
||||||
|
var publishEntries = GetIncludeFiles(project.PublishOptions, targetBasePath);
|
||||||
|
|
||||||
|
publishEntries.Should().HaveCount(5);
|
||||||
|
|
||||||
|
publishEntries.Should().Contain(entry =>
|
||||||
|
entry.SourcePath == Path.GetFullPath(Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator("../pubfiles/morefiles/file1.txt"))) &&
|
||||||
|
entry.TargetPath == Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator("pubfiles/morefiles/file1.txt")));
|
||||||
|
publishEntries.Should().Contain(entry =>
|
||||||
|
entry.SourcePath == Path.GetFullPath(Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator("../pubfiles/file3.txt"))) &&
|
||||||
|
entry.TargetPath == Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator("pubfiles/file3.txt")));
|
||||||
|
publishEntries.Should().Contain(entry =>
|
||||||
|
entry.SourcePath == Path.GetFullPath(Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator("../mappubfiles/morefiles/file4.txt"))) &&
|
||||||
|
entry.TargetPath == Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator("somepath/morefiles/file4.txt")));
|
||||||
|
publishEntries.Should().Contain(entry =>
|
||||||
|
entry.SourcePath == Path.GetFullPath(Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator("../mappubfiles/morefiles/file5.txt"))) &&
|
||||||
|
entry.TargetPath == Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator("somepath/morefiles/file5.txt")));
|
||||||
|
publishEntries.Should().Contain(entry =>
|
||||||
|
entry.SourcePath == Path.GetFullPath(Path.Combine(sourceBasePath, PathUtility.GetPathWithDirectorySeparator("../mappubfiles/file6.txt"))) &&
|
||||||
|
entry.TargetPath == Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator("somepath/file6.txt")));
|
||||||
|
}
|
||||||
|
|
||||||
private Project GetProject(JObject json, ProjectReaderSettings settings = null)
|
private Project GetProject(JObject json, ProjectReaderSettings settings = null)
|
||||||
{
|
{
|
||||||
using (var stream = new MemoryStream())
|
using (var stream = new MemoryStream())
|
||||||
|
|
|
@ -109,7 +109,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
|
||||||
File.Exists(outputPackage).Should().BeTrue(outputPackage);
|
File.Exists(outputPackage).Should().BeTrue(outputPackage);
|
||||||
|
|
||||||
var zip = ZipFile.Open(outputPackage, ZipArchiveMode.Read);
|
var zip = ZipFile.Open(outputPackage, ZipArchiveMode.Read);
|
||||||
zip.Entries.Should().Contain(e => e.FullName == "pack1.txt");
|
zip.Entries.Should().Contain(e => e.FullName == "packfiles/pack1.txt");
|
||||||
zip.Entries.Should().Contain(e => e.FullName == "newpath/pack2.txt");
|
zip.Entries.Should().Contain(e => e.FullName == "newpath/pack2.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue