Commit c02bcdab authored by Léo-Paul Géneau's avatar Léo-Paul Géneau 👾

Fix undefined rubygems version

Traceback (most recent call last):
  ...
    recipe.install()
  File "rubygemsrecipe/rubygems.py", line 347, in install
    gem_executable):
  File "rubygemsrecipe/rubygems.py", line 300, in get_dependency_list
    if self.version[0] < '3':
TypeError: 'NoneType' object has no attribute '__getitem__'

The test that was added in 60b10788 is adapted due to the call to subprocess.checkout .
parent 60b10788
...@@ -34,6 +34,7 @@ class Recipe(object): ...@@ -34,6 +34,7 @@ class Recipe(object):
buildout['buildout']['parts-directory'], buildout['buildout']['parts-directory'],
self.name, self.name,
) )
self.bindir = os.path.join(self.options['location'], 'bin')
self.gems = options.get('gems') self.gems = options.get('gems')
if self.gems: if self.gems:
...@@ -42,10 +43,12 @@ class Recipe(object): ...@@ -42,10 +43,12 @@ class Recipe(object):
raise zc.buildout.UserError( raise zc.buildout.UserError(
"Configuration error, 'gems' option is missing") "Configuration error, 'gems' option is missing")
self.version = options.get('version')
self.url = options.get('url') self.url = options.get('url')
# Allow to define specific ruby executable. If not, take just 'ruby' # Allow to define specific ruby executable. If not, take just 'ruby'
self.ruby_executable = options.get('ruby-executable', 'ruby') self.ruby_executable = options.get('ruby-executable', 'ruby')
self.version = self.get_rubygems_version()
if int(self.version.split(".")[0]) < 2:
raise zc.buildout.UserError("Rubygems version must be >= 2.0.0")
deployment = options.get('deployment') deployment = options.get('deployment')
self.deployment = is_true(deployment) if deployment else not allow_picked_versions() self.deployment = is_true(deployment) if deployment else not allow_picked_versions()
...@@ -172,45 +175,29 @@ class Recipe(object): ...@@ -172,45 +175,29 @@ class Recipe(object):
gem_dict['version'] = parsed_gem[1].strip() gem_dict['version'] = parsed_gem[1].strip()
return gem_dict return gem_dict
def _get_latest_rubygems(self): def _get_rubygems_url(self, version):
if self.url: if version:
version = self.version return 'https://rubygems.org/rubygems/rubygems-%s.zip' % version
if not version:
version = (
re.search(r'rubygems-([0-9.]+).zip$', self.url).group(1)
)
return (self.url, version)
if self.version:
return ('https://rubygems.org/rubygems/'
'rubygems-%s.zip' % self.version, self.version)
f = urllib.request.urlopen('https://rubygems.org/pages/download') f = urllib.request.urlopen('https://rubygems.org/pages/download')
s = f.read().decode('utf-8') s = f.read().decode('utf-8')
f.close() f.close()
r = re.search(r'https://rubygems.org/rubygems/' r = re.search(r'https://rubygems.org/rubygems/rubygems-([0-9.]+).zip', s)
r'rubygems-([0-9.]+).zip', s)
if r: if r:
url = r.group(0) return r.group(0)
version = r.group(1)
return (url, version)
else: else:
raise zc.buildout.UserError( raise zc.buildout.UserError(
'Can\'t find latest rubygems version.') 'Can\'t find latest rubygems version.')
def _install_rubygems(self): def _install_rubygems(self):
url, version = self._get_latest_rubygems()
if int(version.split(".")[0]) < 2:
raise zc.buildout.UserError("Rubygems version must be >= 2.0.0")
srcdir = os.path.join(self.buildout['buildout']['parts-directory'], srcdir = os.path.join(self.buildout['buildout']['parts-directory'],
'rubygems-' + version) 'rubygems-' + self.version)
options = { options = {
'url': url, 'url': self.url,
'destination': srcdir, 'destination': srcdir,
} }
recipe = Download(self.buildout, self.name, options) recipe = Download(self.buildout, self.name, options)
recipe.install() recipe.install()
self.version = version
current_dir = os.getcwd() current_dir = os.getcwd()
try: try:
...@@ -324,15 +311,22 @@ class Recipe(object): ...@@ -324,15 +311,22 @@ class Recipe(object):
], ],
} for match in self.gem_regex.findall(cmd_result)] } for match in self.gem_regex.findall(cmd_result)]
def get_rubygems_version(self):
gem_executable = self.get_gem_executable(self.bindir)
if gem_executable:
return self.run([gem_executable, '--version',])
if not self.url:
self.url = self._get_rubygems_url(self.options.get('version'))
return re.search(r'rubygems-([0-9.]+).zip$', self.url).group(1)
def install(self): def install(self):
parts = [self.options['location']] parts = [self.options['location']]
bindir = os.path.join(self.options['location'], 'bin') gem_executable = self.get_gem_executable(self.bindir)
gem_executable = self.get_gem_executable(bindir)
if not gem_executable: if not gem_executable:
self._install_rubygems() self._install_rubygems()
gem_executable = self.get_gem_executable(bindir) gem_executable = self.get_gem_executable(self.bindir)
gem_dict_list = list(map(self.get_gem_dict, self.gems)) gem_dict_list = list(map(self.get_gem_dict, self.gems))
for gem_dict in gem_dict_list: for gem_dict in gem_dict_list:
...@@ -359,11 +353,11 @@ class Recipe(object): ...@@ -359,11 +353,11 @@ class Recipe(object):
constraint, match[0], gem_dict['gemname']) constraint, match[0], gem_dict['gemname'])
self.log.info('installing ruby gem "%s"', gem_dict['gemname']) self.log.info('installing ruby gem "%s"', gem_dict['gemname'])
self._install_gem(gem_dict, gem_executable, bindir) self._install_gem(gem_dict, gem_executable, self.bindir)
for executable in os.listdir(bindir): for executable in os.listdir(self.bindir):
installed_path = self._install_executable( installed_path = self._install_executable(
os.path.join(bindir, executable) os.path.join(self.bindir, executable)
) )
parts.append(installed_path) parts.append(installed_path)
......
...@@ -17,6 +17,8 @@ import zc.buildout ...@@ -17,6 +17,8 @@ import zc.buildout
import rubygems import rubygems
RUBYGEMS_DEFAULT_VERSION = '2.0.0'
def touch(path): def touch(path):
with path.open('w') as f: with path.open('w') as f:
...@@ -24,9 +26,31 @@ def touch(path): ...@@ -24,9 +26,31 @@ def touch(path):
class fixture(object): class fixture(object):
def __init__(self, options=None, version='2.0.0'): def __init__(self, options=None, version=RUBYGEMS_DEFAULT_VERSION,
rubygems_installed=False):
self.options = options or {} self.options = options or {}
self.version = version self.version = self.options.get('return', {}).get('version', version)
self.to_patch = (
('check_output', 'rubygems.subprocess.check_output'),
('urlopen', 'rubygems.urllib.request.urlopen'),
('download', 'rubygems.Download'),
)
self.patch_return_value_dict = {
'urlopen': BytesIO(bytes(
('https://rubygems.org/rubygems/rubygems-%s.zip'
% RUBYGEMS_DEFAULT_VERSION).encode('utf-8')
)),
}
self.to_create = ()
if rubygems_installed:
self.to_patch.append(
('get_version', 'rubygems.Recipe.get_rubygems_version'))
self.patch_return_value_dict['get_version'] = self.version
self.to_create.append(path / 'rubygems/bin/gem')
def __call__(self, func): def __call__(self, func):
@functools.wraps(func) @functools.wraps(func)
...@@ -53,24 +77,21 @@ class fixture(object): ...@@ -53,24 +77,21 @@ class fixture(object):
def set_up(self): def set_up(self):
name = 'rubygems' name = 'rubygems'
version = self.options.get('return', {}).get('version', self.version)
self.patch(( self.patch(self.to_patch)
('check_output', 'rubygems.subprocess.check_output'), for k, v in self.patch_return_value_dict.items():
('urlopen', 'rubygems.urllib.request.urlopen'), self.patches[k].return_value = v
('download', 'rubygems.Download'),
))
self.patches['urlopen'].return_value = BytesIO(
b'https://rubygems.org/rubygems/rubygems-2.0.0.zip'
)
self.makedirs(( self.makedirs((
'bin', 'bin',
'ruby-' + version, 'ruby-' + self.version,
'rubygems-' + version, 'rubygems-' + self.version,
'rubygems/bin', 'rubygems/bin',
)) ))
for file_path in self.to_create:
touch(file_path)
buildout = {'buildout': dict({ buildout = {'buildout': dict({
'parts-directory': str(self.tempdir), 'parts-directory': str(self.tempdir),
'bin-directory': str(self.tempdir / 'bin'), 'bin-directory': str(self.tempdir / 'bin'),
...@@ -78,7 +99,7 @@ class fixture(object): ...@@ -78,7 +99,7 @@ class fixture(object):
options = self.options.get('recipe', {}) options = self.options.get('recipe', {})
return buildout, name, options, version return buildout, name, options, self.version
def tear_down(self): def tear_down(self):
for patcher in self.patchers.values(): for patcher in self.patchers.values():
...@@ -108,8 +129,9 @@ class RubyGemsTestCase(unittest.TestCase): ...@@ -108,8 +129,9 @@ class RubyGemsTestCase(unittest.TestCase):
args = patches['download'].mock_calls[0][1] args = patches['download'].mock_calls[0][1]
self.assertEqual(args[2], { self.assertEqual(args[2], {
'url': 'https://rubygems.org/rubygems/rubygems-2.0.0.zip', 'url': 'https://rubygems.org/rubygems/rubygems-%s.zip'
'destination': str(path / 'rubygems-2.0.0'), % RUBYGEMS_DEFAULT_VERSION,
'destination': str(path / ('rubygems-' + RUBYGEMS_DEFAULT_VERSION)),
}) })
expected_install_arg_list_list.insert(0, [ expected_install_arg_list_list.insert(0, [
...@@ -193,13 +215,14 @@ class RubyGemsDefaultTestCase(RubyGemsTestCase): ...@@ -193,13 +215,14 @@ class RubyGemsDefaultTestCase(RubyGemsTestCase):
@fixture({'recipe': { @fixture({'recipe': {
'gems': 'sass', 'gems': 'sass',
'url': 'https://rubygems.org/rubygems/rubygems-2.0.0.zip', 'url': 'https://rubygems.org/rubygems/rubygems-%s.zip'
% RUBYGEMS_DEFAULT_VERSION,
}}) }})
def test_version_from_url(self, path, patches, buildout, name, options, version): def test_version_from_url(self, path, patches, buildout, name, options, version):
recipe = rubygems.Recipe(buildout, name, options) recipe = rubygems.Recipe(buildout, name, options)
recipe.install() recipe.install()
@fixture({'recipe': {'gems': 'sass'}, 'version': '2.0.0'}) @fixture({'recipe': {'gems': 'sass'}, 'version': RUBYGEMS_DEFAULT_VERSION})
def test_version(self, path, patches, buildout, name, options, version): def test_version(self, path, patches, buildout, name, options, version):
recipe = rubygems.Recipe(buildout, name, options) recipe = rubygems.Recipe(buildout, name, options)
recipe.install() recipe.install()
...@@ -207,10 +230,10 @@ class RubyGemsDefaultTestCase(RubyGemsTestCase): ...@@ -207,10 +230,10 @@ class RubyGemsDefaultTestCase(RubyGemsTestCase):
@fixture({'recipe': {'gems': 'sass'}}) @fixture({'recipe': {'gems': 'sass'}})
def test_no_version(self, path, patches, buildout, name, options, version): def test_no_version(self, path, patches, buildout, name, options, version):
patches['urlopen'].return_value = BytesIO(b'') patches['urlopen'].return_value = BytesIO(b'')
recipe = rubygems.Recipe(buildout, name, options)
self.assertRaisesRegexp(zc.buildout.UserError, self.assertRaisesRegexp(zc.buildout.UserError,
'Can\'t find latest rubygems version.', 'Can\'t find latest rubygems version.',
recipe.install rubygems.Recipe,
buildout, name, options
) )
@fixture({'recipe': {'gems': 'sass'}}) @fixture({'recipe': {'gems': 'sass'}})
...@@ -230,10 +253,9 @@ class RubyGemsDefaultTestCase(RubyGemsTestCase): ...@@ -230,10 +253,9 @@ class RubyGemsDefaultTestCase(RubyGemsTestCase):
touch(pathlib.Path(recipe.options['location']) / 'bin/sass') touch(pathlib.Path(recipe.options['location']) / 'bin/sass')
recipe.install() recipe.install()
@fixture({'recipe': {'gems': 'sass==1.0'}}) @fixture({'recipe': {'gems': 'sass==1.0'}, 'rubygems_installed': True})
def test_pinned_versions(self, path, patches, buildout, name, options, version): def test_pinned_versions(self, path, patches, buildout, name, options, version):
recipe = rubygems.Recipe(buildout, name, options) recipe = rubygems.Recipe(buildout, name, options)
touch(path / 'rubygems/bin/gem')
recipe.install() recipe.install()
args = patches['check_output'].mock_calls[0][1] args = patches['check_output'].mock_calls[0][1]
...@@ -302,10 +324,12 @@ class RubyGemsDeploymentTestCase(RubyGemsTestCase): ...@@ -302,10 +324,12 @@ class RubyGemsDeploymentTestCase(RubyGemsTestCase):
self.install_with_default_rubygems_test( self.install_with_default_rubygems_test(
path, patches, expected_install_arg_list_list) path, patches, expected_install_arg_list_list)
@deployment_fixture({'recipe': {'gems': 'hashie==0.3.1'}}) @deployment_fixture({
'recipe': {'gems': 'hashie==0.3.1'},
'rubygems_installed': True
})
def test_already_installed_rubygems( def test_already_installed_rubygems(
self, path, patches, buildout, name, options, version): self, path, patches, buildout, name, options, version):
touch(path / 'rubygems/bin/gem')
self.deployment_install_test( self.deployment_install_test(
buildout, name, path, patches, options, version) buildout, name, path, patches, options, version)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment