diff --git a/src/Paket.Core/Dependencies/DependenciesFileParser.fs b/src/Paket.Core/Dependencies/DependenciesFileParser.fs index d48395dd8d..40810fc040 100644 --- a/src/Paket.Core/Dependencies/DependenciesFileParser.fs +++ b/src/Paket.Core/Dependencies/DependenciesFileParser.fs @@ -326,6 +326,8 @@ module DependenciesFileParser = let setting = match trimmed.Replace(":","").Trim() with | String.EqualsIC "max" -> Some ResolverStrategy.Max + | String.EqualsIC "latest-patch" -> Some ResolverStrategy.LatestPatch + | String.EqualsIC "latest-minor" -> Some ResolverStrategy.LatestMinor | String.EqualsIC "min" -> Some ResolverStrategy.Min | _ -> None diff --git a/src/Paket.Core/Dependencies/PackageResolver.fs b/src/Paket.Core/Dependencies/PackageResolver.fs index 46612d9b40..ee99a5b63f 100644 --- a/src/Paket.Core/Dependencies/PackageResolver.fs +++ b/src/Paket.Core/Dependencies/PackageResolver.fs @@ -44,10 +44,11 @@ type GetPackageDetailsParameters = GetPackageDetailsParameters.ofParamsEx false sources groupName packageName version type GetPackageVersionsParameters = - { Package : SourcePackageInfo } - static member ofParams sources groupName packageName = + { Package : SourcePackageInfo + MinVersion : SemVerInfo option } + static member ofParams sources groupName packageName minVersion = SourcePackageInfo.ofParams sources groupName packageName - |> fun p -> { Package = p } + |> fun p -> { Package = p; MinVersion = minVersion } type PackageDetailsFunc = GetPackageDetailsParameters -> Async type PackageDetailsSyncFunc = GetPackageDetailsParameters -> PackageDetails @@ -585,7 +586,8 @@ let private getCompatibleVersions let resolverStrategy = getResolverStrategy globalStrategyForDirectDependencies globalStrategyForTransitives rootDependencies allRequirementsOfCurrentPackage currentRequirement let allVersions = - getVersionsF resolverStrategy (GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName currentRequirement.Name) + let versionParams = GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName currentRequirement.Name currentRequirement.MinVersionSetting + getVersionsF resolverStrategy versionParams |> Seq.map (fun (v, sources) -> VersionCache.ofParams v sources false) match currentRequirement.VersionRequirement.Range with @@ -1079,6 +1081,26 @@ let Resolve (getVersionsRaw : PackageVersionsFunc, getPreferredVersionsRaw : Pre let sorted = match resolverStrategy with | ResolverStrategy.Max -> List.sortDescending versions + | ResolverStrategy.LatestPatch -> + match versionParams.MinVersion with + | Some minVersion -> + let preferred = + versions + |> List.filter (fun (v,s) -> v.Major = minVersion.Major && v.Minor = minVersion.Minor) + |> List.sortDescending + + preferred @ List.sortDescending versions + | None -> List.sortDescending versions + | ResolverStrategy.LatestMinor -> + match versionParams.MinVersion with + | Some minVersion -> + let preferred = + versions + |> List.filter (fun (v,s) -> v.Major = minVersion.Major) + |> List.sortDescending + + preferred @ List.sortDescending versions + | None -> List.sortDescending versions | ResolverStrategy.Min -> List.sort versions yield! sorted } @@ -1174,7 +1196,8 @@ let Resolve (getVersionsRaw : PackageVersionsFunc, getPreferredVersionsRaw : Pre let currentConflict = let getVersionsF packName = - getVersionsBlock ResolverStrategy.Max (GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName packName) currentStep + let versionParameters = GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName packName currentRequirement.MinVersionSetting + getVersionsBlock ResolverStrategy.Max versionParameters currentStep if Seq.isEmpty conflicts then { currentConflict with @@ -1296,7 +1319,8 @@ let Resolve (getVersionsRaw : PackageVersionsFunc, getPreferredVersionsRaw : Pre if not alreadyExplored then for (pack,verReq,restr) in exploredPackage.Dependencies do async { - let requestVersions = startRequestGetVersions (GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName pack) + let versionParameters = GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName pack currentRequirement.MinVersionSetting + let requestVersions = startRequestGetVersions versionParameters requestVersions.Work.TryReprioritize true WorkPriority.LikelyRequired let! versions = (requestVersions).Work.Task |> Async.AwaitTask // Preload the first version in range of this requirement @@ -1369,7 +1393,8 @@ let Resolve (getVersionsRaw : PackageVersionsFunc, getPreferredVersionsRaw : Pre step (Step((currentConflict,nextStep,currentRequirement), (currentConflict,currentStep,currentRequirement,compatibleVersions,flags)::priorConflictSteps)) stackpack currentConflict.VersionsToExplore flags else let getVersionsF packName = - getVersionsBlock ResolverStrategy.Max (GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName packName) currentStep + let versionParameters = GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName packName currentRequirement.MinVersionSetting + getVersionsBlock ResolverStrategy.Max versionParameters currentStep let conflictingPackageName,vr = match Seq.tryHead conflictingResolvedPackages with @@ -1399,13 +1424,17 @@ let Resolve (getVersionsRaw : PackageVersionsFunc, getPreferredVersionsRaw : Pre } for openReq in startingStep.OpenRequirements do - startRequestGetVersions (GetPackageVersionsParameters.ofParams openReq.Sources groupName openReq.Name) + let versionParamters = GetPackageVersionsParameters.ofParams openReq.Sources groupName openReq.Name openReq.MinVersionSetting + startRequestGetVersions versionParamters |> ignore let currentRequirement = getCurrentRequirement packageFilter startingStep.OpenRequirements (Dictionary()) let status = - let getVersionsF packName = getVersionsBlock ResolverStrategy.Max (GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName packName) startingStep + let getVersionsF packName = + let versionParameters = GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName packName currentRequirement.MinVersionSetting + getVersionsBlock ResolverStrategy.Max versionParameters startingStep + ResolutionRaw.ConflictRaw { ResolveStep = startingStep; RequirementSet = Set.empty; Requirement = currentRequirement; GetPackageVersions = getVersionsF } diff --git a/src/Paket.Core/PaketConfigFiles/DependenciesFile.fs b/src/Paket.Core/PaketConfigFiles/DependenciesFile.fs index fb240f6219..55ff667c62 100644 --- a/src/Paket.Core/PaketConfigFiles/DependenciesFile.fs +++ b/src/Paket.Core/PaketConfigFiles/DependenciesFile.fs @@ -454,7 +454,12 @@ type DependenciesFile(fileName,groups:Map, textRepr match strategy with | None -> this | Some strategy -> - let strategyString = sprintf "strategy: %s" (match strategy with ResolverStrategy.Max -> "max" | ResolverStrategy.Min -> "min") + let strategyString = + match strategy with + | ResolverStrategy.Max -> "strategy: max" + | ResolverStrategy.LatestPatch -> "strategy: latest-patch" + | ResolverStrategy.LatestMinor -> "strategy: latest-minor" + | ResolverStrategy.Min -> "strategy: min" let list = new System.Collections.Generic.List<_>() list.AddRange textRepresentation @@ -478,7 +483,11 @@ type DependenciesFile(fileName,groups:Map, textRepr match strategy with | None -> this | Some strategy -> - let strategyString = sprintf "lowest_matching: %s" (match strategy with ResolverStrategy.Max -> "false" | ResolverStrategy.Min -> "true") + let strategyString = + match strategy with + | ResolverStrategy.Max -> "lowest_matching: max" + | ResolverStrategy.Min -> "lowest_matching: min" + | _ -> failwithf "Strategy %O is invalid for lowest_matching setting." strategy let list = new System.Collections.Generic.List<_>() list.AddRange textRepresentation diff --git a/src/Paket.Core/PaketConfigFiles/LockFile.fs b/src/Paket.Core/PaketConfigFiles/LockFile.fs index 493befa089..a9ec5e9c6b 100644 --- a/src/Paket.Core/PaketConfigFiles/LockFile.fs +++ b/src/Paket.Core/PaketConfigFiles/LockFile.fs @@ -53,11 +53,14 @@ module LockFileSerializer = | None -> () match options.ResolverStrategyForTransitives with | Some ResolverStrategy.Min -> yield "STRATEGY: MIN" + | Some ResolverStrategy.LatestPatch -> yield "STRATEGY: LATEST-PATCH" + | Some ResolverStrategy.LatestMinor -> yield "STRATEGY: LATEST-MINOR" | Some ResolverStrategy.Max -> yield "STRATEGY: MAX" | None -> () match options.ResolverStrategyForDirectDependencies with | Some ResolverStrategy.Min -> yield "LOWEST_MATCHING: TRUE" | Some ResolverStrategy.Max -> yield "LOWEST_MATCHING: FALSE" + | Some x -> failwithf "Strategy %O is invalid as lowest_matching setting." x | None -> () match options.Settings.CopyLocal with | Some x -> yield "COPY-LOCAL: " + x.ToString().ToUpper() @@ -346,6 +349,8 @@ module LockFileParser = let setting = match trimmed.Trim() with | String.EqualsIC "min" -> Some ResolverStrategy.Min + | String.EqualsIC "latest-patch" -> Some ResolverStrategy.LatestPatch + | String.EqualsIC "latest-minor" -> Some ResolverStrategy.LatestMinor | String.EqualsIC "max" -> Some ResolverStrategy.Max | _ -> None diff --git a/src/Paket.Core/PublicAPI.fs b/src/Paket.Core/PublicAPI.fs index f465b645ac..5b63e80439 100644 --- a/src/Paket.Core/PublicAPI.fs +++ b/src/Paket.Core/PublicAPI.fs @@ -720,7 +720,8 @@ type Dependencies(dependenciesFileName: string) = |> List.distinct let versions = - NuGet.GetVersions true alternativeProjectRoot root (GetPackageVersionsParameters.ofParams sources (GroupName "") (PackageName name)) + let versionParameters = GetPackageVersionsParameters.ofParams sources (GroupName "") (PackageName name) None + NuGet.GetVersions true alternativeProjectRoot root versionParameters |> Async.RunSynchronously |> List.map (fun (v,_) -> v.ToString()) |> List.toArray diff --git a/src/Paket.Core/Versioning/Requirements.fs b/src/Paket.Core/Versioning/Requirements.fs index 16b4bad9f8..9d7833eac5 100644 --- a/src/Paket.Core/Versioning/Requirements.fs +++ b/src/Paket.Core/Versioning/Requirements.fs @@ -1220,6 +1220,18 @@ type PackageRequirement = member this.Depth = this.Graph.Count + member this.MinVersionSetting = + match this.VersionRequirement with + | VersionRequirement(v,_) -> + match v with + | Minimum v -> Some v + | GreaterThan v -> Some v + | Maximum _ -> None + | LessThan _ -> None + | Specific v -> Some v + | OverrideAll _ -> None + | Range(_,v,_,_) -> Some v + member this.SettingString = (sprintf "%O %s %s %O %s" this.VersionRequirement (if this.ResolverStrategyForTransitives.IsSome then (sprintf "%O" this.ResolverStrategyForTransitives) else "") (if this.ResolverStrategyForDirectDependencies.IsSome then (sprintf "%O" this.ResolverStrategyForDirectDependencies) else "") this.Settings (if this.TransitivePrereleases then "TransitivePrereleases-true" else "")) member this.HasPackageSettings = String.IsNullOrWhiteSpace this.SettingString |> not diff --git a/src/Paket.Core/Versioning/VersionRange.fs b/src/Paket.Core/Versioning/VersionRange.fs index ad65148ae7..852dd6254f 100644 --- a/src/Paket.Core/Versioning/VersionRange.fs +++ b/src/Paket.Core/Versioning/VersionRange.fs @@ -389,4 +389,6 @@ type VersionRequirement = [] type ResolverStrategy = | Max -| Min +| LatestPatch +| LatestMinor +| Min \ No newline at end of file diff --git a/tests/Paket.Tests/DependenciesFile/ParserSpecs.fs b/tests/Paket.Tests/DependenciesFile/ParserSpecs.fs index 717464c5bf..35a90278c7 100644 --- a/tests/Paket.Tests/DependenciesFile/ParserSpecs.fs +++ b/tests/Paket.Tests/DependenciesFile/ParserSpecs.fs @@ -1086,6 +1086,15 @@ let ``should read config with min and max strategy``() = cfg.Groups.[GroupName "Test"].Options.ResolverStrategyForTransitives |> shouldEqual (Some ResolverStrategy.Max) cfg.Groups.[Constants.MainDependencyGroup].Sources |> shouldEqual [PackageSource.NuGetV2Source "http://www.nuget.org/api/v2"] + + +[] +let ``should read config with latest-patch and latest-minor strategy``() = + let cfg = DependenciesFile.FromSource(strategyConfig "latest-patch" "latest-minor") + cfg.Groups.[Constants.MainDependencyGroup].Options.ResolverStrategyForTransitives |> shouldEqual (Some ResolverStrategy.LatestPatch) + cfg.Groups.[GroupName "Test"].Options.ResolverStrategyForTransitives |> shouldEqual (Some ResolverStrategy.LatestMinor) + + cfg.Groups.[Constants.MainDependencyGroup].Sources |> shouldEqual [PackageSource.NuGetV2Source "http://www.nuget.org/api/v2"] let noStrategyConfig = sprintf """ strategy %s diff --git a/tests/Paket.Tests/Lockfile/ParserSpecs.fs b/tests/Paket.Tests/Lockfile/ParserSpecs.fs index 0489f197da..fd08e3015f 100644 --- a/tests/Paket.Tests/Lockfile/ParserSpecs.fs +++ b/tests/Paket.Tests/Lockfile/ParserSpecs.fs @@ -670,6 +670,34 @@ NUGET packages.Length |> shouldEqual 1 lockFile.Options.ResolverStrategyForTransitives |> shouldEqual (Some ResolverStrategy.Max) +[] +let ``should parse strategy latest-patch lock file``() = + let lockFile = """STRATEGY: LATEST-PATCH +NUGET + remote: "D:\code\temp with space" + specs: + Castle.Windsor (2.1) +""" + let lockFile = LockFileParser.Parse(toLines lockFile) |> List.head + let packages = List.rev lockFile.Packages + + packages.Length |> shouldEqual 1 + lockFile.Options.ResolverStrategyForTransitives |> shouldEqual (Some ResolverStrategy.LatestPatch) + +[] +let ``should parse strategy latest-minor lock file``() = + let lockFile = """STRATEGY: LATEST-MINOR +NUGET + remote: "D:\code\temp with space" + specs: + Castle.Windsor (2.1) +""" + let lockFile = LockFileParser.Parse(toLines lockFile) |> List.head + let packages = List.rev lockFile.Packages + + packages.Length |> shouldEqual 1 + lockFile.Options.ResolverStrategyForTransitives |> shouldEqual (Some ResolverStrategy.LatestMinor) + [] let ``should parse no strategy lock file``() = let lockFile = """NUGET diff --git a/tests/Paket.Tests/Paket.Tests.fsproj b/tests/Paket.Tests/Paket.Tests.fsproj index e9c1f6b251..f63667b259 100644 --- a/tests/Paket.Tests/Paket.Tests.fsproj +++ b/tests/Paket.Tests/Paket.Tests.fsproj @@ -106,6 +106,7 @@ + diff --git a/tests/Paket.Tests/Resolver/DependencyGroupsAndRestrictions.fs b/tests/Paket.Tests/Resolver/DependencyGroupsAndRestrictions.fs index 446da24b17..29b893333a 100644 --- a/tests/Paket.Tests/Resolver/DependencyGroupsAndRestrictions.fs +++ b/tests/Paket.Tests/Resolver/DependencyGroupsAndRestrictions.fs @@ -1,4 +1,4 @@ -module DependencyGroupsAndRestrictions +module Paket.Resolver.DependencyGroupsAndRestrictions open Paket open NUnit.Framework diff --git a/tests/Paket.Tests/Resolver/GlobalKeepLatestPatchStrategySpecs.fs b/tests/Paket.Tests/Resolver/GlobalKeepLatestPatchStrategySpecs.fs new file mode 100644 index 0000000000..107b701bcd --- /dev/null +++ b/tests/Paket.Tests/Resolver/GlobalKeepLatestPatchStrategySpecs.fs @@ -0,0 +1,128 @@ +module Paket.Resolver.GlobalKeepLatestPatchStrategySpecs + +open Paket +open NUnit.Framework +open FsUnit +open TestHelpers +open Paket.Domain +open Paket.PackageResolver + +let resolve graph updateMode (cfg : DependenciesFile) = + let groups = [Constants.MainDependencyGroup, None ] |> Map.ofSeq + cfg.Resolve(true,noSha1,VersionsFromGraphAsSeq graph, (fun _ _ -> []),PackageDetailsFromGraph graph,(fun _ _ _ -> None),groups,updateMode).[Constants.MainDependencyGroup].ResolvedPackages.GetModelOrFail() + +let graph = + OfSimpleGraph [ + "Nancy.Bootstrappers.Windsor","0.23",["Castle.Windsor",VersionRequirement(VersionRange.AtLeast "3.2.1",PreReleaseStatus.No)] + "Castle.Windsor","3.2.1",[] + "Castle.Windsor","3.2.2",[] + "Castle.Windsor","3.3.0",[] + "Castle.Windsor","4.1.0",[] + ] + +let config1 = sprintf """ +strategy %s +source http://www.nuget.org/api/v2 + +nuget Nancy.Bootstrappers.Windsor ~> 0.23 +""" + +[] +let ``should resolve simple config1 with latest-patch``() = + let resolved = + DependenciesFile.FromSource(config1 "latest-patch") + |> resolve graph UpdateMode.UpdateAll + getVersion resolved.[PackageName "Castle.Windsor"] |> shouldEqual "3.2.2" + getVersion resolved.[PackageName "Nancy.Bootstrappers.Windsor"] |> shouldEqual "0.23" + + +[] +let ``should resolve simple config1 with latest-minor``() = + let resolved = + DependenciesFile.FromSource(config1 "latest-minor") + |> resolve graph UpdateMode.UpdateAll + getVersion resolved.[PackageName "Castle.Windsor"] |> shouldEqual "3.3.0" + getVersion resolved.[PackageName "Nancy.Bootstrappers.Windsor"] |> shouldEqual "0.23" + +let config2 = sprintf """ +strategy %s +source http://www.nuget.org/api/v2 + +nuget Castle.Windsor +nuget Nancy.Bootstrappers.Windsor ~> 0.23 +""" + +[] +let ``should resolve simple config2 with latest-patch``() = + let resolved = + DependenciesFile.FromSource(config2 "latest-patch") + |> resolve graph UpdateMode.UpdateAll + getVersion resolved.[PackageName "Castle.Windsor"] |> shouldEqual "3.2.2" + getVersion resolved.[PackageName "Nancy.Bootstrappers.Windsor"] |> shouldEqual "0.23" + + +[] +let ``should resolve simple config2 with latest-minor``() = + let resolved = + DependenciesFile.FromSource(config2 "latest-minor") + |> resolve graph UpdateMode.UpdateAll + getVersion resolved.[PackageName "Castle.Windsor"] |> shouldEqual "3.3.0" + getVersion resolved.[PackageName "Nancy.Bootstrappers.Windsor"] |> shouldEqual "0.23" + +let config3 = sprintf """ +strategy %s +source http://www.nuget.org/api/v2 + +nuget Nancy.Bootstrappers.Windsor ~> 0.23 +nuget Castle.Windsor +""" + +[] +let ``should resolve simple config3 with latest-patch``() = + let resolved = + DependenciesFile.FromSource(config3 "latest-patch") + |> resolve graph UpdateMode.UpdateAll + getVersion resolved.[PackageName "Castle.Windsor"] |> shouldEqual "3.2.2" + getVersion resolved.[PackageName "Nancy.Bootstrappers.Windsor"] |> shouldEqual "0.23" + + +[] +let ``should resolve simple config3 with latest-minor``() = + let resolved = + DependenciesFile.FromSource(config3 "latest-minor") + |> resolve graph UpdateMode.UpdateAll + getVersion resolved.[PackageName "Castle.Windsor"] |> shouldEqual "3.3.0" + getVersion resolved.[PackageName "Nancy.Bootstrappers.Windsor"] |> shouldEqual "0.23" + +let graph2 = + OfSimpleGraph [ + "Nancy.Bootstrappers.Windsor","0.23",["Castle.Windsor",VersionRequirement(VersionRange.AtLeast "3.2.1",PreReleaseStatus.No)] + "Castle.Windsor","3.2.0",["Castle.Core",VersionRequirement(VersionRange.AtLeast "3.2.0",PreReleaseStatus.No)] + "Castle.Windsor","3.2.1",["Castle.Core",VersionRequirement(VersionRange.AtLeast "3.2.0",PreReleaseStatus.No)] + "Castle.Windsor","3.3.0",["Castle.Core",VersionRequirement(VersionRange.AtLeast "3.3.0",PreReleaseStatus.No)] + "Castle.Windsor-NLog","3.2.0.1",["Castle.Core-NLog",VersionRequirement(VersionRange.AtLeast "3.2.0",PreReleaseStatus.No)] + "Castle.Windsor-NLog","3.3.0",["Castle.Core-NLog",VersionRequirement(VersionRange.AtLeast "3.3.0",PreReleaseStatus.No)] + "Castle.Core-NLog","3.2.0",["Castle.Core",VersionRequirement(VersionRange.AtLeast "3.2.0",PreReleaseStatus.No)] + "Castle.Core-NLog","3.3.0",["Castle.Core",VersionRequirement(VersionRange.AtLeast "3.3.0",PreReleaseStatus.No)] + "Castle.Core-NLog","3.3.1",["Castle.Core",VersionRequirement(VersionRange.AtLeast "3.3.1",PreReleaseStatus.No)] + "Castle.Core","3.2.0",[] + "Castle.Core","3.2.1",[] + "Castle.Core","3.2.2",[] + "Castle.Core","3.3.0",[] + "Castle.Core","3.3.1",[] + ] + +let config5 = sprintf """ +strategy %s +source http://www.nuget.org/api/v2 + +nuget Nancy.Bootstrappers.Windsor !~> 0.23 +""" + +[] +let ``should override global strategy with latest-patch``() = + let resolved = + DependenciesFile.FromSource(config5 "latest-patch") + |> resolve graph2 UpdateMode.UpdateAll + getVersion resolved.[PackageName "Castle.Windsor"] |> shouldEqual "3.2.1" + getVersion resolved.[PackageName "Nancy.Bootstrappers.Windsor"] |> shouldEqual "0.23" \ No newline at end of file diff --git a/tests/Paket.Tests/Resolver/GlobalOptimisticStrategySpecs.fs b/tests/Paket.Tests/Resolver/GlobalOptimisticStrategySpecs.fs index f91ea70552..9a0f4781f6 100644 --- a/tests/Paket.Tests/Resolver/GlobalOptimisticStrategySpecs.fs +++ b/tests/Paket.Tests/Resolver/GlobalOptimisticStrategySpecs.fs @@ -1,4 +1,4 @@ -module GlobalOptimisticStrategySpecs +module Paket.Resolver.GlobalOptimisticStrategySpecs open Paket open NUnit.Framework diff --git a/tests/Paket.Tests/Resolver/ResolverErrorSituationTests.fs b/tests/Paket.Tests/Resolver/ResolverErrorSituationTests.fs index eb96831cf3..297e3716e9 100644 --- a/tests/Paket.Tests/Resolver/ResolverErrorSituationTests.fs +++ b/tests/Paket.Tests/Resolver/ResolverErrorSituationTests.fs @@ -1,4 +1,4 @@ -module ResolverErrorSituationTests +module Paket.Resolver.ResolverErrorSituationTests open Paket open NUnit.Framework diff --git a/tests/Paket.Tests/Resolver/StrategySpecs.fs b/tests/Paket.Tests/Resolver/StrategySpecs.fs index 0315319c2f..a7a202a3d3 100644 --- a/tests/Paket.Tests/Resolver/StrategySpecs.fs +++ b/tests/Paket.Tests/Resolver/StrategySpecs.fs @@ -1,4 +1,4 @@ -module StrategySpecs +module Paket.Resolver.StrategySpecs open Paket open NUnit.Framework