summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Giddins <segiddins@segiddins.me>2017-08-01 15:41:21 -0500
committerSamuel Giddins <segiddins@segiddins.me>2017-08-01 16:07:28 -0500
commit02ed02ff784f7168d29e5633a094013199db4d46 (patch)
tree3e35f2a836347d728e2102d42490f62b5faeca46
parentb3f33fdfe128ebe1266a1e34a453a5c2cd46a4c2 (diff)
downloadbundler-02ed02ff784f7168d29e5633a094013199db4d46.tar.gz
WIP
-rw-r--r--lib/bundler/definition.rb41
-rw-r--r--lib/bundler/dependency.rb12
-rw-r--r--lib/bundler/lazy_specification.rb7
-rw-r--r--lib/bundler/lockfile_generator.rb36
-rw-r--r--lib/bundler/lockfile_parser.rb144
-rw-r--r--lib/bundler/source_list.rb9
6 files changed, 196 insertions, 53 deletions
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index a91f7e2a28..a84f4c3b5d 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -34,14 +34,27 @@ module Bundler
raise GemfileNotFound, "#{gemfile} not found" unless gemfile.file?
+ parsed_lockfile = nil
if lockfile && File.exist?(lockfile)
- # parsed_lockfile = LockfileParser.new(Bundler.read_file(lockfile))
- # use_lockfile = parsed_lockfile.gemfile_checksums.reduce do |file, checksum|
- # p file, checksum
- # end
+ parsed_lockfile = LockfileParser.new(Bundler.read_file(lockfile))
+ changed_gemfiles = parsed_lockfile.gemfiles.values.reject(&:unchanged?)
+ if !changed_gemfiles.empty?
+ warn "skipping lf since #{changed_gemfiles} changed"
+ elsif unlock != {}
+ warn "skipping lf since unlocking"
+ elsif parsed_lockfile.gemfiles.empty?
+ warn "skipping lf since no locked gemfiles"
+ else
+ sources = parsed_lockfile.sources.dup
+ aggregate = sources.delete(parsed_lockfile.aggregate_source)
+ sources = SourceList.from_sources(sources)
+ aggregate.remotes.each {|r| sources.global_rubygems_source = r } if aggregate
+ # warn "using lockfile"
+ return Definition.new(parsed_lockfile, parsed_lockfile.dependencies.values, sources, {}, parsed_lockfile.ruby_version, parsed_lockfile.optional_groups, parsed_lockfile.gemfiles.keys)
+ end
end
- Dsl.evaluate(gemfile, lockfile, unlock)
+ Dsl.evaluate(gemfile, parsed_lockfile, unlock)
end
#
@@ -74,18 +87,15 @@ module Bundler
@ruby_version = ruby_version
@gemfiles = gemfiles
- @lockfile = lockfile
- @lockfile_contents = String.new
- @locked_bundler_version = nil
- @locked_ruby_version = nil
+ @locked_gems = lockfile
+ @lockfile = lockfile && lockfile.lockfile
+ @lockfile_contents = lockfile && lockfile.lockfile_contents || String.new
+ @locked_bundler_version = lockfile && lockfile.bundler_version
+ @locked_ruby_version = lockfile && lockfile.ruby_version
+ @locked_platforms = lockfile && lockfile.platforms || []
- if lockfile && File.exist?(lockfile)
- @lockfile_contents = Bundler.read_file(lockfile)
- @locked_gems = LockfileParser.new(@lockfile_contents)
- @locked_platforms = @locked_gems.platforms
+ if lockfile
@platforms = @locked_platforms.dup
- @locked_bundler_version = @locked_gems.bundler_version
- @locked_ruby_version = @locked_gems.ruby_version
if unlock != true
@locked_deps = @locked_gems.dependencies
@@ -104,7 +114,6 @@ module Bundler
@locked_deps = {}
@locked_specs = SpecSet.new([])
@locked_sources = []
- @locked_platforms = []
end
@unlock[:gems] ||= []
diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb
index f943c6530a..2edd4ffa63 100644
--- a/lib/bundler/dependency.rb
+++ b/lib/bundler/dependency.rb
@@ -84,13 +84,23 @@ module Bundler
@autorequire = nil
@groups = Array(options["group"] || :default).map(&:to_sym)
@source = options["source"]
- @platforms = Array(options["platforms"])
+ @platforms = Array(options["platforms"]).map(&:to_sym)
@env = options["env"]
@should_include = options.fetch("should_include", true)
@autorequire = Array(options["require"] || []) if options.key?("require")
end
+ def options_to_lock
+ {
+ "env" => @env,
+ "group" => @groups,
+ "platforms" => @platforms,
+ "require" => @autorequire,
+ "source" => @source,
+ }.reject {|_, v| Array(v).empty? }
+ end
+
# Returns the platforms this dependency is valid for, in the same order as
# passed in the `valid_platforms` parameter
def gem_platforms(valid_platforms)
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index 821d249d96..8e8f242ee9 100644
--- a/lib/bundler/lazy_specification.rb
+++ b/lib/bundler/lazy_specification.rb
@@ -62,7 +62,12 @@ module Bundler
dependencies.sort_by(&:to_s).uniq.each do |dep|
next if dep.type == :development
- out << " #{dep.to_lock(true)}\n"
+ out << " #{dep.name}"
+ unless dep.requirement.none?
+ reqs = dep.requirement.requirements.map {|o, v| "#{o} #{v}" }.sort.reverse
+ out << " (#{reqs.join(", ")})"
+ end
+ out << "\n"
end
out
diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb
index a065298486..324b39324b 100644
--- a/lib/bundler/lockfile_generator.rb
+++ b/lib/bundler/lockfile_generator.rb
@@ -20,6 +20,7 @@ module Bundler
add_platforms
add_dependencies
add_section("OPTIONAL GROUPS", definition.optional_groups)
+ add_gemfiles
add_locked_ruby_version
add_bundled_with
@@ -61,11 +62,31 @@ module Bundler
handled = []
definition.dependencies.sort_by(&:to_s).each do |dep|
next if handled.include?(dep.name)
- out << dep.to_lock
+ out << " #{dep.name}"
+ unless dep.requirement.none?
+ reqs = dep.requirement.requirements.map {|o, v| "#{o} #{v}" }.sort.reverse
+ out << " (#{reqs.join(", ")})"
+ end
+ out << "!" if dep.source
+ out << "\n"
+ add_value(dep.options_to_lock, 4)
handled << dep.name
end
end
+ def add_gemfiles
+ return unless SharedHelpers.md5_available?
+ gemfiles = {}
+ definition.gemfiles.each do |file|
+ md5 = Digest::MD5.file(file).hexdigest
+ if file.to_s.start_with?(Bundler.root.to_s)
+ file = file.relative_path_from(Bundler.root)
+ end
+ gemfiles[file] = "md5 #{md5}"
+ end
+ add_section("GEMFILE CHECKSUMS", gemfiles)
+ end
+
def add_locked_ruby_version
return unless locked_ruby_version = definition.locked_ruby_version
add_section("RUBY VERSION", locked_ruby_version.to_s)
@@ -77,17 +98,24 @@ module Bundler
def add_section(name, value)
out << "\n#{name}\n"
+ add_value(value, 2)
+ end
+
+ def add_value(value, indentation)
+ indent = " " * indentation
case value
when Array
value.map(&:to_s).sort.each do |val|
- out << " #{val}\n"
+ out << "#{indent}#{val}\n"
end
when Hash
value.to_a.sort_by {|k, _| k.to_s }.each do |key, val|
- out << " #{key}: #{val}\n"
+ Array(val).sort.each do |v|
+ out << "#{indent}#{key}: #{v}\n"
+ end
end
when String
- out << " #{value}\n"
+ out << "#{indent} #{value}\n"
else
raise ArgumentError, "#{value.inspect} can't be serialized in a lockfile"
end
diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb
index ff706fca1d..8c23d1bae2 100644
--- a/lib/bundler/lockfile_parser.rb
+++ b/lib/bundler/lockfile_parser.rb
@@ -12,7 +12,20 @@
module Bundler
class LockfileParser
- attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version
+ LockedGemfile = Struct.new(:path, :checksum, :digest) do
+ def unchanged?
+ return unless path.file?
+ calculate_checksum == checksum
+ end
+
+ def calculate_checksum
+ case digest
+ when "md5" then SharedHelpers.md5_available? && Digest::MD5.file(path).hexdigest
+ end.to_s
+ end
+ end
+
+ attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version, :optional_groups, :gemfiles, :lockfile_contents, :lockfile
BUNDLED = "BUNDLED WITH".freeze
DEPENDENCIES = "DEPENDENCIES".freeze
@@ -22,8 +35,10 @@ module Bundler
GEM = "GEM".freeze
PATH = "PATH".freeze
PLUGIN = "PLUGIN SOURCE".freeze
+ GROUPS = "OPTIONAL GROUPS".freeze
+ GEMFILES = "GEMFILE CHECKSUMS".freeze
SPECS = " specs:".freeze
- OPTIONS = /^ ([a-z]+): (.*)$/i
+ OPTIONS = /^ {2}+([a-z]+): (.*)$/i
SOURCE = [GIT, GEM, PATH, PLUGIN].freeze
SECTIONS_BY_VERSION_INTRODUCED = {
@@ -33,6 +48,7 @@ module Bundler
Gem::Version.create("1.10".dup) => [BUNDLED].freeze,
Gem::Version.create("1.12".dup) => [RUBY].freeze,
Gem::Version.create("1.13".dup) => [PLUGIN].freeze,
+ Gem::Version.create("2.0".dup) => [GROUPS, GEMFILES].freeze,
}.freeze
KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten.freeze
@@ -58,12 +74,16 @@ module Bundler
attributes
end
- def initialize(lockfile)
+ def initialize(lockfile, path = nil)
@platforms = []
@sources = []
@dependencies = {}
@state = nil
@specs = {}
+ @gemfiles = {}
+ @optional_groups = []
+ @lockfile_contents = lockfile
+ @lockfile = path
@rubygems_aggregate = Source::Rubygems.new
@@ -72,24 +92,34 @@ module Bundler
"Run `git checkout HEAD -- #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` first to get a clean lock."
end
- lockfile.split(/(?:\r?\n)+/).each do |line|
- if SOURCE.include?(line)
- @state = :source
- parse_source(line)
- elsif line == DEPENDENCIES
+ lockfile.split(/(?:\r?\n)/).each do |line|
+ case line
+ when DEPENDENCIES
@state = :dependency
- elsif line == PLATFORMS
+ when PLATFORMS
@state = :platform
- elsif line == RUBY
+ when RUBY
@state = :ruby
- elsif line == BUNDLED
+ when BUNDLED
@state = :bundled_with
- elsif line =~ /^[^\s]/
+ when GROUPS
+ @state = :optional_group
+ when GEMFILES
+ @state = :gemfile
+ when ""
+ send("finalize_#{@state}") if @state
+ when *SOURCE # rubocop:disable Performance/CaseWhenSplat
+ @state = :source
+ parse_source(line)
+ when /^[^\s]/
@state = nil
- elsif @state
- send("parse_#{@state}", line)
+ else
+ send("parse_#{@state}", line) if @state
end
+ # puts "#{@state.to_s.rjust(15)} => #{line}"
end
+ send("finalize_#{@state}") if @state
+
@sources << @rubygems_aggregate unless Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
@specs = @specs.values.sort_by(&:identifier)
warn_for_outdated_bundler_version
@@ -116,6 +146,16 @@ module Bundler
end
end
+ def aggregate_source
+ return @aggregate_source unless Bundler.feature_flag.lockfile_uses_separate_rubygems_sources?
+ pinned_sources = @dependencies.values.map(&:source).uniq
+ @sources.find do |s|
+ next unless s.is_a?(Source::Rubygems)
+ next if pinned_sources.include?(s)
+ true
+ end
+ end
+
private
TYPES = {
@@ -155,19 +195,9 @@ module Bundler
@current_source = Plugin.source_from_lock(@opts)
@sources << @current_source
end
+ @opts = {}
when OPTIONS
- value = $2
- value = true if value == "true"
- value = false if value == "false"
-
- key = $1
-
- if @opts[key]
- @opts[key] = Array(@opts[key])
- @opts[key] << value
- else
- @opts[key] = value
- end
+ parse_opts($1, $2)
when *SOURCE
@current_source = nil
@opts = {}
@@ -177,6 +207,20 @@ module Bundler
end
end
+ def finalize_source; end
+
+ def parse_opts(key, value)
+ value = true if value == "true"
+ value = false if value == "false"
+
+ if @opts[key]
+ @opts[key] = Array(@opts[key])
+ @opts[key] << value
+ else
+ @opts[key] = value
+ end
+ end
+
space = / /
NAME_VERSION = /
^(#{space}{2}|#{space}{4}|#{space}{6})(?!#{space}) # Exactly 2, 4, or 6 spaces at the start of the line
@@ -188,6 +232,10 @@ module Bundler
/xo
def parse_dependency(line)
+ return parse_opts($1, $2) if line =~ OPTIONS
+
+ finalize_dependency
+
return unless line =~ NAME_VERSION
spaces = $1
return unless spaces.size == 2
@@ -195,11 +243,16 @@ module Bundler
version = $3
pinned = $5
- version = version.split(",").map(&:strip) if version
+ @dep_name = name
+ @dep_version = version && version.split(",").map(&:strip)
+ @dep_pinned = pinned
+ end
- dep = Bundler::Dependency.new(name, version)
+ def finalize_dependency
+ return unless defined?(@dep_name) && @dep_name
+ dep = Bundler::Dependency.new(@dep_name, @dep_version, @opts)
- if pinned && dep.name != "bundler"
+ if @dep_pinned && dep.name != "bundler"
spec = @specs.find {|_, v| v.name == dep.name }
dep.source = spec.last.source if spec
@@ -207,11 +260,12 @@ module Bundler
# to use in the case that there are no gemspecs present. A fake
# gemspec is created based on the version set on the dependency
# TODO: Use the version from the spec instead of from the dependency
- if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path)
- dep.source.name = name
+ if @dep_version && @dep_version.size == 1 && @dep_version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path)
+ dep.source.name = @dep_name
dep.source.version = $1
end
end
+ @opts = {}
@dependencies[dep.name] = dep
end
@@ -239,18 +293,46 @@ module Bundler
end
end
+ def finalize_spec; end
+
def parse_platform(line)
@platforms << Gem::Platform.new($1) if line =~ /^ (.*)$/
end
+ def finalize_platform; end
+
def parse_bundled_with(line)
line = line.strip
return unless Gem::Version.correct?(line)
@bundler_version = Gem::Version.create(line)
end
+ def finalize_bundled_with; end
+
def parse_ruby(line)
@ruby_version = line.strip
end
+
+ def finalize_ruby; end
+
+ def parse_optional_group(line)
+ @optional_groups << line.strip
+ end
+
+ def finalize_optional_group; end
+
+ def parse_gemfile(line)
+ return unless line =~ OPTIONS
+ parse_opts($1, $2)
+ end
+
+ def finalize_gemfile
+ @opts.each do |file, checksum|
+ digest, sum = checksum.split(" ", 2)
+ file = Pathname(file).expand_path(Bundler.root)
+ @gemfiles[file] = LockedGemfile.new(file, sum, digest)
+ end
+ @opts = {}
+ end
end
end
diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb
index dd5a07afd7..61598c128f 100644
--- a/lib/bundler/source_list.rb
+++ b/lib/bundler/source_list.rb
@@ -18,6 +18,15 @@ module Bundler
@metadata_source = Source::Metadata.new
end
+ def self.from_sources(sources)
+ source_list = new
+ sources.each do |source|
+ list = source_list.send(:source_list_for, source)
+ source_list.send(:add_source_to_list, source, list)
+ end
+ source_list
+ end
+
def add_path_source(options = {})
if options["gemspec"]
add_source_to_list Source::Gemspec.new(options), path_sources