Skip to content

Commit c77ae2f

Browse files
committed
packagedcode: fix gemspec version constants being stored as-is
When a gemspec uses a Ruby constant for the version field like: s.version = Elasticsearch::API::VERSION s.version = Faraday::VERSION scancode was storing the constant name as the version string. These constants cannot be resolved without executing Ruby code. Add is_ruby_version_constant() to detect Ruby constant expressions (containing :: namespace separator or bare uppercase constant names) and return None for the version instead of storing an unresolvable constant string. Also fixes the download_url and api_data_url which were generating invalid URLs with the constant name embedded. Fixes #3129 Signed-off-by: kumarasantosh <santosh.pulikond02@gmail.com>
1 parent d320c97 commit c77ae2f

8 files changed

Lines changed: 222 additions & 0 deletions

File tree

src/packagedcode/rubygems.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,7 @@ def party_mapper(role, names=[], emails=[]):
706706
models.Party(type=models.party_person, email=email, role=role)
707707
for email in emails
708708
)
709+
return ()
709710

710711

711712
def get_parties(gem_data):

src/packagedcode/spec.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,40 @@ def get_authors(line):
133133
}
134134

135135

136+
def is_ruby_version_constant(value):
137+
"""
138+
Return True if value looks like a Ruby constant expression
139+
that cannot be resolved statically, such as:
140+
Elasticsearch::API::VERSION or MyGem::VERSION
141+
142+
These are dynamic values that reference Ruby constants
143+
and cannot be determined without executing the Ruby code.
144+
145+
For example:
146+
>>> is_ruby_version_constant('Elasticsearch::API::VERSION')
147+
True
148+
>>> is_ruby_version_constant('MyGem::VERSION')
149+
True
150+
>>> is_ruby_version_constant('1.0.0')
151+
False
152+
>>> is_ruby_version_constant("'2.3.4'")
153+
False
154+
>>> is_ruby_version_constant(None)
155+
False
156+
"""
157+
if not value:
158+
return False
159+
# Ruby constants use :: as namespace separator
160+
if '::' in value:
161+
return True
162+
# A bare constant starts with uppercase and has no dots/quotes
163+
# e.g. VERSION (unlikely but possible)
164+
stripped = value.strip('\'"')
165+
if stripped and stripped[0].isupper() and '.' not in stripped:
166+
return True
167+
return False
168+
169+
136170
def parse_spec(location, package_type):
137171
"""
138172
Return a mapping of data parsed from a podspec/gemspec/Pofile/Gemfile file
@@ -151,6 +185,10 @@ def parse_spec(location, package_type):
151185
parsed = parser(line=line)
152186
if parsed:
153187
spec_data[attribute_name] = parsed
188+
189+
version = spec_data.get('version')
190+
if is_ruby_version_constant(version):
191+
spec_data['version'] = None
154192

155193
# description can be in single or multi-lines
156194
# There are many different ways to write description.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Licensed to Elasticsearch B.V. under one or more contributor
2+
# license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright
4+
# ownership. Elasticsearch B.V. licenses this file to you under
5+
# the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# coding: utf-8
19+
lib = File.expand_path('../lib', __FILE__)
20+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
21+
require 'elasticsearch/api/version'
22+
23+
Gem::Specification.new do |s|
24+
s.name = 'elasticsearch-api'
25+
s.version = Elasticsearch::API::VERSION
26+
s.authors = ['Karel Minarik']
27+
s.email = ['karel.minarik@elasticsearch.org']
28+
s.summary = 'Ruby API for Elasticsearch.'
29+
s.homepage = 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current/index.html'
30+
s.license = 'Apache-2.0'
31+
s.metadata = {
32+
'homepage_uri' => 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current/index.html',
33+
'changelog_uri' => 'https://github.com/elastic/elasticsearch-ruby/blob/main/CHANGELOG.md',
34+
'source_code_uri' => 'https://github.com/elastic/elasticsearch-ruby/tree/main/elasticsearch-api',
35+
'bug_tracker_uri' => 'https://github.com/elastic/elasticsearch-ruby/issues'
36+
}
37+
s.files = `git ls-files`.split($/)
38+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
39+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
40+
s.require_paths = ['lib']
41+
42+
s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
43+
s.rdoc_options = ['--charset=UTF-8']
44+
45+
s.required_ruby_version = '>= 2.5'
46+
47+
s.add_dependency 'multi_json'
48+
49+
s.add_development_dependency 'ansi'
50+
s.add_development_dependency 'bundler'
51+
s.add_development_dependency 'elasticsearch'
52+
s.add_development_dependency 'minitest'
53+
s.add_development_dependency 'minitest-reporters'
54+
s.add_development_dependency 'mocha'
55+
s.add_development_dependency 'pry'
56+
s.add_development_dependency 'rake'
57+
s.add_development_dependency 'shoulda-context'
58+
s.add_development_dependency 'yard'
59+
60+
# Gems for testing integrations
61+
s.add_development_dependency 'jsonify'
62+
s.add_development_dependency 'hashie'
63+
# Temporary support for Ruby 2.6, since it's EOL March 2022:
64+
if RUBY_VERSION < '2.7.0'
65+
s.add_development_dependency 'jbuilder', '< 7.0.0'
66+
else
67+
s.add_development_dependency 'activesupport'
68+
s.add_development_dependency 'jbuilder'
69+
end
70+
71+
s.add_development_dependency 'cane'
72+
s.add_development_dependency 'escape_utils' unless defined? JRUBY_VERSION
73+
74+
s.add_development_dependency 'require-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius)
75+
s.add_development_dependency 'ruby-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius)
76+
s.add_development_dependency 'simplecov'
77+
78+
s.add_development_dependency 'test-unit', '~> 2'
79+
80+
s.description = <<-DESC.gsub(/^ /, '')
81+
Ruby API for Elasticsearch. See the `elasticsearch` gem for full integration.
82+
DESC
83+
end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
2+
require 'excon/version'
3+
4+
Gem::Specification.new do |s|
5+
s.name = 'excon'
6+
s.version = Excon::VERSION
7+
s.summary = "speed, persistence, http(s)"
8+
s.description = "EXtended http(s) CONnections"
9+
s.authors = ["dpiddy (Dan Peterson)", "geemus (Wesley Beary)", "nextmat (Matt Sanders)"]
10+
s.email = 'geemus@gmail.com'
11+
s.homepage = 'https://github.com/excon/excon'
12+
s.license = 'MIT'
13+
s.rdoc_options = ["--charset=UTF-8"]
14+
s.extra_rdoc_files = %w[README.md CONTRIBUTORS.md CONTRIBUTING.md]
15+
s.files = `git ls-files -- data/* lib/*`.split("\n") + [
16+
"CONTRIBUTING.md",
17+
"CONTRIBUTORS.md",
18+
"LICENSE.md",
19+
"README.md",
20+
"excon.gemspec"
21+
]
22+
23+
s.add_development_dependency('rspec', '>= 3.5.0')
24+
s.add_development_dependency('activesupport')
25+
s.add_development_dependency('delorean')
26+
s.add_development_dependency('eventmachine', '>= 1.0.4')
27+
s.add_development_dependency('open4')
28+
s.add_development_dependency('rake')
29+
s.add_development_dependency('rdoc')
30+
s.add_development_dependency('shindo')
31+
s.add_development_dependency('sinatra')
32+
s.add_development_dependency('sinatra-contrib')
33+
s.add_development_dependency('json', '>= 1.8.5')
34+
s.add_development_dependency('puma')
35+
s.add_development_dependency('webrick')
36+
37+
s.metadata = {
38+
'homepage_uri' => 'https://github.com/excon/excon',
39+
'bug_tracker_uri' => 'https://github.com/excon/excon/issues',
40+
'changelog_uri' => 'https://github.com/excon/excon/blob/master/changelog.txt',
41+
'documentation_uri' => 'https://github.com/excon/excon/blob/master/README.md',
42+
'source_code_uri' => 'https://github.com/excon/excon',
43+
'wiki_uri' => 'https://github.com/excon/excon/wiki'
44+
}
45+
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
404: Not Found
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Gem::Specification.new do |s|
2+
s.name = 'my-gem'
3+
s.version = MyGem::VERSION
4+
s.summary = 'A gem with a version constant'
5+
s.license = 'MIT'
6+
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Gem::Specification.new do |s|
2+
s.name = 'my-gem'
3+
s.version = '1.2.3'
4+
s.summary = 'A gem with a real version'
5+
s.license = 'MIT'
6+
end

tests/packagedcode/test_rubygems.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,56 @@
1515
from commoncode.testcase import FileBasedTesting
1616

1717
from packagedcode import rubygems
18+
from packagedcode import spec
1819
from packages_test_utils import PackageTester
1920
from scancode_config import REGEN_TEST_FIXTURES
2021

22+
REGEN_TEST_FIXTURES = False
23+
2124
# TODO: Add test with https://rubygems.org/gems/pbox2d/versions/1.0.3-java
2225
# this is a multiple personality package (Java and Ruby)
2326
# see also https://rubygems.org/downloads/jaro_winkler-1.5.1-java.gem
2427

2528

29+
class TestGemspecVersionConstant(PackageTester):
30+
test_data_dir = os.path.join(os.path.dirname(__file__), 'data')
31+
32+
def test_version_constant_returns_none_for_elasticsearch(self):
33+
test_file = self.get_test_loc('rubygems/version-constant/elasticsearch-api.gemspec')
34+
packages = list(rubygems.GemspecHandler.parse(test_file))
35+
assert packages
36+
pkg = packages[0]
37+
assert pkg.name == 'elasticsearch-api'
38+
assert pkg.version is None
39+
assert 'Elasticsearch' not in str(pkg.version)
40+
assert pkg.download_url is None
41+
42+
def test_version_constant_returns_none_for_simple_constant(self):
43+
test_file = self.get_test_loc('rubygems/version-constant/simple-constant.gemspec')
44+
packages = list(rubygems.GemspecHandler.parse(test_file))
45+
assert packages
46+
pkg = packages[0]
47+
assert pkg.name == 'my-gem'
48+
assert pkg.version is None
49+
50+
def test_real_version_is_preserved(self):
51+
test_file = self.get_test_loc('rubygems/version-constant/simple-version.gemspec')
52+
packages = list(rubygems.GemspecHandler.parse(test_file))
53+
assert packages
54+
pkg = packages[0]
55+
assert pkg.name == 'my-gem'
56+
assert pkg.version == '1.2.3'
57+
58+
def test_is_ruby_version_constant_function(self):
59+
assert spec.is_ruby_version_constant('Elasticsearch::API::VERSION') is True
60+
assert spec.is_ruby_version_constant('MyGem::VERSION') is True
61+
assert spec.is_ruby_version_constant('Faraday::VERSION') is True
62+
assert spec.is_ruby_version_constant('1.0.0') is False
63+
assert spec.is_ruby_version_constant("'2.3.4'") is False
64+
assert spec.is_ruby_version_constant(None) is False
65+
assert spec.is_ruby_version_constant('') is False
66+
67+
2668
class TestGemSpec(PackageTester):
2769
test_data_dir = os.path.join(os.path.dirname(__file__), 'data')
2870

0 commit comments

Comments
 (0)