Skip to content

Commit

Permalink
Merge pull request #9542 from dependabot/dev/brettfo/packages-without…
Browse files Browse the repository at this point in the history
…-assemblies

allow updating package without assemblies from `packages.config`
  • Loading branch information
thavaahariharangit committed Apr 25, 2024
2 parents bd9b132 + 68d55fc commit 4403c92
Show file tree
Hide file tree
Showing 7 changed files with 420 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@

<ItemGroup>
<Compile Include="$(NuGetSourceLocation)\src\NuGet.Core\NuGet.Packaging\**\*.cs" />
<!--
The `PackageFolderReader.GetFiles()` method is case-sensitive which doesn't work for some scenarios so this
directory contains a copy of that file with that method with a case-insensitive patch.
-->
<Compile Remove="$(NuGetSourceLocation)\src\NuGet.Core\NuGet.Packaging\PackageFolderReader.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using NuGet.Common;
using NuGet.Frameworks;
using NuGet.Packaging.Core;
using NuGet.Packaging.Signing;

namespace NuGet.Packaging
{
/// <summary>
/// Reads an unzipped nupkg folder.
/// </summary>
public class PackageFolderReader : PackageReaderBase
{
private readonly DirectoryInfo _root;

/// <summary>
/// Package folder reader
/// </summary>
public PackageFolderReader(string folderPath)
: this(folderPath, DefaultFrameworkNameProvider.Instance, DefaultCompatibilityProvider.Instance)
{
}

/// <summary>
/// Package folder reader
/// </summary>
/// <param name="folder">root directory of an extracted nupkg</param>
public PackageFolderReader(DirectoryInfo folder)
: this(folder, DefaultFrameworkNameProvider.Instance, DefaultCompatibilityProvider.Instance)
{
}

/// <summary>
/// Package folder reader
/// </summary>
/// <param name="folderPath">root directory of an extracted nupkg</param>
/// <param name="frameworkProvider">framework mappings</param>
/// <param name="compatibilityProvider">framework compatibility provider</param>
public PackageFolderReader(string folderPath, IFrameworkNameProvider frameworkProvider, IFrameworkCompatibilityProvider compatibilityProvider)
: this(new DirectoryInfo(folderPath), frameworkProvider, compatibilityProvider)
{
}

/// <summary>
/// Package folder reader
/// </summary>
/// <param name="folder">root directory of an extracted nupkg</param>
/// <param name="frameworkProvider">framework mappings</param>
/// <param name="compatibilityProvider">framework compatibility provider</param>
public PackageFolderReader(DirectoryInfo folder, IFrameworkNameProvider frameworkProvider, IFrameworkCompatibilityProvider compatibilityProvider)
: base(frameworkProvider, compatibilityProvider)
{
_root = folder;
}

public override string GetNuspecFile()
{
// This needs to be explicitly case insensitive in order to work on XPlat, since GetFiles is normally case sensitive on non-Windows
var nuspecFiles = _root.GetFiles("*.*", SearchOption.TopDirectoryOnly).Where(f => f.Name.EndsWith(".nuspec", StringComparison.OrdinalIgnoreCase)).ToArray();

if (nuspecFiles.Length == 0)
{
var message = new StringBuilder();
message.Append(Strings.Error_MissingNuspecFile);
message.AppendFormat(CultureInfo.CurrentCulture, Strings.Message_Path, _root.FullName);
throw new PackagingException(NuGetLogCode.NU5037, message.ToString());
}
else if (nuspecFiles.Length > 1)
{
throw new PackagingException(Strings.MultipleNuspecFiles);
}

return nuspecFiles[0].FullName;
}

/// <summary>
/// Opens a local file in read only mode.
/// </summary>
public override Stream GetStream(string path)
{
return GetFile(path).OpenRead();
}

private FileInfo GetFile(string path)
{
var file = new FileInfo(Path.Combine(_root.FullName, path));

if (!file.FullName.StartsWith(_root.FullName, StringComparison.OrdinalIgnoreCase))
{
// the given path does not appear under the folder root
throw new FileNotFoundException(path);
}

return file;
}

public override IEnumerable<string> GetFiles()
{
// Read all files starting at the root.
return GetFiles(folder: null);
}

public override IEnumerable<string> GetFiles(string folder)
{
// Default to retrieve files and throwing if the root
// directory is not found.
var getFiles = true;
var searchFolder = new DirectoryInfo(_root.FullName);

if (!string.IsNullOrEmpty(folder))
{
// Search in the sub folder if one was specified
searchFolder = new DirectoryInfo(Path.Combine(_root.FullName, folder));

// For sub folders verify it exists
// The root is expected to exist and should throw if it does not
getFiles = searchFolder.Exists;

// try a case-insensitive search
if (!getFiles)
{
searchFolder = _root.GetDirectories().FirstOrDefault(d => d.Name.Equals(folder, StringComparison.OrdinalIgnoreCase));
getFiles = searchFolder?.Exists == true;
}
}

if (getFiles)
{
// Enumerate root folder filtering out nupkg files
foreach (var file in searchFolder.GetFiles("*", SearchOption.AllDirectories))
{
var path = GetRelativePath(_root, file);

// disallow nupkgs in the root
if (!IsFileInRoot(path) || !IsNupkg(path))
{
yield return path;
}
}
}

yield break;
}

/// <summary>
/// True if the path does not contain /
/// </summary>
private static bool IsFileInRoot(string path)
{
#if NETCOREAPP
return path.IndexOf('/', StringComparison.Ordinal) == -1;
#else
return path.IndexOf('/') == -1;
#endif
}

/// <summary>
/// True if the path ends with .nupkg
/// </summary>
private static bool IsNupkg(string path)
{
return path.EndsWith(PackagingCoreConstants.NupkgExtension, StringComparison.OrdinalIgnoreCase) == true;
}

/// <summary>
/// Build the relative path in the same format that ZipArchive uses
/// </summary>
private static string GetRelativePath(DirectoryInfo root, FileInfo file)
{
var parents = new Stack<DirectoryInfo>();

var parent = file.Directory;

while (parent != null
&& !StringComparer.OrdinalIgnoreCase.Equals(parent.FullName, root.FullName))
{
parents.Push(parent);
parent = parent.Parent;
}

if (parent == null)
{
// the given file path does not appear under root
throw new FileNotFoundException(file.FullName);
}

var parts = parents.Select(d => d.Name).Concat(new string[] { file.Name });

return string.Join("/", parts);
}

public override IEnumerable<string> CopyFiles(
string destination,
IEnumerable<string> packageFiles,
ExtractPackageFileDelegate extractFile,
ILogger logger,
CancellationToken token)
{
var filesCopied = new List<string>();

foreach (var packageFile in packageFiles)
{
token.ThrowIfCancellationRequested();

var sourceFile = GetFile(packageFile);

var targetPath = Path.Combine(destination, packageFile);
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));

using (var fileStream = sourceFile.OpenRead())
{
targetPath = extractFile(sourceFile.FullName, targetPath, fileStream);
if (targetPath != null)
{
ZipArchiveExtensions.UpdateFileTime(targetPath, sourceFile.LastWriteTimeUtc);
filesCopied.Add(targetPath);
}
}
}

return filesCopied;
}

protected override void Dispose(bool disposing)
{
// do nothing here
}

public override Task<PrimarySignature> GetPrimarySignatureAsync(CancellationToken token)
{
return TaskResult.Null<PrimarySignature>();
}

public override Task<bool> IsSignedAsync(CancellationToken token)
{
return TaskResult.False;
}

public override Task ValidateIntegrityAsync(SignatureContent signatureContent, CancellationToken token)
{
throw new NotImplementedException();
}

public override Task<byte[]> GetArchiveHashAsync(HashAlgorithmName hashAlgorithm, CancellationToken token)
{
throw new NotImplementedException();
}

public override bool CanVerifySignedPackages(SignedPackageVerifierSettings verifierSettings)
{
return false;
}

public override string GetContentHash(CancellationToken token, Func<string> GetUnsignedPackageHash = null)
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class PackagesConfigUpdaterTests : TestBase
public void PathToPackagesDirectoryCanBeDetermined(string projectContents, string dependencyName, string dependencyVersion, string expectedPackagesDirectoryPath)
{
var projectBuildFile = ProjectBuildFile.Parse("/", "project.csproj", projectContents);
var actualPackagesDirectorypath = PackagesConfigUpdater.GetPathToPackagesDirectory(projectBuildFile, dependencyName, dependencyVersion);
var actualPackagesDirectorypath = PackagesConfigUpdater.GetPathToPackagesDirectory(projectBuildFile, dependencyName, dependencyVersion, "packages.config");
Assert.Equal(expectedPackagesDirectoryPath, actualPackagesDirectorypath);
}

Expand Down
Loading

0 comments on commit 4403c92

Please sign in to comment.