Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented nuget.exe add and nuget.exe init commands #14

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/NuGet.Clients/NuGet.CommandLine/Commands/AddCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Threading;
using System.Threading.Tasks;

namespace NuGet.CommandLine
{
[Command(typeof(NuGetCommand), "add", "AddCommandDescription",
MinArgs = 1, MaxArgs = 1, UsageDescriptionResourceName = "AddCommandUsageDescription",
UsageSummaryResourceName = "AddCommandUsageSummary", UsageExampleResourceName = "AddCommandUsageExamples")]
public class AddCommand : Command
{
[Option(typeof(NuGetCommand), "AddCommandSourceDescription", AltName = "src")]
public string Source { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check if we have a way to declare mandatory parameters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. We do not have a way today to declare mandatory parameters.


[Option(typeof(NuGetCommand), "ExpandDescription")]
public bool Expand { get; set; }

public override async Task ExecuteCommandAsync()
{
// Arguments[0] will not be null at this point.
// Because, this command has MinArgs set to 1.
var packagePath = Arguments[0];

if (string.IsNullOrEmpty(Source))
{
throw new CommandLineException(
LocalizedResourceManager.GetString(nameof(NuGetResources.AddCommand_SourceNotProvided)));
}

OfflineFeedUtility.ThrowIfInvalidOrNotFound(
packagePath,
isDirectory: false,
nameOfNotFoundErrorResource: nameof(NuGetResources.NupkgPath_NotFound));

// If the Source Feed Folder does not exist, it will be created.
OfflineFeedUtility.ThrowIfInvalid(Source);

var offlineFeedAddContext = new OfflineFeedAddContext(
packagePath,
Source,
Console, // IConsole is an ILogger
throwIfSourcePackageIsInvalid: true,
throwIfPackageExistsAndInvalid: true,
throwIfPackageExists: false,
expand: Expand);

await OfflineFeedUtility.AddPackageToSource(offlineFeedAddContext, CancellationToken.None);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this throw? if so which exception the user will get?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OfflineFeedAddContext, which is passed in to the method, has that information. It will always throw CommandLineException

}
}
}
151 changes: 151 additions & 0 deletions src/NuGet.Clients/NuGet.CommandLine/Commands/InitCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace NuGet.CommandLine
{
[Command(typeof(NuGetCommand), "init", "InitCommandDescription",
MinArgs = 2, MaxArgs = 2, UsageDescriptionResourceName = "InitCommandUsageDescription",
UsageSummaryResourceName = "InitCommandUsageSummary", UsageExampleResourceName = "InitCommandUsageExamples")]
public class InitCommand : Command
{
[Option(typeof(NuGetCommand), "ExpandDescription")]
public bool Expand { get; set; }

public override async Task ExecuteCommandAsync()
{
// Arguments[0] or Arguments[1] will not be null at this point.
// Because, this command has MinArgs set to 2.
var source = Arguments[0];
var destination = Arguments[1];

OfflineFeedUtility.ThrowIfInvalidOrNotFound(
source,
isDirectory: true,
nameOfNotFoundErrorResource: nameof(NuGetResources.InitCommand_FeedIsNotFound));

// If the Destination Feed Folder does not exist, it will be created.
OfflineFeedUtility.ThrowIfInvalid(destination);

var packagePaths = GetPackageFilePaths(source, "*" + ProjectManagement.Constants.PackageExtension);

if (packagePaths.Count > 0)
{
foreach (var packagePath in packagePaths)
{
var offlineFeedAddContext = new OfflineFeedAddContext(
packagePath,
destination,
Console, // IConsole is an ILogger
throwIfSourcePackageIsInvalid: false,
throwIfPackageExistsAndInvalid: false,
throwIfPackageExists: false,
expand: Expand);

await OfflineFeedUtility.AddPackageToSource(offlineFeedAddContext, CancellationToken.None);
}
}
else
{
var message = string.Format(
CultureInfo.CurrentCulture,
LocalizedResourceManager.GetString(nameof(NuGetResources.InitCommand_FeedContainsNoPackages)),
source);

Console.LogInformation(message);
}
}

/// <summary>
/// Helper method based on LocalPackageRepository and ExpandedPackageRepository
/// to avoid dependency on NuGet.Core. Links to the classes are
/// https://github.com/NuGet/NuGet2/blob/2.9/src/Core/Repositories/LocalPackageRepository.cs
/// AND
/// https://github.com/NuGet/NuGet2/blob/2.9/src/Core/Repositories/ExpandedPackageRepository.cs
/// </summary>
private static IReadOnlyList<string> GetPackageFilePaths(string source, string nupkgFilter)
{
var packagePaths = new List<string>();
var isV2StyleFolderSource = IsV2StyleFolderSource(source, nupkgFilter);

if (!isV2StyleFolderSource.HasValue)
{
// There are no nupkg files, v2-style or v3-style, under 'source'.
return packagePaths;
}

if (isV2StyleFolderSource.Value)
{
foreach (var idDirectory in Directory.EnumerateDirectories(source))
{
// Since we need the .nupkg file for nuget.exe init, PackageSaveMode.Nuspec is not supported.
// And, Default search option for EnumerateFiles is top directory only.
var packagesAtIdDirectory = Directory.EnumerateFiles(idDirectory, nupkgFilter);

packagePaths.AddRange(packagesAtIdDirectory);
}

var packagesAtRoot = Directory.EnumerateFiles(source, nupkgFilter);
packagePaths.AddRange(packagesAtRoot);
}
else
{
foreach (var idDirectory in Directory.EnumerateDirectories(source))
{
var packageId = Path.GetFileName(idDirectory);

foreach (var versionDirectory in Directory.EnumerateDirectories(idDirectory))
{
var packagesAtVersionDirectory = Directory.EnumerateFiles(versionDirectory, nupkgFilter);
packagePaths.AddRange(packagesAtVersionDirectory);
}
}
}

return packagePaths;
}

/// <summary>
/// Helper method based on the LazyLocalPackageRepository.cs to avoid dependency on NuGet.Core
/// https://github.com/NuGet/NuGet2/blob/2.9/src/Core/Repositories/LazyLocalPackageRepository.cs#L74
/// </summary>
/// <returns>Return true if source v2 style folder. Otherwise, false.
/// If no nupkgs were found under the source, returns null</returns>
private static bool? IsV2StyleFolderSource(string source, string nupkgFilter)
{
var packagesAtRoot = Directory.EnumerateFiles(source, nupkgFilter);

if (packagesAtRoot.Any())
{
return true;
}

foreach (var idDirectory in Directory.EnumerateDirectories(source))
{
// Since we need the .nupkg file for nuget.exe init, PackageSaveMode.Nuspec is not supported.
// And, Default search option for EnumerateFiles is top directory only.
var packagesAtIdDirectory = Directory.EnumerateFiles(idDirectory, nupkgFilter);

if (packagesAtIdDirectory.Any())
{
return true;
}

foreach (var versionDirectory in Directory.EnumerateDirectories(idDirectory))
{
var packagesAtVersionDirectory = Directory.EnumerateFiles(versionDirectory, nupkgFilter);

if (packagesAtVersionDirectory.Any())
{
return false;
}
}
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
<Compile Include="CommandLineSourceRepositoryProvider.cs" />
<Compile Include="CommandManager.cs" />
<Compile Include="CommandOutputLogger.cs" />
<Compile Include="Commands\AddCommand.cs" />
<Compile Include="Commands\Command.cs" />
<Compile Include="Commands\CommandAttribute.cs" />
<Compile Include="Commands\ConfigCommand.cs" />
Expand All @@ -56,6 +57,7 @@
<Compile Include="Commands\HelpCommand.cs" />
<Compile Include="Commands\HelpCommandMarkdownTemplate.cs" />
<Compile Include="Commands\ICommand.cs" />
<Compile Include="Commands\InitCommand.cs" />
<Compile Include="Commands\InstallCommand.cs" />
<Compile Include="Commands\ListCommand.cs" />
<Compile Include="Commands\OptionAttribute.cs" />
Expand Down Expand Up @@ -107,6 +109,8 @@
<DesignTime>True</DesignTime>
<DependentUpon>NuGetResources.resx</DependentUpon>
</Compile>
<Compile Include="OfflineFeedAddContext.cs" />
<Compile Include="OfflineFeedUtility.cs" />
<Compile Include="PackageServer.cs" />
<Compile Include="PackageSourceBuilder.cs" />
<Compile Include="Program.cs" />
Expand Down
103 changes: 103 additions & 0 deletions src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.resx
Original file line number Diff line number Diff line change
Expand Up @@ -5245,4 +5245,41 @@ nuget update -Self</value>
<data name="CommandMSBuildVersion" xml:space="preserve">
<value>Specifies the version of MSBuild to be used with this command. Supported values are 4, 12, 14. By default the MSBuild in your path is picked, otherwise it defaults to the highest installed version of MSBuild.</value>
</data>
<data name="AddCommandDescription" xml:space="preserve">
<value>Adds the given package to a hierarchical source. http sources are not supported. For more info, goto https://docs.nuget.org/consume/command-line-reference#add-command.</value>
</data>
<data name="AddCommandUsageDescription" xml:space="preserve">
<value>Specify the path to the package to be added to the specified file source.</value>
</data>
<data name="AddCommandUsageExamples" xml:space="preserve">
<value>nuget add foo.nupkg -Source c:\bar\

nuget add foo.nupkg -Source \\bar\packages\</value>
</data>
<data name="AddCommandUsageSummary" xml:space="preserve">
<value>&lt;packagePath&gt; -Source &lt;fileSourceFolder&gt; [options]</value>
</data>
<data name="AddCommandSourceDescription" xml:space="preserve">
<value>Specifies the fileSourceFolder to which the nupkg will be added. http sources are not supported.</value>
</data>
<data name="InitCommandDescription" xml:space="preserve">
<value>Adds all the packages from the &lt;srcFeed&gt; to the hierarchical &lt;destFeed&gt;. http feeds are not supported. For more info, goto https://docs.nuget.org/consume/command-line-reference#init-command.</value>
</data>
<data name="InitCommandSourceDescription" xml:space="preserve">
<value>Specifies the fileSourceFolder. Cannot be an http source.</value>
</data>
<data name="InitCommandUsageDescription" xml:space="preserve">
<value>Specify the path to the feed to be added to the specified destination feed</value>
</data>
<data name="InitCommandUsageExamples" xml:space="preserve">
<value>nuget init c:\foo c:\bar

nuget add \\foo\packages \\bar\packages</value>
</data>
<data name="InitCommandUsageSummary" xml:space="preserve">
<value>&lt;srcFeedPath&gt; &lt;destFeedPath&gt; [options]</value>
</data>
<data name="ExpandDescription" xml:space="preserve">
<value>If provided, a package added to offline feed is also expanded.</value>
</data>
</root>
Loading