Skip to content

Commit

Permalink
Merge pull request #3758 from NuGet/feature/semver2
Browse files Browse the repository at this point in the history
Phase 2 of SemVer2 support
  • Loading branch information
xavierdecoster authored Jun 1, 2017
2 parents 65bfb66 + b0edded commit 730f54e
Show file tree
Hide file tree
Showing 87 changed files with 3,065 additions and 755 deletions.
8 changes: 7 additions & 1 deletion src/NuGet.Services.Search.Client/Client/SearchClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,20 @@ public async Task<ServiceResponse<SearchResults>> Search(
bool countOnly = false,
bool explain = false,
bool getAllVersions = false,
string supportedFramework = null)
string supportedFramework = null,
string semVerLevel = null)
{
IDictionary<string, string> nameValue = new Dictionary<string, string>();
nameValue.Add("q", query);
nameValue.Add("skip", skip.ToString());
nameValue.Add("take", take.ToString());
nameValue.Add("sortBy", SortNames[sortBy]);

if (!String.IsNullOrEmpty(semVerLevel))
{
nameValue.Add("semVerLevel", semVerLevel);
}

if (!String.IsNullOrEmpty(supportedFramework))
{
nameValue.Add("supportedFramework", supportedFramework);
Expand Down
2 changes: 1 addition & 1 deletion src/NuGetGallery.Core/Auditing/PackageAuditRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public PackageAuditRecord(Package package, AuditedPackageAction action)

public override string GetPath()
{
return $"{Id}/{NuGetVersionNormalizer.Normalize(Version)}"
return $"{Id}/{NuGetVersionFormatter.Normalize(Version)}"
.ToLowerInvariant();
}
}
Expand Down
18 changes: 9 additions & 9 deletions src/NuGetGallery.Core/CoreStrings.Designer.cs

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

6 changes: 3 additions & 3 deletions src/NuGetGallery.Core/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,6 @@
<data name="Manifest_TargetFrameworkNotSupported" xml:space="preserve">
<value>The target framework {0} is not supported.</value>
</data>
<data name="Manifest_InvalidVersionSemVer200" xml:space="preserve">
<value>The version '{0}' is not supported. The NuGet Gallery currently does not currently support Semantic Version 2.0 as it would break older NuGet clients.</value>
</data>
<data name="Http404NotFound" xml:space="preserve">
<value>(404) Error - Not Found</value>
</data>
Expand All @@ -180,4 +177,7 @@
<data name="NegativeIndexesAreInvalid" xml:space="preserve">
<value>Negative indexes are invalid.</value>
</data>
<data name="Manifest_InvalidDependencyVersion" xml:space="preserve">
<value>The package manifest contains an invalid Dependency Version: '{0}'</value>
</data>
</root>
3 changes: 3 additions & 0 deletions src/NuGetGallery.Core/Entities/Package.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ public Package()
public bool IsLatest { get; set; }
public bool IsLatestStable { get; set; }

public bool IsLatestSemVer2 { get; set; }
public bool IsLatestStableSemVer2 { get; set; }

/// <summary>
/// This is when the Package Entity was last touched (so caches can notice changes). In UTC.
/// </summary>
Expand Down
10 changes: 5 additions & 5 deletions src/NuGetGallery.Core/NuGetGallery.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,19 @@
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NuGet.Common, Version=4.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\NuGet.Common.4.3.0-preview1-2507\lib\net45\NuGet.Common.dll</HintPath>
<HintPath>..\..\packages\NuGet.Common.4.3.0-preview1-2524\lib\net45\NuGet.Common.dll</HintPath>
</Reference>
<Reference Include="NuGet.Frameworks, Version=4.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\NuGet.Frameworks.4.3.0-preview1-2507\lib\net45\NuGet.Frameworks.dll</HintPath>
<HintPath>..\..\packages\NuGet.Frameworks.4.3.0-preview1-2524\lib\net45\NuGet.Frameworks.dll</HintPath>
</Reference>
<Reference Include="NuGet.Packaging, Version=4.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\NuGet.Packaging.4.3.0-preview1-2507\lib\net45\NuGet.Packaging.dll</HintPath>
<HintPath>..\..\packages\NuGet.Packaging.4.3.0-preview1-2524\lib\net45\NuGet.Packaging.dll</HintPath>
</Reference>
<Reference Include="NuGet.Packaging.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\NuGet.Packaging.Core.4.3.0-preview1-2507\lib\net45\NuGet.Packaging.Core.dll</HintPath>
<HintPath>..\..\packages\NuGet.Packaging.Core.4.3.0-preview1-2524\lib\net45\NuGet.Packaging.Core.dll</HintPath>
</Reference>
<Reference Include="NuGet.Versioning, Version=4.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\NuGet.Versioning.4.3.0-preview1-2507\lib\net45\NuGet.Versioning.dll</HintPath>
<HintPath>..\..\packages\NuGet.Versioning.4.3.0-preview1-2524\lib\net45\NuGet.Versioning.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
Expand Down
26 changes: 22 additions & 4 deletions src/NuGetGallery.Core/NuGetVersionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace NuGetGallery
{
public static class NuGetVersionNormalizer
public static class NuGetVersionFormatter
{
public static string Normalize(string version)
{
Expand All @@ -19,16 +19,34 @@ public static string Normalize(string version)

return parsed.ToNormalizedString();
}

public static string ToFullStringOrFallback(string version, string fallback = "")
{
NuGetVersion nugetVersion;
if (NuGetVersion.TryParse(version, out nugetVersion))
{
return nugetVersion.ToFullString();
}
else
{
return fallback;
}
}
}

public static class NuGetVersionExtensions
{
private const RegexOptions Flags = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
private static readonly Regex SemanticVersionRegex = new Regex(@"^(?<Version>\d+(\s*\.\s*\d+){0,3})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
private const RegexOptions SemanticVersionRegexFlags = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
private static readonly Regex SemanticVersionRegex = new Regex(@"^(?<Version>\d+(\s*\.\s*\d+){0,3})(?<Release>-[a-z][0-9a-z-]*)?$", SemanticVersionRegexFlags);

public static string ToNormalizedStringSafe(this NuGetVersion self)
{
return self != null ? self.ToNormalizedString() : String.Empty;
return self != null ? self.ToNormalizedString() : string.Empty;
}

public static string ToFullStringSafe(this NuGetVersion self)
{
return self != null ? self.ToFullString() : string.Empty;
}

public static bool IsValidVersionForLegacyClients(this NuGetVersion self)
Expand Down
43 changes: 29 additions & 14 deletions src/NuGetGallery.Core/Packaging/ManifestValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static IEnumerable<ValidationResult> Validate(Stream nuspecStream, out Nu
catch (Exception ex)
{
nuspecReader = null;
return new [] { new ValidationResult(ex.Message) };
return new[] { new ValidationResult(ex.Message) };
}

return Enumerable.Empty<ValidationResult>();
Expand Down Expand Up @@ -59,7 +59,7 @@ private static IEnumerable<ValidationResult> ValidateCore(PackageMetadata packag
// Check and validate URL properties
foreach (var result in CheckUrls(
packageMetadata.GetValueFromMetadata("IconUrl"),
packageMetadata.GetValueFromMetadata("ProjectUrl"),
packageMetadata.GetValueFromMetadata("ProjectUrl"),
packageMetadata.GetValueFromMetadata("LicenseUrl")))
{
yield return result;
Expand All @@ -76,7 +76,7 @@ private static IEnumerable<ValidationResult> ValidateCore(PackageMetadata packag
version));
}

var versionValidationResult = ValidateVersion(packageMetadata.Version);
var versionValidationResult = ValidateVersionForLegacyClients(packageMetadata.Version);
if (versionValidationResult != null)
{
yield return versionValidationResult;
Expand Down Expand Up @@ -143,17 +143,19 @@ private static IEnumerable<ValidationResult> ValidateCore(PackageMetadata packag
// Versions
if (dependency.VersionRange.MinVersion != null)
{
var versionRangeValidationResult = ValidateVersion(dependency.VersionRange.MinVersion);
var versionRangeValidationResult =
ValidateDependencyVersion(dependency.VersionRange.MinVersion);
if (versionRangeValidationResult != null)
{
yield return versionRangeValidationResult;
}
}

if (dependency.VersionRange.MaxVersion != null
if (dependency.VersionRange.MaxVersion != null
&& dependency.VersionRange.MaxVersion != dependency.VersionRange.MinVersion)
{
var versionRangeValidationResult = ValidateVersion(dependency.VersionRange.MaxVersion);
var versionRangeValidationResult =
ValidateDependencyVersion(dependency.VersionRange.MaxVersion);
if (versionRangeValidationResult != null)
{
yield return versionRangeValidationResult;
Expand All @@ -164,24 +166,37 @@ private static IEnumerable<ValidationResult> ValidateCore(PackageMetadata packag
}
}

private static ValidationResult ValidateVersion(NuGetVersion version)
/// <summary>
/// Checks whether the provided version is consumable by legacy 2.x clients,
/// which do not support a `.` in release labels, or release labels starting with numeric characters.
/// See also https://github.com/NuGet/NuGetGallery/issues/3226.
/// </summary>
/// <param name="version">The <see cref="NuGetVersion"/> to check for 2.x client compatibility.</param>
/// <returns>Returns a <see cref="ValidationResult"/> when non-compliant; otherwise <c>null</c>.</returns>
private static ValidationResult ValidateVersionForLegacyClients(NuGetVersion version)
{
if (version.IsSemVer2)
if (!version.IsSemVer2 && !version.IsValidVersionForLegacyClients())
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
CoreStrings.Manifest_InvalidVersionSemVer200,
version.ToFullString()));
CoreStrings.Manifest_InvalidVersion,
version));
}
else if (!version.IsValidVersionForLegacyClients())

return null;
}

private static ValidationResult ValidateDependencyVersion(NuGetVersion version)
{
if (version.HasMetadata)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
CoreStrings.Manifest_InvalidVersion,
version));
CoreStrings.Manifest_InvalidDependencyVersion,
version.ToFullString()));
}

return null;
return ValidateVersionForLegacyClients(version);
}

private static IEnumerable<ValidationResult> CheckUrls(params string[] urls)
Expand Down
65 changes: 59 additions & 6 deletions src/NuGetGallery.Core/SemVerLevelKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using NuGet.Versioning;

namespace NuGetGallery
Expand All @@ -15,6 +16,9 @@ namespace NuGetGallery
/// </summary>
public static class SemVerLevelKey
{
public static readonly string SemVerLevel2 = "2.0.0";
private static readonly NuGetVersion _semVer2Version = NuGetVersion.Parse(SemVerLevel2);

/// <summary>
/// This could either indicate being SemVer1-compliant, or non-SemVer-compliant at all (e.g. System.Versioning pattern).
/// </summary>
Expand All @@ -31,7 +35,7 @@ public static class SemVerLevelKey
/// </summary>
/// <param name="originalVersion">The package's non-normalized, original version string.</param>
/// <param name="dependencies">The package's direct dependencies as defined in the package's manifest.</param>
/// <returns>Returns <c>null</c> when unknown; otherwise the identified SemVer-level.</returns>
/// <returns>Returns <c>null</c> when unknown; otherwise the identified SemVer-level key.</returns>
public static int? ForPackage(NuGetVersion originalVersion, IEnumerable<PackageDependency> dependencies)
{
if (originalVersion == null)
Expand All @@ -53,17 +57,66 @@ public static class SemVerLevelKey
// Check the package dependencies for SemVer-compliance.
// As soon as a SemVer2-compliant dependency version is found that is not SemVer1-compliant,
// this package in itself is to be identified as to have SemVerLevelKey.SemVer2.
var dependencyVersionRange = VersionRange.Parse(dependency.VersionSpec);

if ((dependencyVersionRange.MinVersion != null && dependencyVersionRange.MinVersion.IsSemVer2)
|| (dependencyVersionRange.MaxVersion != null && dependencyVersionRange.MaxVersion.IsSemVer2))
VersionRange dependencyVersionRange;
if (dependency.VersionSpec != null && VersionRange.TryParse(dependency.VersionSpec, out dependencyVersionRange))
{
return SemVer2;
if ((dependencyVersionRange.MinVersion != null && dependencyVersionRange.MinVersion.IsSemVer2)
|| (dependencyVersionRange.MaxVersion != null && dependencyVersionRange.MaxVersion.IsSemVer2))
{
return SemVer2;
}
}
}
}

return Unknown;
}

/// <summary>
/// Identifies the SemVer-level for a given semVerLevel version string.
/// </summary>
/// <param name="semVerLevel">The version string indicating the supported SemVer-level.</param>
/// <returns>
/// Returns <c>null</c> when unknown; otherwise the identified SemVer-level key.
/// </returns>
/// <remarks>
/// Older clients don't send the semVerLevel query parameter at all,
/// so we default to Unknown for backwards-compatibility.
/// </remarks>
public static int? ForSemVerLevel(string semVerLevel)
{
if (semVerLevel == null)
{
return Unknown;
}

NuGetVersion parsedVersion;
if (NuGetVersion.TryParse(semVerLevel, out parsedVersion))
{
return _semVer2Version <= parsedVersion ? SemVer2 : Unknown;
}
else
{
return Unknown;
}
}

/// <summary>
/// Indicates whether the provided SemVer-level key is compliant with the provided SemVer-level version string.
/// </summary>
/// <param name="semVerLevel">The SemVer-level string indicating the SemVer-level to comply with.</param>
/// <returns><c>True</c> if compliant; otherwise <c>false</c>.</returns>
public static Expression<Func<Package, bool>> IsPackageCompliantWithSemVerLevel(string semVerLevel)
{
// Note: we must return an expression that Linq to Entities is able to translate to SQL
var parsedSemVerLevelKey = ForSemVerLevel(semVerLevel);

if (parsedSemVerLevelKey == SemVer2)
{
return p => p.SemVerLevelKey == Unknown || p.SemVerLevelKey == parsedSemVerLevelKey;
}

return p => p.SemVerLevelKey == Unknown;
}
}
}
10 changes: 5 additions & 5 deletions src/NuGetGallery.Core/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
<package id="Microsoft.Web.Xdt" version="2.1.1" targetFramework="net452" />
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.1.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="NuGet.Common" version="4.3.0-preview1-2507" targetFramework="net452" />
<package id="NuGet.Frameworks" version="4.3.0-preview1-2507" targetFramework="net452" />
<package id="NuGet.Packaging" version="4.3.0-preview1-2507" targetFramework="net452" />
<package id="NuGet.Packaging.Core" version="4.3.0-preview1-2507" targetFramework="net452" />
<package id="NuGet.Versioning" version="4.3.0-preview1-2507" targetFramework="net452" />
<package id="NuGet.Common" version="4.3.0-preview1-2524" targetFramework="net452" />
<package id="NuGet.Frameworks" version="4.3.0-preview1-2524" targetFramework="net452" />
<package id="NuGet.Packaging" version="4.3.0-preview1-2524" targetFramework="net452" />
<package id="NuGet.Packaging.Core" version="4.3.0-preview1-2524" targetFramework="net452" />
<package id="NuGet.Versioning" version="4.3.0-preview1-2524" targetFramework="net452" />
<package id="System.Spatial" version="5.6.5-beta" targetFramework="net452" />
<package id="WindowsAzure.Storage" version="7.0.0" targetFramework="net452" />
</packages>
2 changes: 2 additions & 0 deletions src/NuGetGallery/App_Start/NuGetODataV2FeedConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static IEdmModel GetEdmModel()
searchAction.Parameter<string>("searchTerm");
searchAction.Parameter<string>("targetFramework");
searchAction.Parameter<bool>("includePrerelease");
searchAction.Parameter<string>("semVerLevel");
searchAction.ReturnsCollectionFromEntitySet<V2FeedPackage>("Packages");

var findPackagesAction = builder.Action("FindPackagesById");
Expand All @@ -73,6 +74,7 @@ public static IEdmModel GetEdmModel()
getUpdatesAction.Parameter<bool>("includeAllVersions");
getUpdatesAction.Parameter<string>("targetFrameworks");
getUpdatesAction.Parameter<string>("versionConstraints");
getUpdatesAction.Parameter<string>("semVerLevel");
getUpdatesAction.ReturnsCollectionFromEntitySet<V2FeedPackage>("Packages");

var model = builder.GetEdmModel();
Expand Down
Loading

0 comments on commit 730f54e

Please sign in to comment.