From 839a1f4a9a723e642ac4a1b26154bbb8f49e488a Mon Sep 17 00:00:00 2001 From: Arthur Zamarin Date: Thu, 29 Jun 2023 21:27:16 +0300 Subject: [PATCH] RubyCompatCheck: new check for new USE_RUBY compatible values Resolves: https://github.com/pkgcore/pkgcheck/issues/304 Signed-off-by: Arthur Zamarin --- src/pkgcheck/checks/ruby.py | 109 ++++++++++++++++++ .../RubyCompatUpdate/expected.json | 2 + .../RubyCompatUpdate-0.ebuild | 17 +++ .../RubyCompatUpdate-1.ebuild | 17 +++ .../RubyCompatUpdate-2.ebuild | 17 +++ .../python/dev-lang/ruby/ruby-2.7.ebuild | 6 + .../python/dev-lang/ruby/ruby-3.0.ebuild | 6 + .../python/dev-lang/ruby/ruby-3.1.ebuild | 6 + .../python/dev-lang/ruby/ruby-3.2.ebuild | 6 + testdata/repos/python/eclass/ruby-ng.eclass | 83 +++++++++++++ .../python/profiles/desc/ruby_targets.desc | 5 + .../stub/ruby-dep-old/ruby-dep-old-0.ebuild | 14 +++ .../python/stub/ruby-dep/ruby-dep-0.ebuild | 14 +++ 13 files changed, 302 insertions(+) create mode 100644 src/pkgcheck/checks/ruby.py create mode 100644 testdata/data/repos/python/RubyCompatCheck/RubyCompatUpdate/expected.json create mode 100644 testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-0.ebuild create mode 100644 testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-1.ebuild create mode 100644 testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-2.ebuild create mode 100644 testdata/repos/python/dev-lang/ruby/ruby-2.7.ebuild create mode 100644 testdata/repos/python/dev-lang/ruby/ruby-3.0.ebuild create mode 100644 testdata/repos/python/dev-lang/ruby/ruby-3.1.ebuild create mode 100644 testdata/repos/python/dev-lang/ruby/ruby-3.2.ebuild create mode 100644 testdata/repos/python/eclass/ruby-ng.eclass create mode 100644 testdata/repos/python/profiles/desc/ruby_targets.desc create mode 100644 testdata/repos/python/stub/ruby-dep-old/ruby-dep-old-0.ebuild create mode 100644 testdata/repos/python/stub/ruby-dep/ruby-dep-0.ebuild diff --git a/src/pkgcheck/checks/ruby.py b/src/pkgcheck/checks/ruby.py new file mode 100644 index 000000000..d7cbb914e --- /dev/null +++ b/src/pkgcheck/checks/ruby.py @@ -0,0 +1,109 @@ +import itertools + +from pkgcore.ebuild.atom import atom +from snakeoil.sequences import iflatten_instance +from snakeoil.strings import pluralism + +from .. import results +from . import Check + + +IUSE_PREFIX = "ruby_targets_" + + +class RubyCompatUpdate(results.VersionResult, results.Info): + """``USE_RUBY`` can be updated to support newer ruby version(s).""" + + def __init__(self, updates, **kwargs): + super().__init__(**kwargs) + self.updates = tuple(updates) + + @property + def desc(self): + s = pluralism(self.updates) + updates = ", ".join(self.updates) + return f"USE_RUBY update{s} available: {updates}" + + +class RubyCompatCheck(Check): + """Check ruby ebuilds for possible ``USE_RUBY`` updates. + + Supports ebuilds inheriting ``ruby-ng``. + """ + + known_results = frozenset({RubyCompatUpdate}) + + whitelist_categories = frozenset({"virtual"}) + + def __init__(self, *args): + super().__init__(*args) + repo = self.options.target_repo + # sorter for ruby targets leveraging USE_EXPAND flag ordering from repo + self.sorter = repo.use_expand_sorter("ruby_targets") + + # determine available USE_RUBY use flags + targets = [] + for target, _desc in repo.use_expand_desc.get(IUSE_PREFIX[:-1], ()): + if target[len(IUSE_PREFIX) :].startswith("ruby"): + targets.append(target[len(IUSE_PREFIX) :]) + self.multi_targets = tuple(sorted(targets, key=self.sorter)) + + def ruby_deps(self, deps, prefix): + for dep in (x for x in deps if x.use): + for x in dep.use: + if x.startswith(("-", "!")): + continue + if x.startswith(prefix): + yield dep.no_usedeps + break + + def deps(self, pkg): + """Set of dependencies for a given package's attributes.""" + return { + p + for attr in (x.lower() for x in pkg.eapi.dep_keys) + for p in iflatten_instance(getattr(pkg, attr), atom) + if not p.blocks + } + + def feed(self, pkg): + if pkg.category in self.whitelist_categories or "ruby-ng" not in pkg.inherited: + return + + deps = self.deps(pkg) + + try: + # determine the latest supported ruby version + latest_target = sorted( + ( + f"ruby{x.slot.replace('.', '')}" + for x in deps + if x.key == "dev-lang/ruby" and x.slot is not None + ), + key=self.sorter, + )[-1] + except IndexError: + return + + # determine ruby impls to target + targets = set( + itertools.takewhile(lambda x: x != latest_target, reversed(self.multi_targets)) + ) + + if targets: + try: + # determine if deps support missing ruby targets + for dep in self.ruby_deps(deps, IUSE_PREFIX): + # TODO: use query caching for repo matching? + latest = sorted(self.options.search_repo.match(dep))[-1] + targets.intersection_update( + f"ruby{x.rsplit('ruby', 1)[-1]}" + for x in latest.iuse_stripped + if x.startswith(IUSE_PREFIX) + ) + if not targets: + return + except IndexError: + return + + yield RubyCompatUpdate(sorted(targets, key=self.sorter), pkg=pkg) diff --git a/testdata/data/repos/python/RubyCompatCheck/RubyCompatUpdate/expected.json b/testdata/data/repos/python/RubyCompatCheck/RubyCompatUpdate/expected.json new file mode 100644 index 000000000..b5253f792 --- /dev/null +++ b/testdata/data/repos/python/RubyCompatCheck/RubyCompatUpdate/expected.json @@ -0,0 +1,2 @@ +{"__class__": "RubyCompatUpdate", "category": "RubyCompatCheck", "package": "RubyCompatUpdate", "version": "0", "updates": ["ruby31", "ruby32"]} +{"__class__": "RubyCompatUpdate", "category": "RubyCompatCheck", "package": "RubyCompatUpdate", "version": "1", "updates": ["ruby32"]} diff --git a/testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-0.ebuild b/testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-0.ebuild new file mode 100644 index 000000000..8f6f1da48 --- /dev/null +++ b/testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-0.ebuild @@ -0,0 +1,17 @@ +EAPI=7 + +USE_RUBY="ruby27 ruby30" +inherit ruby-ng + +DESCRIPTION="Ebuild with potential USE_RUBY updates" +HOMEPAGE="https://github.com/pkgcore/pkgcheck" +LICENSE="BSD" +SLOT="0" + +RDEPEND=" + stub/stub2 +" + +ruby_add_rdepend " + stub/ruby-dep +" diff --git a/testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-1.ebuild b/testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-1.ebuild new file mode 100644 index 000000000..8af80e699 --- /dev/null +++ b/testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-1.ebuild @@ -0,0 +1,17 @@ +EAPI=7 + +USE_RUBY="ruby27 ruby30 ruby31" +inherit ruby-ng + +DESCRIPTION="Ebuild with potential USE_RUBY updates" +HOMEPAGE="https://github.com/pkgcore/pkgcheck" +LICENSE="BSD" +SLOT="0" + +RDEPEND=" + stub/stub2 +" + +ruby_add_depend " + stub/ruby-dep +" diff --git a/testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-2.ebuild b/testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-2.ebuild new file mode 100644 index 000000000..bb2e95d30 --- /dev/null +++ b/testdata/repos/python/RubyCompatCheck/RubyCompatUpdate/RubyCompatUpdate-2.ebuild @@ -0,0 +1,17 @@ +EAPI=7 + +USE_RUBY="ruby27 ruby30" +inherit ruby-ng + +DESCRIPTION="Ebuild without potential USE_RUBY updates" +HOMEPAGE="https://github.com/pkgcore/pkgcheck" +LICENSE="BSD" +SLOT="0" + +RDEPEND=" + stub/stub2 +" + +ruby_add_rdepend " + stub/ruby-dep-old +" diff --git a/testdata/repos/python/dev-lang/ruby/ruby-2.7.ebuild b/testdata/repos/python/dev-lang/ruby/ruby-2.7.ebuild new file mode 100644 index 000000000..6192e6258 --- /dev/null +++ b/testdata/repos/python/dev-lang/ruby/ruby-2.7.ebuild @@ -0,0 +1,6 @@ +EAPI=7 + +DESCRIPTION="Stub ebuild for ruby interpreter" +HOMEPAGE="https://github.com/pkgcore/pkgcheck" +LICENSE="BSD" +SLOT="2.7" diff --git a/testdata/repos/python/dev-lang/ruby/ruby-3.0.ebuild b/testdata/repos/python/dev-lang/ruby/ruby-3.0.ebuild new file mode 100644 index 000000000..452b04e71 --- /dev/null +++ b/testdata/repos/python/dev-lang/ruby/ruby-3.0.ebuild @@ -0,0 +1,6 @@ +EAPI=7 + +DESCRIPTION="Stub ebuild for ruby interpreter" +HOMEPAGE="https://github.com/pkgcore/pkgcheck" +LICENSE="BSD" +SLOT="3.0" diff --git a/testdata/repos/python/dev-lang/ruby/ruby-3.1.ebuild b/testdata/repos/python/dev-lang/ruby/ruby-3.1.ebuild new file mode 100644 index 000000000..570b17099 --- /dev/null +++ b/testdata/repos/python/dev-lang/ruby/ruby-3.1.ebuild @@ -0,0 +1,6 @@ +EAPI=7 + +DESCRIPTION="Stub ebuild for ruby interpreter" +HOMEPAGE="https://github.com/pkgcore/pkgcheck" +LICENSE="BSD" +SLOT="3.1" diff --git a/testdata/repos/python/dev-lang/ruby/ruby-3.2.ebuild b/testdata/repos/python/dev-lang/ruby/ruby-3.2.ebuild new file mode 100644 index 000000000..9bbb0f658 --- /dev/null +++ b/testdata/repos/python/dev-lang/ruby/ruby-3.2.ebuild @@ -0,0 +1,6 @@ +EAPI=7 + +DESCRIPTION="Stub ebuild for ruby interpreter" +HOMEPAGE="https://github.com/pkgcore/pkgcheck" +LICENSE="BSD" +SLOT="3.2" diff --git a/testdata/repos/python/eclass/ruby-ng.eclass b/testdata/repos/python/eclass/ruby-ng.eclass new file mode 100644 index 000000000..082c98f04 --- /dev/null +++ b/testdata/repos/python/eclass/ruby-ng.eclass @@ -0,0 +1,83 @@ +_ruby_implementation_depend() { + local rubypn= rubyslot= + + case $1 in + ruby27) rubypn="dev-lang/ruby" rubyslot=":2.7" ;; + ruby30) rubypn="dev-lang/ruby" rubyslot=":3.0" ;; + ruby31) rubypn="dev-lang/ruby" rubyslot=":3.1" ;; + ruby32) rubypn="dev-lang/ruby" rubyslot=":3.2" ;; + *) die "$1: unknown Ruby implementation" + esac + + echo "$2${rubypn}$3${rubyslot}" +} + +_ruby_implementations_depend() { + local depend _ruby_implementation + for _ruby_implementation in "${_RUBY_GET_ALL_IMPLS[@]}"; do + depend="${depend}${depend+ }ruby_targets_${_ruby_implementation}? ( $(_ruby_implementation_depend $_ruby_implementation) )" + done + DEPEND="${depend}" + IUSE="${_RUBY_GET_ALL_IMPLS[*]/#/ruby_targets_}" + REQUIRED_USE="|| ( ${IUSE} )" +} + +_ruby_atoms_samelib_generic() { + local shopt_save=$(shopt -p -o noglob) + echo "RUBYTARGET? (" + for token in $*; do + case "$token" in + "||" | "(" | ")" | *"?") + echo "${token}" ;; + *]) + echo "${token%[*}[RUBYTARGET(-),${token/*[}" ;; + *) + echo "${token}[RUBYTARGET(-)]" ;; + esac + done + echo ")" + ${shopt_save} +} + +_ruby_atoms_samelib() { + local atoms=$(_ruby_atoms_samelib_generic "$*") + for _ruby_implementation in "${_RUBY_GET_ALL_IMPLS[@]}"; do + echo "${atoms//RUBYTARGET/ruby_targets_${_ruby_implementation}}" + done +} + +_ruby_get_all_impls() { + _RUBY_GET_ALL_IMPLS=() + + local i found_valid_impl + for i in ${USE_RUBY}; do + case ${i} in + # removed implementations + ruby19|ruby2[0-7]|jruby) + ;; + *) + found_valid_impl=1 + _RUBY_GET_ALL_IMPLS+=( ${i} ) + ;; + esac + done + + if [[ -z ${found_valid_impl} ]] ; then + die "No supported implementation in USE_RUBY." + fi +} + +ruby_add_depend() { + DEPEND+=" $(_ruby_atoms_samelib "$1")" +} + +ruby_add_bdepend() { + BDEPEND+=" $(_ruby_atoms_samelib "$1")" +} + +ruby_add_rdepend() { + RDEPEND+=" $(_ruby_atoms_samelib "$1")" +} + +_ruby_get_all_impls +_ruby_implementations_depend diff --git a/testdata/repos/python/profiles/desc/ruby_targets.desc b/testdata/repos/python/profiles/desc/ruby_targets.desc new file mode 100644 index 000000000..59c3dec7a --- /dev/null +++ b/testdata/repos/python/profiles/desc/ruby_targets.desc @@ -0,0 +1,5 @@ +# available RUBY_TARGETS USE_EXPAND flags +ruby27 - Build with MRI Ruby 2.7.x +ruby30 - Build with MRI Ruby 3.0.x +ruby31 - Build with MRI Ruby 3.1.x +ruby32 - Build with MRI Ruby 3.2.x diff --git a/testdata/repos/python/stub/ruby-dep-old/ruby-dep-old-0.ebuild b/testdata/repos/python/stub/ruby-dep-old/ruby-dep-old-0.ebuild new file mode 100644 index 000000000..ec9071c36 --- /dev/null +++ b/testdata/repos/python/stub/ruby-dep-old/ruby-dep-old-0.ebuild @@ -0,0 +1,14 @@ +EAPI=7 + +USE_RUBY="ruby27 ruby30" + +inherit ruby-ng + +DESCRIPTION="Stub ebuild with old USE_RUBY support" +HOMEPAGE="https://github.com/pkgcore/pkgcheck" +LICENSE="BSD" +SLOT="0" + +RDEPEND=" + stub/stub2 +" diff --git a/testdata/repos/python/stub/ruby-dep/ruby-dep-0.ebuild b/testdata/repos/python/stub/ruby-dep/ruby-dep-0.ebuild new file mode 100644 index 000000000..abc2efee9 --- /dev/null +++ b/testdata/repos/python/stub/ruby-dep/ruby-dep-0.ebuild @@ -0,0 +1,14 @@ +EAPI=7 + +USE_RUBY="ruby27 ruby30 ruby31 ruby32" + +inherit ruby-ng + +DESCRIPTION="Stub ebuild with complete USE_RUBY support" +HOMEPAGE="https://github.com/pkgcore/pkgcheck" +LICENSE="BSD" +SLOT="0" + +RDEPEND=" + stub/stub1 +"