Skip to content

Commit

Permalink
Add the ruby platform to the gem version
Browse files Browse the repository at this point in the history
Knowing the exact platform of a compiled gem is important
to download the correct gem and because different binaries have different hashes.

Note that this change makes the gemset platform dependant.
  • Loading branch information
lavoiesl committed Oct 15, 2019
1 parent c5155b9 commit b86d7c5
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 10 deletions.
45 changes: 36 additions & 9 deletions lib/bundix/source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,35 +92,59 @@ def format_hash(hash)
end

def fetch_local_hash(spec)
spec.source.caches.each do |cache|
path = File.join(cache, "#{spec.full_name}.gem")
next unless File.file?(path)
has_platform = spec.platform && spec.platform != Gem::Platform::RUBY
name_version = "#{spec.name}-#{spec.version}"
filename = has_platform ? "#{name_version}-*" : name_version

paths = spec.source.caches.map(&:to_s)
Dir.glob("{#{paths.join(',')}}/#{filename}.gem").each do |path|
if has_platform
# Find first gem that matches the platform
platform = File.basename(path, '.gem')[(name_version.size + 1)..-1]
next unless Gem::Platform.match(platform)
end

hash = nix_prefetch_url(path)[SHA256_32]
return format_hash(hash) if hash
return format_hash(hash), platform if hash
end

nil
end

def fetch_remotes_hash(spec, remotes)
remotes.each do |remote|
hash = fetch_remote_hash(spec, remote)
return remote, format_hash(hash) if hash
hash, platform = fetch_remote_hash(spec, remote)
return remote, format_hash(hash), platform if hash
end

nil
end

def fetch_remote_hash(spec, remote)
has_platform = spec.platform && spec.platform != Gem::Platform::RUBY
if has_platform
# Fetch remote spec to determine the exact platform
# Note that we can't simply use the local platform; the platform of the gem might differ.
# e.g. universal-darwin-14 covers x86_64-darwin-14
spec = spec_for_dependency(remote, spec.name, spec.version)
return unless spec
end

uri = "#{remote}/gems/#{spec.full_name}.gem"
result = nix_prefetch_url(uri)
return unless result
result[SHA256_32]

return result[SHA256_32], spec.platform&.to_s
rescue => e
puts "ignoring error during fetching: #{e}"
puts e.backtrace
nil
end

def spec_for_dependency(remote, name, version)
sources = Gem::SourceList.from([remote])
Gem::SpecFetcher.new(sources).spec_for_dependency(Gem::Dependency.new(name, version)).first&.first&.first
end
end

class Source < Struct.new(:spec, :fetcher)
Expand Down Expand Up @@ -150,11 +174,14 @@ def convert_path

def convert_rubygems
remotes = spec.source.remotes.map{|remote| remote.to_s.sub(/\/+$/, '') }
hash = fetcher.fetch_local_hash(spec)
remote, hash = fetcher.fetch_remotes_hash(spec, remotes) unless hash
hash, platform = fetcher.fetch_local_hash(spec)
remote, hash, platform = fetcher.fetch_remotes_hash(spec, remotes) unless hash
fail "couldn't fetch hash for #{spec.full_name}" unless hash

version = spec.version.to_s
if platform && platform != Gem::Platform::RUBY
version += "-#{platform}"
end

puts "#{hash} => #{spec.name}-#{version}.gem" if $VERBOSE

Expand Down
19 changes: 18 additions & 1 deletion test/convert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@

class TestConvert < Minitest::Test
class PrefetchStub < Bundix::Fetcher
SPECS = {
"sorbet-static" => {
platform: 'java-123',
version: "0.4.4821",
},
}

def nix_prefetch_url(*args)
format_hash(Digest::SHA256.hexdigest(args.to_s))
end
Expand All @@ -18,6 +25,16 @@ def fetch_local_hash(spec)
return nil
end

def spec_for_dependency(remote, name, version)
opts = SPECS[name]
raise "Unexpected spec query: #{name}" unless opts

Gem::Specification.new do |s|
s.name = name
s.version = version
s.platform = Gem::Platform.new(opts[:platform]) if opts[:platform]
end
end
end

def with_gemset(options)
Expand All @@ -40,7 +57,7 @@ def test_bundler_dep
) do |gemset|
assert_equal("0.5.0", gemset.dig("bundler-audit", :version))
assert_equal("0.19.4", gemset.dig("thor", :version))
assert_equal("0.4.4821", gemset.dig("sorbet-static", :version))
assert_equal("0.4.4821-java-unknown", gemset.dig("sorbet-static", :version))
end
end
end

0 comments on commit b86d7c5

Please sign in to comment.