Skip to content

Commit 40358cc

Browse files
committed
Fix: pre-releases must be opt-in (SAT solver)
SAT solver musn't select a solution with a pre-release unless one of the dependent shard did opt-in for a pre-release.
1 parent f223173 commit 40358cc

File tree

1 file changed

+33
-10
lines changed

1 file changed

+33
-10
lines changed

src/solver.cr

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ require "./spec"
66
module Shards
77
class Solver
88
setter locks : Array(Dependency)?
9+
@solution : Array(Package)?
910

1011
def initialize(@spec : Spec)
1112
@graph = Graph.new
1213
@sat = SAT.new
14+
@solution = nil
15+
@solution_distance = Int32::MAX
1316
end
1417

1518
def prepare(development = true) : Nil
@@ -20,26 +23,46 @@ module Shards
2023
def solve : Array(Package)?
2124
distances = calculate_distances
2225

23-
solution = nil
24-
solution_distance = Int32::MAX
25-
2626
@sat.solve do |proposal|
2727
# calculate the proposal quality (distance from ideal solution):
2828
distance = proposal.reduce(0) { |a, e| a + distances[e] }
2929

30-
if distance < solution_distance
30+
if distance < @solution_distance
3131
# select better solution (less distance from ideal):
32-
solution = proposal.dup
33-
solution_distance = distance
32+
consider(proposal, distance)
3433

35-
elsif distance == solution_distance && proposal.size < solution.not_nil!.size
34+
elsif distance == @solution_distance && proposal.size < @solution.not_nil!.size
3635
# select solution with fewer dependencies (same distance from ideal):
37-
solution = proposal.dup
38-
solution_distance = distance
36+
consider(proposal, distance)
37+
end
38+
end
39+
40+
@solution
41+
end
42+
43+
private def consider(proposal, distance)
44+
packages = to_packages(proposal)
45+
46+
# pre-releases are opt-in, so we must check that the solution didn't
47+
# select one unless at least one requirement in the selected graph asked
48+
# for it:
49+
packages.each do |package|
50+
next unless Versions.prerelease?(package.version)
51+
52+
if dependency = @spec.dependencies.find { |d| d.name == package.name }
53+
break if Versions.prerelease?(dependency.version)
54+
end
55+
56+
return unless packages.any? do |pkg|
57+
if dependency = pkg.spec.dependencies.find { |d| d.name == package.name }
58+
Versions.prerelease?(dependency.version)
59+
end
3960
end
4061
end
4162

42-
to_packages(solution) if solution
63+
# solution is acceptable:
64+
@solution = packages
65+
@solution_distance = distance
4366
end
4467

4568
private def to_packages(solution)

0 commit comments

Comments
 (0)