1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
require 'librarian/helpers'
require 'librarian/manifest_set'
require 'librarian/resolution'
require 'librarian/spec'
module Librarian
class SpecChangeSet
attr_accessor :environment
private :environment=
attr_reader :spec, :lock
def initialize(environment, spec, lock)
self.environment = environment
raise TypeError, "can't convert #{spec.class} into #{Spec}" unless Spec === spec
raise TypeError, "can't convert #{lock.class} into #{Resolution}" unless Resolution === lock
@spec, @lock = spec, lock
end
def same?
@same ||= spec.dependencies.sort_by{|d| d.name} == lock.dependencies.sort_by{|d| d.name}
end
def changed?
!same?
end
def spec_dependencies
@spec_dependencies ||= spec.dependencies
end
def spec_dependency_names
@spec_dependency_names ||= Set.new(spec_dependencies.map{|d| d.name})
end
def spec_dependency_index
@spec_dependency_index ||= Hash[spec_dependencies.map{|d| [d.name, d]}]
end
def lock_dependencies
@lock_dependencies ||= lock.dependencies
end
def lock_dependency_names
@lock_dependency_names ||= Set.new(lock_dependencies.map{|d| d.name})
end
def lock_dependency_index
@lock_dependency_index ||= Hash[lock_dependencies.map{|d| [d.name, d]}]
end
def lock_manifests
@lock_manifests ||= lock.manifests
end
def lock_manifests_index
@lock_manifests_index ||= ManifestSet.new(lock_manifests).to_hash
end
def removed_dependency_names
@removed_dependency_names ||= lock_dependency_names - spec_dependency_names
end
# A dependency which is deleted from the specfile will, in the general case,
# be removed conservatively. This means it might not actually be removed.
# But if the dependency originally declared a source which is now non-
# default, it must be removed, even if another dependency has a transitive
# dependency on the one that was removed (which is the scenario in which
# a conservative removal would not remove it). In this case, we must also
# remove it explicitly so that it can be re-resolved from the default
# source.
def explicit_removed_dependency_names
@explicit_removed_dependency_names ||= removed_dependency_names.reject do |name|
lock_manifest = lock_manifests_index[name]
spec.sources.include?(lock_manifest.source)
end.to_set
end
def added_dependency_names
@added_dependency_names ||= spec_dependency_names - lock_dependency_names
end
def nonmatching_added_dependency_names
@nonmatching_added_dependency_names ||= added_dependency_names.reject do |name|
spec_dependency = spec_dependency_index[name]
lock_manifest = lock_manifests_index[name]
if lock_manifest
matching = true
matching &&= spec_dependency.satisfied_by?(lock_manifest)
matching &&= spec_dependency.source == lock_manifest.source
matching
else
false
end
end.to_set
end
def common_dependency_names
@common_dependency_names ||= lock_dependency_names & spec_dependency_names
end
def changed_dependency_names
@changed_dependency_names ||= common_dependency_names.reject do |name|
spec_dependency = spec_dependency_index[name]
lock_dependency = lock_dependency_index[name]
lock_manifest = lock_manifests_index[name]
same = true
same &&= spec_dependency.satisfied_by?(lock_manifest)
same &&= spec_dependency.source == lock_dependency.source
same
end.to_set
end
def deep_keep_manifest_names
@deep_keep_manifest_names ||= begin
lock_dependency_names - (
removed_dependency_names +
changed_dependency_names +
nonmatching_added_dependency_names
)
end
end
def shallow_strip_manifest_names
@shallow_strip_manifest_names ||= begin
explicit_removed_dependency_names + changed_dependency_names
end
end
def inspect
Helpers.strip_heredoc(<<-INSPECT)
<##{self.class.name}:
Removed: #{removed_dependency_names.to_a.join(", ")}
ExplicitRemoved: #{explicit_removed_dependency_names.to_a.join(", ")}
Added: #{added_dependency_names.to_a.join(", ")}
NonMatchingAdded: #{nonmatching_added_dependency_names.to_a.join(", ")}
Changed: #{changed_dependency_names.to_a.join(", ")}
DeepKeep: #{deep_keep_manifest_names.to_a.join(", ")}
ShallowStrip: #{shallow_strip_manifest_names.to_a.join(", ")}
>
INSPECT
end
# Returns an array of those manifests from the previous spec which should be kept,
# based on inspecting the new spec against the locked resolution from the previous spec.
def analyze
@analyze ||= begin
debug { "Analyzing spec and lock:" }
if same?
debug { " Same!" }
return lock.manifests
end
debug { " Removed:" } ; removed_dependency_names.each { |name| debug { " #{name}" } }
debug { " ExplicitRemoved:" } ; explicit_removed_dependency_names.each { |name| debug { " #{name}" } }
debug { " Added:" } ; added_dependency_names.each { |name| debug { " #{name}" } }
debug { " NonMatchingAdded:" } ; nonmatching_added_dependency_names.each { |name| debug { " #{name}" } }
debug { " Changed:" } ; changed_dependency_names.each { |name| debug { " #{name}" } }
debug { " DeepKeep:" } ; deep_keep_manifest_names.each { |name| debug { " #{name}" } }
debug { " ShallowStrip:" } ; shallow_strip_manifest_names.each { |name| debug { " #{name}" } }
manifests = ManifestSet.new(lock_manifests)
manifests.deep_keep!(deep_keep_manifest_names)
manifests.shallow_strip!(shallow_strip_manifest_names)
manifests.to_a
end
end
private
def debug(*args, &block)
environment.logger.debug(*args, &block)
end
end
end
|