Skip to content

Commit e29dea3

Browse files
authored
Add support for local feeds in PackageRepository (#211)
1 parent 3a242ca commit e29dea3

File tree

14 files changed

+110
-19
lines changed

14 files changed

+110
-19
lines changed

README.md

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ NuGet and MSBuild are very tightly coupled and a lot of times you need packages
212212
## Package Repository
213213
Create a package repository if you want to generate packages as if they've already been installed. If you want to create actual `.nupkg` packages, see [Package Feed]
214214

215-
### Example
215+
### Examples
216216

217217
Create a package repository with a package that supports two target frameworks:
218218

@@ -246,23 +246,39 @@ By default, all package sources are disabled so that when running unit tests pac
246246

247247
You can add feeds if needed with the following examples:
248248

249+
#### Add nuget.org as a feed
249250
```C#
250-
using(PackageRepository.Create(
251-
rootPath,
252-
feeds: new[] { new Uri("https://api.nuget.org/v3/index.json") })
251+
using(PackageRepository.Create(rootPath, feeds: new Uri("https://api.nuget.org/v3/index.json"))
253252
{
254253
}
255254

256255
```
257256

257+
#### Add a local directory as a feed
258258
```C#
259-
using(PackageRepository.Create(rootPath)
260-
.Feed("https://api.nuget.org/v3/index.json")
259+
DirectoryInfo localFeed = new DirectoryInfo("<path to local feed>");
260+
261+
using(PackageRepository.Create(rootPath, feeds: new Uri(localFeed.FullName))
261262
{
262263
}
263264

264265
```
265266

267+
#### Create a local feed on-the-fly
268+
```C#
269+
// Create a local feed with one package
270+
string TestRootPath = "<path to folder used for testing>";
271+
string FeedRootPath = Path.Combine(TestRootPath, "Feed");
272+
273+
using PackageFeed packageFeed = PackageFeed.Create(FeedRootPath)
274+
.Package("PackageA", "1.0.0", out Package packageA)
275+
.Library(TargetFramework)
276+
.Save();
277+
278+
// Create a package repository that points to the local feed
279+
using PackageRepository packageRepository = PackageRepository.Create(TestRootPath, feeds: packageFeed);
280+
```
281+
266282
## Package Feed
267283
Create a package feed if you want to generate `.nupkg` packages that can be installed by NuGet. If you want to create a repository of packages as if they've already been installed, see [Package Repository].
268284

src/Microsoft.Build.Utilities.ProjectCreation.UnitTests/PackageFeedTests/PackageFeedTests.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// Licensed under the MIT license.
44

55
using Shouldly;
6+
using System;
7+
using System.IO;
68
using System.Linq;
79
using Xunit;
810

@@ -14,7 +16,7 @@ public class PackageFeedTests : PackageFeedTestBase
1416
public void BasicPackage()
1517
{
1618
using PackageFeed packageFeed = PackageFeed.Create(FeedRootPath)
17-
.Package("PackageA", "1.0.0", out _, "John Smith", "Custom Description", true)
19+
.Package("PackageA", "1.0.0", out _, "John Smith", "Custom Description", developmentDependency: true)
1820
.Library("net45")
1921
.Save();
2022

@@ -44,7 +46,7 @@ public void BasicPackage()
4446
public void BasicPackageWithDependency()
4547
{
4648
using PackageFeed packageFeed = PackageFeed.Create(FeedRootPath)
47-
.Package("PackageA", "1.0.0", out _, "John Smith", "Custom Description", true)
49+
.Package("PackageA", "1.0.0", out _, "John Smith", "Custom Description", developmentDependency: true)
4850
.Library("net45")
4951
.Dependency("net45", "PackageB", "2.0.0")
5052
.Save();
@@ -74,5 +76,24 @@ public void BasicPackageWithDependency()
7476
dependency.Version.ShouldBe("2.0.0");
7577
dependency.ExcludeAssets.ShouldBe("Build, Analyzers");
7678
}
79+
80+
[Fact]
81+
public void RestoreCanConsumePackage()
82+
{
83+
using PackageFeed packageFeed = PackageFeed.Create(FeedRootPath)
84+
.Package("PackageA", "1.0.0", out Package packageA)
85+
.Library(TargetFramework)
86+
.Save();
87+
88+
using PackageRepository packageRepository = PackageRepository.Create(TestRootPath, feeds: packageFeed);
89+
90+
ProjectCreator.Templates.SdkCsproj(
91+
path: Path.Combine(TestRootPath, "ClassLibraryA", "ClassLibraryA.csproj"),
92+
targetFramework: TargetFramework)
93+
.ItemPackageReference(packageA)
94+
.TryRestore(out bool result, out BuildOutput buildOutput);
95+
96+
result.ShouldBeTrue(buildOutput.GetConsoleLog());
97+
}
7798
}
7899
}

src/Microsoft.Build.Utilities.ProjectCreation.UnitTests/PackageRepositoryTests/RepositoryTests.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System;
88
using System.Collections.Generic;
99
using System.IO;
10+
using System.Xml.Linq;
1011
using Xunit;
1112

1213
namespace Microsoft.Build.Utilities.ProjectCreation.UnitTests.PackageRepositoryTests
@@ -57,6 +58,48 @@ public void BuildCanConsumePackage()
5758
}
5859
}
5960

61+
[Fact]
62+
public void LocalFeedsAreAddedCorrectly()
63+
{
64+
DirectoryInfo feed1 = Directory.CreateDirectory(Path.Combine(TestRootPath, "Feed1"));
65+
DirectoryInfo feed2 = Directory.CreateDirectory(Path.Combine(TestRootPath, "Feed2"));
66+
67+
using (PackageRepository.Create(TestRootPath, new Uri(feed1.FullName), new Uri(feed2.FullName)))
68+
{
69+
FileInfo nuGetConfig = new FileInfo(Path.Combine(TestRootPath, "NuGet.config"));
70+
71+
nuGetConfig.ShouldExist();
72+
73+
XDocument xmlDocument = XDocument.Load(nuGetConfig.FullName);
74+
75+
XElement packageSourcesElement = xmlDocument
76+
.Element("configuration")
77+
.Element("packageSources");
78+
79+
packageSourcesElement.ShouldNotBeNull();
80+
81+
IEnumerator<XElement> enumerator = packageSourcesElement.Elements().GetEnumerator();
82+
83+
for (int i = 0; i < 3; i++)
84+
{
85+
enumerator.MoveNext().ShouldBeTrue();
86+
87+
XElement element = enumerator.Current;
88+
89+
if (i == 0)
90+
{
91+
element.Name.ShouldBe("clear");
92+
continue;
93+
}
94+
95+
element.Name.ShouldBe("add");
96+
element.Attribute("key").Value.ShouldBe($"Local{i}");
97+
98+
element.Attribute("value").Value.ShouldBe(i == 1 ? feed1.FullName : feed2.FullName);
99+
}
100+
}
101+
}
102+
60103
[Fact]
61104
public void CanSetAllPackageProperties()
62105
{

src/Microsoft.Build.Utilities.ProjectCreation/PackageFeed.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ private PackageFeed(DirectoryInfo rootPath)
2323
_rootPath.Create();
2424
}
2525

26+
/// <summary>
27+
/// Converts the current <see cref="PackageFeed" /> to a <see cref="Uri" />.
28+
/// </summary>
29+
/// <param name="packageFeed">The <see cref="PackageFeed" /> to convert.</param>
30+
public static implicit operator Uri(PackageFeed packageFeed) => new Uri(packageFeed._rootPath.FullName);
31+
2632
/// <summary>
2733
/// Creates a new <see cref="PackageFeed" /> instance at the specified path.
2834
/// </summary>

src/Microsoft.Build.Utilities.ProjectCreation/PackageRepository.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,15 @@ private PackageRepository(string rootPath, IEnumerable<Uri>? feeds = null)
4949

5050
if (feeds != null)
5151
{
52+
int i = 1;
53+
5254
foreach (Uri? feed in feeds.Where(i => i != null))
5355
{
56+
string feedName = feed.IsFile ? $"Local{i++}" : feed.Host;
57+
5458
writer.WriteStartElement("add");
55-
writer.WriteAttributeString("key", feed.Host);
56-
writer.WriteAttributeString("value", feed.ToString());
59+
writer.WriteAttributeString("key", feedName);
60+
writer.WriteAttributeString("value", feed.IsFile ? feed.LocalPath : feed.ToString());
5761
writer.WriteEndElement();
5862
}
5963
}
@@ -80,10 +84,7 @@ private PackageRepository(string rootPath, IEnumerable<Uri>? feeds = null)
8084
/// <param name="rootPath">The root directory to create a package repository directory in.</param>
8185
/// <param name="feeds">Optional feeds to include in the configuration.</param>
8286
/// <returns>A <see cref="PackageRepository" /> object that is used to construct an NuGet package repository.</returns>
83-
public static PackageRepository Create(string rootPath, IEnumerable<Uri>? feeds = null)
84-
{
85-
return new PackageRepository(rootPath, feeds);
86-
}
87+
public static PackageRepository Create(string rootPath, params Uri[] feeds) => new PackageRepository(rootPath, feeds);
8788

8889
/// <inheritdoc cref="IDisposable.Dispose" />
8990
public void Dispose()

src/Microsoft.Build.Utilities.ProjectCreation/PublicAPI/net472/PublicAPI.Shipped.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ static Microsoft.Build.Utilities.ProjectCreation.MSBuildAssemblyResolver.SearchP
296296
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Create(string! rootPath) -> Microsoft.Build.Utilities.ProjectCreation.PackageFeed!
297297
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Create(System.IO.DirectoryInfo! rootPath) -> Microsoft.Build.Utilities.ProjectCreation.PackageFeed!
298298
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Templates.get -> Microsoft.Build.Utilities.ProjectCreation.PackageFeedTemplates!
299-
static Microsoft.Build.Utilities.ProjectCreation.PackageRepository.Create(string! rootPath, System.Collections.Generic.IEnumerable<System.Uri!>? feeds = null) -> Microsoft.Build.Utilities.ProjectCreation.PackageRepository!
300299
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.Create(string? path = null, string? defaultTargets = null, string? initialTargets = null, string? sdk = null, string? toolsVersion = null, string? treatAsLocalProperty = null, Microsoft.Build.Evaluation.ProjectCollection? projectCollection = null, Microsoft.Build.Evaluation.NewProjectFileOptions? projectFileOptions = null, System.Collections.Generic.IDictionary<string!, string!>? globalProperties = null) -> Microsoft.Build.Utilities.ProjectCreation.ProjectCreator!
301300
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.implicit operator Microsoft.Build.Construction.ProjectRootElement!(Microsoft.Build.Utilities.ProjectCreation.ProjectCreator! creator) -> Microsoft.Build.Construction.ProjectRootElement!
302301
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.implicit operator Microsoft.Build.Evaluation.Project!(Microsoft.Build.Utilities.ProjectCreation.ProjectCreator! creator) -> Microsoft.Build.Evaluation.Project!
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
#nullable enable
2+
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.implicit operator System.Uri!(Microsoft.Build.Utilities.ProjectCreation.PackageFeed! packageFeed) -> System.Uri!
3+
static Microsoft.Build.Utilities.ProjectCreation.PackageRepository.Create(string! rootPath, params System.Uri![]! feeds) -> Microsoft.Build.Utilities.ProjectCreation.PackageRepository!

src/Microsoft.Build.Utilities.ProjectCreation/PublicAPI/net6.0/PublicAPI.Shipped.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ static Microsoft.Build.Utilities.ProjectCreation.MSBuildAssemblyResolver.SearchP
296296
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Create(string! rootPath) -> Microsoft.Build.Utilities.ProjectCreation.PackageFeed!
297297
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Create(System.IO.DirectoryInfo! rootPath) -> Microsoft.Build.Utilities.ProjectCreation.PackageFeed!
298298
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Templates.get -> Microsoft.Build.Utilities.ProjectCreation.PackageFeedTemplates!
299-
static Microsoft.Build.Utilities.ProjectCreation.PackageRepository.Create(string! rootPath, System.Collections.Generic.IEnumerable<System.Uri!>? feeds = null) -> Microsoft.Build.Utilities.ProjectCreation.PackageRepository!
300299
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.Create(string? path = null, string? defaultTargets = null, string? initialTargets = null, string? sdk = null, string? toolsVersion = null, string? treatAsLocalProperty = null, Microsoft.Build.Evaluation.ProjectCollection? projectCollection = null, Microsoft.Build.Evaluation.NewProjectFileOptions? projectFileOptions = null, System.Collections.Generic.IDictionary<string!, string!>? globalProperties = null) -> Microsoft.Build.Utilities.ProjectCreation.ProjectCreator!
301300
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.implicit operator Microsoft.Build.Construction.ProjectRootElement!(Microsoft.Build.Utilities.ProjectCreation.ProjectCreator! creator) -> Microsoft.Build.Construction.ProjectRootElement!
302301
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.implicit operator Microsoft.Build.Evaluation.Project!(Microsoft.Build.Utilities.ProjectCreation.ProjectCreator! creator) -> Microsoft.Build.Evaluation.Project!
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
#nullable enable
2+
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.implicit operator System.Uri!(Microsoft.Build.Utilities.ProjectCreation.PackageFeed! packageFeed) -> System.Uri!
3+
static Microsoft.Build.Utilities.ProjectCreation.PackageRepository.Create(string! rootPath, params System.Uri![]! feeds) -> Microsoft.Build.Utilities.ProjectCreation.PackageRepository!

src/Microsoft.Build.Utilities.ProjectCreation/PublicAPI/net7.0/PublicAPI.Shipped.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ static Microsoft.Build.Utilities.ProjectCreation.MSBuildAssemblyResolver.SearchP
296296
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Create(string! rootPath) -> Microsoft.Build.Utilities.ProjectCreation.PackageFeed!
297297
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Create(System.IO.DirectoryInfo! rootPath) -> Microsoft.Build.Utilities.ProjectCreation.PackageFeed!
298298
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Templates.get -> Microsoft.Build.Utilities.ProjectCreation.PackageFeedTemplates!
299-
static Microsoft.Build.Utilities.ProjectCreation.PackageRepository.Create(string! rootPath, System.Collections.Generic.IEnumerable<System.Uri!>? feeds = null) -> Microsoft.Build.Utilities.ProjectCreation.PackageRepository!
300299
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.Create(string? path = null, string? defaultTargets = null, string? initialTargets = null, string? sdk = null, string? toolsVersion = null, string? treatAsLocalProperty = null, Microsoft.Build.Evaluation.ProjectCollection? projectCollection = null, Microsoft.Build.Evaluation.NewProjectFileOptions? projectFileOptions = null, System.Collections.Generic.IDictionary<string!, string!>? globalProperties = null) -> Microsoft.Build.Utilities.ProjectCreation.ProjectCreator!
301300
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.implicit operator Microsoft.Build.Construction.ProjectRootElement!(Microsoft.Build.Utilities.ProjectCreation.ProjectCreator! creator) -> Microsoft.Build.Construction.ProjectRootElement!
302301
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.implicit operator Microsoft.Build.Evaluation.Project!(Microsoft.Build.Utilities.ProjectCreation.ProjectCreator! creator) -> Microsoft.Build.Evaluation.Project!
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
#nullable enable
2+
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.implicit operator System.Uri!(Microsoft.Build.Utilities.ProjectCreation.PackageFeed! packageFeed) -> System.Uri!
3+
static Microsoft.Build.Utilities.ProjectCreation.PackageRepository.Create(string! rootPath, params System.Uri![]! feeds) -> Microsoft.Build.Utilities.ProjectCreation.PackageRepository!

src/Microsoft.Build.Utilities.ProjectCreation/PublicAPI/netcoreapp3.1/PublicAPI.Shipped.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ static Microsoft.Build.Utilities.ProjectCreation.MSBuildAssemblyResolver.SearchP
296296
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Create(string! rootPath) -> Microsoft.Build.Utilities.ProjectCreation.PackageFeed!
297297
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Create(System.IO.DirectoryInfo! rootPath) -> Microsoft.Build.Utilities.ProjectCreation.PackageFeed!
298298
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.Templates.get -> Microsoft.Build.Utilities.ProjectCreation.PackageFeedTemplates!
299-
static Microsoft.Build.Utilities.ProjectCreation.PackageRepository.Create(string! rootPath, System.Collections.Generic.IEnumerable<System.Uri!>? feeds = null) -> Microsoft.Build.Utilities.ProjectCreation.PackageRepository!
300299
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.Create(string? path = null, string? defaultTargets = null, string? initialTargets = null, string? sdk = null, string? toolsVersion = null, string? treatAsLocalProperty = null, Microsoft.Build.Evaluation.ProjectCollection? projectCollection = null, Microsoft.Build.Evaluation.NewProjectFileOptions? projectFileOptions = null, System.Collections.Generic.IDictionary<string!, string!>? globalProperties = null) -> Microsoft.Build.Utilities.ProjectCreation.ProjectCreator!
301300
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.implicit operator Microsoft.Build.Construction.ProjectRootElement!(Microsoft.Build.Utilities.ProjectCreation.ProjectCreator! creator) -> Microsoft.Build.Construction.ProjectRootElement!
302301
static Microsoft.Build.Utilities.ProjectCreation.ProjectCreator.implicit operator Microsoft.Build.Evaluation.Project!(Microsoft.Build.Utilities.ProjectCreation.ProjectCreator! creator) -> Microsoft.Build.Evaluation.Project!
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
#nullable enable
2+
static Microsoft.Build.Utilities.ProjectCreation.PackageFeed.implicit operator System.Uri!(Microsoft.Build.Utilities.ProjectCreation.PackageFeed! packageFeed) -> System.Uri!
3+
static Microsoft.Build.Utilities.ProjectCreation.PackageRepository.Create(string! rootPath, params System.Uri![]! feeds) -> Microsoft.Build.Utilities.ProjectCreation.PackageRepository!

version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "9.0",
2+
"version": "10.0",
33
"assemblyVersion": "1.0",
44
"buildNumberOffset": -1,
55
"nugetPackageVersion": {

0 commit comments

Comments
 (0)