diff options
author | Thong Kuah <tkuah@gitlab.com> | 2022-08-01 11:42:18 +1200 |
---|---|---|
committer | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2023-10-23 13:59:01 +0900 |
commit | ad08674d8dc17c4ca031ce20760c4a4779c83e27 (patch) | |
tree | bf4958d6f06c36051f9c65e53c9e615ea1d978b2 /lib | |
parent | 2d468358a516f575d013f07801079e0906c61f0c (diff) |
[rubygems/rubygems] Add CHECKSUMS for each gem in lockfile
We lock the checksum for each resolved spec under a new CHECKSUMS
section in the lockfile.
If the locked spec does not resolve for the local platform, we preserve
the locked checksum, similar to how we preserve specs.
Checksum locking only makes sense on install. The compact index
information is only available then.
https://github.com/rubygems/rubygems/commit/bde37ca6bf
Diffstat (limited to 'lib')
-rw-r--r-- | lib/bundler.rb | 1 | ||||
-rw-r--r-- | lib/bundler/checksum.rb | 42 | ||||
-rw-r--r-- | lib/bundler/definition.rb | 3 | ||||
-rw-r--r-- | lib/bundler/endpoint_specification.rb | 5 | ||||
-rw-r--r-- | lib/bundler/lazy_specification.rb | 26 | ||||
-rw-r--r-- | lib/bundler/lockfile_generator.rb | 19 | ||||
-rw-r--r-- | lib/bundler/lockfile_parser.rb | 23 |
7 files changed, 118 insertions, 1 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb index ee08a2dab8..cc6c2c075c 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -39,6 +39,7 @@ module Bundler environment_preserver.replace_with_backup SUDO_MUTEX = Thread::Mutex.new + autoload :Checksum, File.expand_path("bundler/checksum", __dir__) autoload :Definition, File.expand_path("bundler/definition", __dir__) autoload :Dependency, File.expand_path("bundler/dependency", __dir__) autoload :Deprecate, File.expand_path("bundler/deprecate", __dir__) diff --git a/lib/bundler/checksum.rb b/lib/bundler/checksum.rb new file mode 100644 index 0000000000..2e0a80cac2 --- /dev/null +++ b/lib/bundler/checksum.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Bundler + class Checksum + attr_reader :name, :version, :platform + attr_accessor :checksum + + SHA256 = /\Asha256-([a-z0-9]{64}|[A-Za-z0-9+\/=]{44})\z/.freeze + + def initialize(name, version, platform, checksum = nil) + @name = name + @version = version + @platform = platform || Gem::Platform::RUBY + @checksum = checksum + + if @checksum && @checksum !~ SHA256 + raise ArgumentError, "invalid checksum (#{@checksum})" + end + end + + def match_spec?(spec) + name == spec.name && + version == spec.version && + platform.to_s == spec.platform.to_s + end + + def to_lock + out = String.new + + if platform == Gem::Platform::RUBY + out << " #{name} (#{version})" + else + out << " #{name} (#{version}-#{platform})" + end + + out << " #{checksum}" if checksum + out << "\n" + + out + end + end +end diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index d9c2670d6d..6b066051d8 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -15,6 +15,7 @@ module Bundler :dependencies, :locked_deps, :locked_gems, + :locked_checksums, :platforms, :ruby_version, :lockfile, @@ -92,6 +93,7 @@ module Bundler @locked_bundler_version = @locked_gems.bundler_version @locked_ruby_version = @locked_gems.ruby_version @originally_locked_specs = SpecSet.new(@locked_gems.specs) + @locked_checksums = @locked_gems.checksums if unlock != true @locked_deps = @locked_gems.dependencies @@ -112,6 +114,7 @@ module Bundler @originally_locked_specs = @locked_specs @locked_sources = [] @locked_platforms = [] + @locked_checksums = [] end locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) } diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb index 863544b1f9..4c41285043 100644 --- a/lib/bundler/endpoint_specification.rb +++ b/lib/bundler/endpoint_specification.rb @@ -104,6 +104,11 @@ module Bundler @remote_specification = spec end + def to_checksum + digest = "sha256-#{checksum}" if checksum + Bundler::Checksum.new(name, version, platform, digest) + end + private def _remote_specification diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index c9b161dc0e..b4aadb0b5c 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -76,6 +76,18 @@ module Bundler out end + #def materialize_for_checksum + #if @specification + #yield + #else + #materialize_for_installation + + #yield + + #@specification = nil + #end + #end + def materialize_for_installation source.local! @@ -134,6 +146,20 @@ module Bundler " #{source.revision[0..6]}" end + def to_checksum + return nil unless @specification + + # + # See comment about #ruby_platform_materializes_to_ruby_platform? + # If the old lockfile format is present where there is no specific + # platform, then we should skip locking checksums as it is not + # deterministic which platform variant is locked. + # + return nil unless ruby_platform_materializes_to_ruby_platform? + + @specification.to_checksum + end + private def use_exact_resolved_specifications? diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb index f7ba51b3e6..11e8e3f103 100644 --- a/lib/bundler/lockfile_generator.rb +++ b/lib/bundler/lockfile_generator.rb @@ -19,6 +19,7 @@ module Bundler add_sources add_platforms add_dependencies + add_checksums add_locked_ruby_version add_bundled_with @@ -65,6 +66,24 @@ module Bundler end end + def add_checksums + out << "\nCHECKSUMS\n" + + definition.resolve.sort_by(&:full_name).each do |spec| + checksum = spec.to_checksum if spec.respond_to?(:to_checksum) + + #if spec.is_a?(LazySpecification) + #spec.materialize_for_checksum do + #checksum ||= spec.to_checksum if spec.respond_to?(:to_checksum) + #end + #end + + checksum ||= definition.locked_checksums.find {|c| c.match_spec?(spec) } + + out << checksum.to_lock if checksum + end + end + def add_locked_ruby_version return unless locked_ruby_version = definition.locked_ruby_version add_section("RUBY VERSION", locked_ruby_version.to_s) diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index 31f57f14e8..fc331a928c 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -2,10 +2,11 @@ module Bundler class LockfileParser - attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version + attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version, :checksums BUNDLED = "BUNDLED WITH" DEPENDENCIES = "DEPENDENCIES" + CHECKSUMS = "CHECKSUMS" PLATFORMS = "PLATFORMS" RUBY = "RUBY VERSION" GIT = "GIT" @@ -21,6 +22,7 @@ module Bundler Gem::Version.create("1.10") => [BUNDLED].freeze, Gem::Version.create("1.12") => [RUBY].freeze, Gem::Version.create("1.13") => [PLUGIN].freeze, + Gem::Version.create("2.4.0") => [CHECKSUMS].freeze, }.freeze KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten!.freeze @@ -64,6 +66,7 @@ module Bundler @sources = [] @dependencies = {} @parse_method = nil + @checksums = [] @specs = {} if lockfile.match?(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/) @@ -77,6 +80,8 @@ module Bundler parse_source(line) elsif line == DEPENDENCIES @parse_method = :parse_dependency + elsif line == CHECKSUMS + @parse_method = :parse_checksum elsif line == PLATFORMS @parse_method = :parse_platform elsif line == RUBY @@ -144,6 +149,7 @@ module Bundler (?:#{space}\(([^-]*) # Space, followed by version (?:-(.*))?\))? # Optional platform (!)? # Optional pinned marker + (?:#{space}(.*))? # Optional checksum $ # Line end /xo.freeze @@ -176,6 +182,21 @@ module Bundler @dependencies[dep.name] = dep end + def parse_checksum(line) + if line =~ NAME_VERSION + spaces = $1 + return unless spaces.size == 2 + name = $2 + version = $3 + platform = $4 + checksum = $6 + + version = Gem::Version.new(version) + platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY + @checksums << Bundler::Checksum.new(name, version, platform, checksum) + end + end + def parse_spec(line) return unless line =~ NAME_VERSION spaces = $1 |