Merge pull request #2864 from dotnet/ajbaaska/fix-mappings

Maintain folder structure in mappings
This commit is contained in:
Eric Erhardt 2016-05-19 07:40:23 -05:00
commit 2837db914c
5 changed files with 165 additions and 32 deletions

View file

@ -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;
} }
} }
} }

View file

@ -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;

View file

@ -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())

View file

@ -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())

View file

@ -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");
} }