Skip to content

Commit ca23da4

Browse files
emgartenCopilot
andauthored
Add README support (#213)
* Add README support * Update src/SleetLib/Commands/InitCommand.cs Co-authored-by: Copilot <[email protected]> * Update ReleaseNotes.md Co-authored-by: Copilot <[email protected]> * Fixes --------- Co-authored-by: Copilot <[email protected]>
1 parent 6f4b189 commit ca23da4

File tree

11 files changed

+201
-21
lines changed

11 files changed

+201
-21
lines changed

ReleaseNotes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Release Notes
22

3+
## 6.5.0
4+
* Added README support. Flatcontainer will now place the README alongside the icon and nuspec files. Visual Studio package search will display README files the same as it does for nuget.org.
5+
36
## 6.4.0
47
* Added disablePayloadSigning option for S3 feeds [PR](https://github.com/emgarten/Sleet/pull/211)
58

build.sh

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env bash
2+
set -e
23

34
# No options are needed to run a basic build and unit tests
45
# To run functional tests against azure and or aws, use the following options:
@@ -26,18 +27,14 @@ while [ : ]; do
2627
export AWS_DEFAULT_REGION=$2
2728
shift 2
2829
;;
29-
--) shift;
30-
break
30+
--) shift;
31+
break
3132
;;
3233
esac
3334
done
3435

35-
RESULTCODE=0
3636
pushd $(pwd)
3737

3838
# Download dotnet cli and run tests
3939
. build/common/common.sh
40-
run_standard_tests
41-
42-
popd
43-
exit $RESULTCODE
40+
run_standard_tests

build/config.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<AzureStorageBlobsVersion>12.20.0</AzureStorageBlobsVersion>
77
<JsonVersion>13.0.3</JsonVersion>
88
<CommandLineUtilsVersion>2.0.0</CommandLineUtilsVersion>
9-
<NuGetTestHelpersVersion>2.1.36</NuGetTestHelpersVersion>
9+
<NuGetTestHelpersVersion>2.1.39</NuGetTestHelpersVersion>
1010
<PortablePdbVersion>1.5.0</PortablePdbVersion>
1111
<AWSSDKVersion>3.7.310.4</AWSSDKVersion>
1212
<AWSSDKTokenVersion>3.7.300.117</AWSSDKTokenVersion>

src/SleetLib/Commands/InitCommand.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public static async Task<bool> InitAsync(LocalSettings settings, ISleetFileSyste
9595
AddServiceIndexEntry(source.BaseURI, "registration/", "RegistrationsBaseUrl/3.4.0", "Package registrations used for search and packages.config.", serviceIndexJson);
9696
AddServiceIndexEntry(source.BaseURI, "", "ReportAbuseUriTemplate/3.0.0", "Report abuse template.", serviceIndexJson);
9797
AddServiceIndexEntry(source.BaseURI, "flatcontainer/", "PackageBaseAddress/3.0.0", "Packages used by project.json", serviceIndexJson);
98+
AddServiceIndexEntry(source.BaseURI, "flatcontainer/{lower_id}/{lower_version}/readme", "ReadmeUriTemplate/6.13.0", "URI template used by NuGet Client to construct a URL for downloading a package's README.", serviceIndexJson);
9899

99100
// Add symbols feed if enabled
100101
if (feedSettings.SymbolsEnabled)
@@ -151,10 +152,15 @@ private static void AddServiceIndexEntry(Uri baseUri, string relativeFilePath, s
151152
private static JObject GetServiceIndexEntry(Uri baseUri, string relativeFilePath, string type, string comment)
152153
{
153154
var id = UriUtility.GetPath(baseUri, relativeFilePath);
155+
var url = id.AbsoluteUri;
156+
157+
// Remove encoding for templates
158+
url = url.Replace("%7Blower_id%7D", "{lower_id}");
159+
url = url.Replace("%7Blower_version%7D", "{lower_version}");
154160

155161
var json = new JObject
156162
{
157-
["@id"] = id.AbsoluteUri,
163+
["@id"] = url,
158164
["@type"] = type,
159165
["comment"] = comment
160166
};

src/SleetLib/FileSystem/AmazonS3File.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ protected override async Task CopyToSource(ILogger log, CancellationToken token)
132132
{
133133
contentType = "image/png";
134134
}
135+
else if (absoluteUri.AbsoluteUri.EndsWith("/readme"))
136+
{
137+
contentType = "text/markdown";
138+
}
135139
else
136140
{
137141
log.LogWarning($"Unknown file type: {absoluteUri}");

src/SleetLib/FileSystem/AzureFile.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ protected override async Task CopyToSource(ILogger log, CancellationToken token)
9696
{
9797
blobHeaders.ContentType = "image/png";
9898
}
99+
else if (_blob.Uri.AbsoluteUri.EndsWith("/readme"))
100+
{
101+
blobHeaders.ContentType = "text/markdown";
102+
}
99103
else
100104
{
101105
log.LogWarning($"Unknown file type: {_blob.Uri.AbsoluteUri}");

src/SleetLib/Services/FlatContainer.cs

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ private void DeleteNupkg(PackageIdentity package)
6464
// Icon
6565
var iconFile = _context.Source.Get(GetIconPath(package));
6666
iconFile.Delete(_context.Log, _context.Token);
67+
68+
// Readme
69+
var readmeFile = _context.Source.Get(GetReadmePath(package));
70+
readmeFile.Delete(_context.Log, _context.Token);
6771
}
6872

6973
public Uri GetNupkgPath(PackageIdentity package)
@@ -92,6 +96,19 @@ public static Uri GetIconPath(SleetContext context, PackageIdentity package)
9296
return context.Source.GetPath($"/flatcontainer/{id}/{version}/icon".ToLowerInvariant());
9397
}
9498

99+
public Uri GetReadmePath(PackageIdentity package)
100+
{
101+
return GetReadmePath(_context, package);
102+
}
103+
104+
public static Uri GetReadmePath(SleetContext context, PackageIdentity package)
105+
{
106+
var id = package.Id;
107+
var version = package.Version.ToIdentityString();
108+
109+
return context.Source.GetPath($"/flatcontainer/{id}/{version}/readme".ToLowerInvariant());
110+
}
111+
95112
public Uri GetIndexUri(string id)
96113
{
97114
return _context.Source.GetPath($"/flatcontainer/{id}/index.json".ToLowerInvariant());
@@ -181,7 +198,8 @@ private Task AddPackageAsync(PackageInput packageInput)
181198
{
182199
AddNuspecAsync(packageInput),
183200
AddNupkgAsync(packageInput),
184-
AddIconAsync(packageInput)
201+
AddIconAsync(packageInput),
202+
AddReadmeAsync(packageInput)
185203
});
186204
}
187205

@@ -206,22 +224,40 @@ private async Task AddNuspecAsync(PackageInput packageInput)
206224
}
207225
}
208226

209-
private async Task AddIconAsync(PackageInput packageInput)
227+
private Task AddIconAsync(PackageInput packageInput)
210228
{
211229
// Find icon path in package from nuspec
212-
var iconPath = packageInput.Nuspec.GetIcon();
230+
var path = packageInput.Nuspec.GetIcon();
231+
var feedPath = GetIconPath(packageInput.Identity);
232+
233+
return AddFileAsync(packageInput, path, feedPath);
234+
}
235+
236+
private Task AddReadmeAsync(PackageInput packageInput)
237+
{
238+
// Find readme path in package from nuspec
239+
var path = packageInput.Nuspec.GetReadme();
240+
var feedPath = GetReadmePath(packageInput.Identity);
241+
242+
return AddFileAsync(packageInput, path, feedPath);
243+
}
213244

214-
if (!string.IsNullOrWhiteSpace(iconPath))
245+
private async Task AddFileAsync(PackageInput packageInput, string nupkgPath, Uri feedPath)
246+
{
247+
if (!string.IsNullOrWhiteSpace(nupkgPath))
215248
{
216-
iconPath = PathUtility.StripLeadingDirectorySeparators(iconPath).Trim();
249+
nupkgPath = PathUtility.StripLeadingDirectorySeparators(nupkgPath).Trim();
217250

218-
using (var zip = packageInput.CreateZip())
251+
if (!string.IsNullOrWhiteSpace(nupkgPath))
219252
{
220-
var entry = zip.GetEntry(iconPath);
221-
if (entry != null)
253+
using (var zip = packageInput.CreateZip())
222254
{
223-
var entryFile = _context.Source.Get(GetIconPath(packageInput.Identity));
224-
await entryFile.Write(entry.Open(), _context.Log, _context.Token);
255+
var entry = zip.GetEntry(nupkgPath);
256+
if (entry != null)
257+
{
258+
var entryFile = _context.Source.Get(feedPath);
259+
await entryFile.Write(entry.Open(), _context.Log, _context.Token);
260+
}
225261
}
226262
}
227263
}

test/Sleet.AmazonS3.Tests/BasicTests.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,36 @@ public async Task GivenAStorageAccountVerifyPushAndRemoveSucceed()
170170
{
171171
await testContext.InitAsync();
172172

173-
var testPackage = new TestNupkg("packageA", "1.0.0");
173+
var testPackage = new TestNupkg()
174+
{
175+
Nuspec = new TestNuspec()
176+
{
177+
Id = "packageA",
178+
Version = "1.0.0",
179+
Authors = "author",
180+
Description = "desc",
181+
IconUrl = "http://www.tempuri.org",
182+
Icon = "images/icon.png",
183+
Readme = "README.md",
184+
Language = "en-us",
185+
MinClientVersion = "1.0.0",
186+
Title = "title",
187+
Tags = "a b d",
188+
Summary = "summary",
189+
LicenseUrl = "http://www.tempuri.org/lic",
190+
ProjectUrl = "http://www.tempuri.org/proj",
191+
ReleaseNotes = "notes",
192+
Owners = "owners",
193+
Copyright = "copyright",
194+
RequireLicenseAcceptance = "true"
195+
},
196+
Files = new List<TestNupkgFile>()
197+
{
198+
new("README.md"),
199+
new("images/icon.png")
200+
}
201+
};
202+
174203
var zipFile = testPackage.Save(packagesFolder.Root);
175204

176205
var result = await InitCommand.RunAsync(testContext.LocalSettings,

test/Sleet.Azure.Tests/BasicTests.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,36 @@ public async Task GivenAStorageAccountVerifyPushAndRemoveSucceed()
137137
{
138138
await testContext.InitAsync();
139139

140-
var testPackage = new TestNupkg("packageA", "1.0.0");
140+
var testPackage = new TestNupkg()
141+
{
142+
Nuspec = new TestNuspec()
143+
{
144+
Id = "packageA",
145+
Version = "1.0.0",
146+
Authors = "author",
147+
Description = "desc",
148+
IconUrl = "http://www.tempuri.org",
149+
Icon = "images/icon.png",
150+
Readme = "README.md",
151+
Language = "en-us",
152+
MinClientVersion = "1.0.0",
153+
Title = "title",
154+
Tags = "a b d",
155+
Summary = "summary",
156+
LicenseUrl = "http://www.tempuri.org/lic",
157+
ProjectUrl = "http://www.tempuri.org/proj",
158+
ReleaseNotes = "notes",
159+
Owners = "owners",
160+
Copyright = "copyright",
161+
RequireLicenseAcceptance = "true"
162+
},
163+
Files = new List<TestNupkgFile>()
164+
{
165+
new("README.md"),
166+
new("images/icon.png")
167+
}
168+
};
169+
141170
var zipFile = testPackage.Save(packagesFolder.Root);
142171

143172
var result = await InitCommand.RunAsync(testContext.LocalSettings,

test/SleetLib.Tests/AddRemoveTests.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,77 @@ public async Task AddRemove_AddAndRemovePackageWithIcon()
389389
}
390390
}
391391

392+
[Fact]
393+
public async Task AddRemove_AddAndRemovePackageWithReadme()
394+
{
395+
// Arrange
396+
using (var packagesFolder = new TestFolder())
397+
using (var target = new TestFolder())
398+
using (var cache = new LocalCache())
399+
{
400+
var log = new TestLogger();
401+
var fileSystem = new PhysicalFileSystem(cache, UriUtility.CreateUri(target.Root));
402+
var settings = new LocalSettings();
403+
404+
var context = new SleetContext()
405+
{
406+
Token = CancellationToken.None,
407+
LocalSettings = settings,
408+
Log = log,
409+
Source = fileSystem,
410+
SourceSettings = new FeedSettings()
411+
{
412+
CatalogEnabled = true
413+
}
414+
};
415+
416+
var testPackage = new TestNupkg()
417+
{
418+
Nuspec = new TestNuspec()
419+
{
420+
Id = "packageA",
421+
Version = "1.0.0",
422+
Authors = "author",
423+
Description = "desc",
424+
IconUrl = "http://www.tempuri.org",
425+
Readme = "README.md",
426+
Language = "en-us",
427+
MinClientVersion = "1.0.0",
428+
Title = "title",
429+
Tags = "a b d",
430+
Summary = "summary",
431+
LicenseUrl = "http://www.tempuri.org/lic",
432+
ProjectUrl = "http://www.tempuri.org/proj",
433+
ReleaseNotes = "notes",
434+
Owners = "owners",
435+
Copyright = "copyright",
436+
RequireLicenseAcceptance = "true"
437+
},
438+
Files = new List<TestNupkgFile>()
439+
{
440+
new("README.md")
441+
}
442+
};
443+
444+
var zipFile = testPackage.Save(packagesFolder.Root);
445+
using (var zip = new ZipArchive(File.OpenRead(zipFile.FullName), ZipArchiveMode.Read, false))
446+
{
447+
var input = PackageInput.Create(zipFile.FullName);
448+
449+
await InitCommand.InitAsync(context);
450+
await PushCommand.RunAsync(context.LocalSettings, context.Source, new List<string>() { zipFile.FullName }, false, false, context.Log);
451+
452+
var file = fileSystem.Get(fileSystem.GetPath("flatcontainer/packagea/1.0.0/readme"));
453+
Assert.True(await file.Exists(context.Log, context.Token));
454+
455+
await DeleteCommand.RunAsync(context.LocalSettings, context.Source, "packageA", "1.0.0", string.Empty, false, context.Log);
456+
457+
file = fileSystem.Get(fileSystem.GetPath("flatcontainer/packagea/1.0.0/readme"));
458+
Assert.False(await file.Exists(context.Log, context.Token));
459+
}
460+
}
461+
}
462+
392463
// Verify DeletePackagesAsync method
393464
[Fact]
394465
public async Task AddRemove_AddAndDeletePackagesAsync()

0 commit comments

Comments
 (0)