159 lines
4.9 KiB
Ruby
159 lines
4.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Bundler
|
|
class LockfileGenerator
|
|
attr_reader :definition
|
|
attr_reader :out
|
|
|
|
# @private
|
|
def initialize(definition)
|
|
@definition = definition
|
|
@out = String.new
|
|
end
|
|
|
|
def self.generate(definition)
|
|
new(definition).generate!
|
|
end
|
|
|
|
def generate!
|
|
add_sources
|
|
add_platforms
|
|
add_dependencies
|
|
add_checksums
|
|
add_locked_ruby_version
|
|
add_bundled_with
|
|
|
|
out
|
|
end
|
|
|
|
private
|
|
|
|
def add_sources
|
|
definition.sources.lock_sources.each_with_index do |source, idx|
|
|
out << "\n" unless idx.zero?
|
|
|
|
# Add the source header
|
|
out << source.to_lock
|
|
|
|
# Find all specs for this source
|
|
specs = definition.resolve.select {|s| source.can_lock?(s) }
|
|
add_specs(specs)
|
|
end
|
|
end
|
|
|
|
def add_specs(specs)
|
|
# This needs to be sorted by full name so that
|
|
# gems with the same name, but different platform
|
|
# are ordered consistently
|
|
specs.sort_by(&:full_name).each do |spec|
|
|
next if spec.name == "bundler"
|
|
out << spec.to_lock
|
|
end
|
|
end
|
|
|
|
def add_platforms
|
|
add_section("PLATFORMS", definition.platforms)
|
|
end
|
|
|
|
def add_dependencies
|
|
out << "\nDEPENDENCIES\n"
|
|
|
|
handled = []
|
|
definition.dependencies.sort_by(&:to_s).each do |dep|
|
|
next if handled.include?(dep.name)
|
|
out << dep.to_lock << "\n"
|
|
handled << dep.name
|
|
end
|
|
end
|
|
|
|
def add_checksums
|
|
return unless definition.locked_checksums
|
|
checksums = definition.resolve.map do |spec|
|
|
spec.source.checksum_store.to_lock(spec)
|
|
end
|
|
|
|
add_section("CHECKSUMS", checksums + bundler_checksum)
|
|
end
|
|
|
|
def add_locked_ruby_version
|
|
return unless locked_ruby_version = definition.locked_ruby_version
|
|
add_section("RUBY VERSION", locked_ruby_version.to_s)
|
|
end
|
|
|
|
def add_bundled_with
|
|
add_section("BUNDLED WITH", definition.bundler_version_to_lock.to_s)
|
|
end
|
|
|
|
def add_section(name, value)
|
|
out << "\n#{name}\n"
|
|
case value
|
|
when Array
|
|
value.map(&:to_s).sort.each do |val|
|
|
out << " #{val}\n"
|
|
end
|
|
when Hash
|
|
value.to_a.sort_by {|k, _| k.to_s }.each do |key, val|
|
|
out << " #{key}: #{val}\n"
|
|
end
|
|
when String
|
|
out << " #{value}\n"
|
|
else
|
|
raise ArgumentError, "#{value.inspect} can't be serialized in a lockfile"
|
|
end
|
|
end
|
|
|
|
def bundler_checksum
|
|
# In frozen mode the lockfile can't change, so reproduce whatever bundler
|
|
# entry is already locked instead of recording one for the running bundler
|
|
# version, which may legitimately differ from the locked one.
|
|
return locked_bundler_checksum if Bundler.frozen_bundle?
|
|
|
|
# `.dev` versions and `SKIP_BUNDLER_CHECKSUM` are deliberate opt-outs (used
|
|
# by Bundler/RubyGems' own development and release tasks): never record a
|
|
# checksum for Bundler itself in those cases.
|
|
return [] if Bundler.gem_version.to_s.end_with?(".dev") || ENV["SKIP_BUNDLER_CHECKSUM"]
|
|
|
|
bundler_spec = definition.sources.metadata_source.specs.search(["bundler", Bundler.gem_version]).last
|
|
|
|
# Record a fresh checksum from the locally cached gem when it's available.
|
|
# When it isn't (e.g. a fresh checkout/CI that never downloaded the bundler
|
|
# gem), fall back to whatever checksum is already locked rather than
|
|
# dropping it, so the entry stays consistent across environments.
|
|
if File.exist?(bundler_spec.cache_file)
|
|
require "rubygems/package"
|
|
|
|
package = Gem::Package.new(bundler_spec.cache_file)
|
|
definition.sources.metadata_source.checksum_store.register(bundler_spec, Checksum.from_gem_package(package))
|
|
elsif bundled_with_changing?
|
|
# We can't compute a fresh checksum (the bundler gem isn't cached) and the
|
|
# BUNDLED WITH version is changing. Keeping the previously locked checksum
|
|
# would leave a `bundler (<old version>) sha256=...` entry that no longer
|
|
# matches the new BUNDLED WITH version, so drop it instead.
|
|
return []
|
|
end
|
|
|
|
return [] if definition.sources.metadata_source.checksum_store.missing?(bundler_spec)
|
|
|
|
[definition.sources.metadata_source.checksum_store.to_lock(bundler_spec)]
|
|
end
|
|
|
|
def bundled_with_changing?
|
|
locked_gems = definition.locked_gems
|
|
return false unless locked_gems
|
|
|
|
locked_gems.bundler_version != definition.bundler_version_to_lock
|
|
end
|
|
|
|
def locked_bundler_checksum
|
|
locked_version = definition.locked_gems&.bundler_version
|
|
return [] unless locked_version
|
|
|
|
metadata_source = definition.sources.metadata_source
|
|
locked_spec = LazySpecification.new("bundler", locked_version, Gem::Platform::RUBY, metadata_source)
|
|
return [] if metadata_source.checksum_store.missing?(locked_spec)
|
|
|
|
[metadata_source.checksum_store.to_lock(locked_spec)]
|
|
end
|
|
end
|
|
end
|