Skip to content

Commit

Permalink
Limit NuGet description length (#80)
Browse files Browse the repository at this point in the history
  • Loading branch information
rasmus authored Feb 24, 2022
1 parent 1b26e06 commit d12cf38
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pull-requests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
run: dotnet publish -o bake-it Source/Bake

- name: Run Bake
run: bake-it/bake run --build-version 0.11.$GITHUB_RUN_NUMBER
run: bake-it/bake run --build-version 0.12.$GITHUB_RUN_NUMBER

- name: Upload test results
uses: actions/upload-artifact@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
run: dotnet publish -o bake-it Source/Bake

- name: Run Bake
run: bake-it/bake run --convention=Release --build-version 0.11.$GITHUB_RUN_NUMBER --destination="nuget>github,nuget,release>github"
run: bake-it/bake run --convention=Release --build-version 0.12.$GITHUB_RUN_NUMBER --destination="nuget>github,nuget,release>github"

- name: Upload test results
uses: actions/upload-artifact@v2
Expand Down
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.12-beta

* Fixed: Not limits description of NuGet packages to 4.000 characters

# 0.11-beta

* New: Use content of `README.md` files as DLL and NuGet descriptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public DotNetComposerServiceTests() : base("NetCore.Console")
}

[Test]
public async Task T()
public async Task TestIt()
{
// Arrange
var ingredients = Ingredients.New(
Expand All @@ -63,6 +63,7 @@ protected override IServiceCollection Configure(IServiceCollection serviceCollec
.AddTransient<IFileSystem, FileSystem>()
.AddTransient<ICredentials, Credentials>()
.AddTransient<IDefaults, Defaults>()
.AddTransient<IDescriptionLimiter, DescriptionLimiter>()
.AddSingleton(TestEnvironmentVariables.None)
.AddTransient<IConventionInterpreter, ConventionInterpreter>()
.AddTransient<IDotNetTfmParser, DotNetTfmParser>()
Expand Down
117 changes: 117 additions & 0 deletions Source/Bake.Tests/UnitTests/Services/DescriptionLimiterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// MIT License
//
// Copyright (c) 2021-2022 Rasmus Mikkelsen
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using System;
using Bake.Services;
using Bake.Tests.Helpers;
using FluentAssertions;
using NUnit.Framework;

// ReSharper disable StringLiteralTypo

namespace Bake.Tests.UnitTests.Services
{
public class DescriptionLimiterTests : TestFor<DescriptionLimiter>
{
[Test]
public void LessThanMaxIsAccepted()
{
// Arrange
var text = A<string>();

// Act
var output = Sut.Limit(text, int.MaxValue);

// Assert
output.Should().Be(text);
}

[Test]
public void CutsAtHeadlineSimple()
{
// Arrange
var text = Concat(
"#",
new string('x', 5),
"#",
new string('y', 10));

// Act
var output = Sut.Limit(text, 15);

// Assert
AssertEquals(
output,
"#",
new string('x', 5));
}

[Test]
public void CutsAtHeadline()
{
// Arrange
var text = Concat(
"#",
new string('x', 10),
"#",
new string('y', 10),
"#",
new string('z', 10),
"#",
new string('i', 10));

// Act
var output = Sut.Limit(text, 36);

// Assert
AssertEquals(
output,
"#",
new string('x', 10),
"#",
new string('y', 10));
}

[Test]
public void Fallback()
{
// Arrange
var text = new string('x', 42);

// Act
var output = Sut.Limit(text, 40);

// Assert
output.Should().Be(new string('x', 40));
}

private static string Concat(params string[] texts) => string.Join(Environment.NewLine, texts);

private static void AssertEquals(
string input,
params string[] expected)
{
var inputLines = input.Split(new[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
inputLines.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering());
}
}
}
12 changes: 8 additions & 4 deletions Source/Bake/Cooking/Composers/DotNetComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ namespace Bake.Cooking.Composers
{
public class DotNetComposer : Composer
{
public const int MaximumNuGetDescription = 4000;
private static readonly IReadOnlyDictionary<string, string> DefaultProperties = new Dictionary<string, string>
{
// We really don't want builds to fail due to old versions
Expand Down Expand Up @@ -70,21 +71,24 @@ public class DotNetComposer : Composer
private readonly IConventionInterpreter _conventionInterpreter;
private readonly IDefaults _defaults;
private readonly IDockerLabels _dockerLabels;
private readonly IDescriptionLimiter _descriptionLimiter;

public DotNetComposer(
ILogger<DotNetComposer> logger,
IFileSystem fileSystem,
ICsProjParser csProjParser,
IConventionInterpreter conventionInterpreter,
IDefaults defaults,
IDockerLabels dockerLabels)
IDockerLabels dockerLabels,
IDescriptionLimiter descriptionLimiter)
{
_logger = logger;
_fileSystem = fileSystem;
_csProjParser = csProjParser;
_conventionInterpreter = conventionInterpreter;
_defaults = defaults;
_dockerLabels = dockerLabels;
_descriptionLimiter = descriptionLimiter;
}

public override async Task<IReadOnlyCollection<Recipe>> ComposeAsync(
Expand Down Expand Up @@ -312,7 +316,7 @@ private static Recipe CreateBuildRecipe(
properties);
}

private static Dictionary<string, string> CreateProperties(
private Dictionary<string, string> CreateProperties(
ValueObjects.Ingredients ingredients,
VisualStudioSolution visualStudioSolution)
{
Expand Down Expand Up @@ -348,13 +352,13 @@ private static Dictionary<string, string> CreateProperties(
return properties;
}

private static string BuildDescription(
private string BuildDescription(
VisualStudioSolution visualStudioSolution,
ValueObjects.Ingredients ingredients)
{
if (ingredients.Description != null)
{
return ingredients.Description.Text;
return _descriptionLimiter.Limit(ingredients.Description.Text, MaximumNuGetDescription);
}

var elements = new Dictionary<string, string>
Expand Down
1 change: 1 addition & 0 deletions Source/Bake/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public static IServiceCollection AddBake(
.AddTransient<IMkDocs, MkDocs>()
.AddTransient<IComposerOrdering, ComposerOrdering>()
.AddTransient<IHelm, Helm>()
.AddTransient<IDescriptionLimiter, DescriptionLimiter>()

// Gathers
.AddTransient<IGather, GitGather>()
Expand Down
97 changes: 97 additions & 0 deletions Source/Bake/Services/DescriptionLimiter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// MIT License
//
// Copyright (c) 2021-2022 Rasmus Mikkelsen
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;

namespace Bake.Services
{
public class DescriptionLimiter : IDescriptionLimiter
{
private readonly ILogger<DescriptionLimiter> _logger;

public DescriptionLimiter(
ILogger<DescriptionLimiter> logger)
{
_logger = logger;
}

public string Limit(string markdown, int maxLength)
{
if (string.IsNullOrEmpty(markdown) ||
markdown.Length <= maxLength)
{
return markdown ?? string.Empty;
}

var lines = markdown
.Split('\n', '\r');
var totalHeadlines = lines.Count(l => l.StartsWith("#"));

for (var i = totalHeadlines; 0 <= i; i--)
{
var text = string.Join(Environment.NewLine, TakeHeadlines(lines, i));
if (string.IsNullOrWhiteSpace(text))
{
break;
}
if (text.Length <= maxLength)
{
_logger.LogWarning(
"Description text with its length of {CurrentLength} is too long compared to the maximum {MaximumLength}, cutting it at closest header to {NewLength}",
markdown.Length,
maxLength,
text.Length);
return text;
}
}

_logger.LogWarning(
"Description text with its length of {CurrentLength} is too long compared to the maximum {MaximumLength}, but cannot find a good markdown header to cut at so cutting it in the middle of text at {NewLength}",
markdown.Length,
maxLength,
maxLength);

return markdown[..maxLength];
}

private static IEnumerable<string> TakeHeadlines(IEnumerable<string> lines, int headlines)
{
var currentHeadline = 0;
foreach (var line in lines)
{
if (line.StartsWith("#"))
{
currentHeadline++;
}
if (headlines < currentHeadline)
{
yield break;
}

yield return line;
}
}
}
}
29 changes: 29 additions & 0 deletions Source/Bake/Services/IDescriptionLimiter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// MIT License
//
// Copyright (c) 2021-2022 Rasmus Mikkelsen
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

namespace Bake.Services
{
public interface IDescriptionLimiter
{
string Limit(string markdown, int maxLength);
}
}

0 comments on commit d12cf38

Please sign in to comment.