Skip to content

Commit

Permalink
Fixes a bug related to legacy gems.
Browse files Browse the repository at this point in the history
fixes: #209
  • Loading branch information
mdellweg authored and dkliban committed Feb 8, 2024
1 parent e53cda4 commit e83b756
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGES/209.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a bug where legacy gems would cause an exception in pulpcore-content.
40 changes: 39 additions & 1 deletion pulp_gem/specs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections import namedtuple
from logging import getLogger

import aiofiles
import datetime
Expand All @@ -14,6 +15,8 @@
import rubymarshal.reader


log = getLogger(__name__)

NAME_REGEX = re.compile(r"[\w\.-]+")
VERSION_REGEX = re.compile(r"\d+(?:\.\d+)*")
PRERELEASE_VERSION_REGEX = NAME_REGEX
Expand Down Expand Up @@ -274,6 +277,41 @@ def yaml_constructor(cls, loader, node):
result.attributes = {f"@{key}": value for key, value in values.items()}
result.attributes["@type"] = rubymarshal.classes.Symbol(result.attributes["@type"][1:])

@property
def requirement(self):
# From:
# File lib/rubygems/dependency.rb, line 116

# @version_requirements and @version_requirement are legacy ivar
# names, and supported here because older gems need to keep
# working and Dependency doesn't implement marshal_dump and
# marshal_load. In a happier world, this would be an
# attr_accessor. The horrifying instance_variable_get you see
# below is also the legacy of some old restructurings.
#
# Note also that because of backwards compatibility (loading new
# gems in an old RubyGems installation), we can't add explicit
# marshaling to this class until we want to make a big
# break. Maybe 2.0.
#
# Children, define explicit marshal and unmarshal behavior for
# public classes. Marshal formats are part of your public API.

if result := self.attributes.get("@requirement"):
return result
if version_requirement := self.attributes.pop("@version_requirement", None):
log.warn(
"You are attempting to process a really old gem. "
"If this codepath fails, please report an issue with this gem "
"so we actually have something to experiment on."
)
version = GemVersion()
version.marshal_load([version_requirement.attributes["@version"]])
requirement = GemRequirement()
requirement._private_data[0] = [[">=", version]]
self.attributes["@version_requirements"] = requirement
return self.attributes.get("@version_requirements")


class RubyTime(rubymarshal.classes.UserDef):
ruby_class_name = "Time"
Expand Down Expand Up @@ -352,7 +390,7 @@ def analyse_gem(file_obj):
gem_info[key] = requirement.to_s()
if (dependencies := data._private_data.get("dependencies")) is not None:
gem_info["dependencies"] = {
dep.attributes["@name"]: dep.attributes["@requirement"].to_s() for dep in dependencies
dep.attributes["@name"]: dep.requirement.to_s() for dep in dependencies
}
zdata = zlib.compress(rubymarshal.writer.writes(data))
return gem_info, zdata
18 changes: 11 additions & 7 deletions pulp_gem/tests/functional/api/test_pull_through.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,22 @@ def test_pull_through_install(
remote2 = gem_remote_factory()
distribution2 = gem_distribution_factory(remote=remote2.pulp_href)

for dis, gem in ((distribution, "a"), (distribution2, "beryl")):
cmd = ["gem", "i", "--remote", "--clear-sources", "-s", dis.base_url, gem, "-v", "0.1.0"]
for dis, gem, version in (
(distribution, "a", "0.1.0"),
(distribution2, "beryl", "0.1.0"),
(distribution, "ruby-hmac", "0.4.0"),
):
cmd = ["gem", "i", "--remote", "--clear-sources", "-s", dis.base_url, gem, "-v", version]

out = subprocess.run(cmd, stdout=subprocess.PIPE)
assert f"Successfully installed {gem}-0.1.0" in out.stdout.decode("utf-8")
assert f"Successfully installed {gem}-{version}" in out.stdout.decode("utf-8")

r = gem_content_api_client.list(name=gem, version="0.1.0")
r = gem_content_api_client.list(name=gem, version=version)
assert r.count == 1
assert r.results[0].name == gem
assert r.results[0].version == "0.1.0"
assert r.results[0].version == version

subprocess.run(("gem", "uninstall", gem, "-v", "0.1.0"))
subprocess.run(("gem", "uninstall", gem, "-v", version))

content_after = gem_content_api_client.list().count
assert content_before + 2 == content_after
assert content_before + 3 == content_after

0 comments on commit e83b756

Please sign in to comment.