Skip to content

Commit

Permalink
Added new Resolvable class, use it in Packages.rb (bsc#1136051)
Browse files Browse the repository at this point in the history
  • Loading branch information
lslezak committed May 27, 2019
1 parent be2e769 commit 2192a6d
Show file tree
Hide file tree
Showing 11 changed files with 316 additions and 36 deletions.
7 changes: 7 additions & 0 deletions package/yast2-packager.changes
@@ -1,3 +1,10 @@
-------------------------------------------------------------------
Thu May 23 12:03:23 UTC 2019 - Ladislav Slezák <lslezak@suse.cz>

- Added Y2packager::Resolvables class
- (bsc#1132650)
- 4.2.8

-------------------------------------------------------------------
Tue May 21 15:19:38 CEST 2019 - schubi@suse.de

Expand Down
10 changes: 5 additions & 5 deletions package/yast2-packager.spec
Expand Up @@ -17,7 +17,7 @@


Name: yast2-packager
Version: 4.2.7
Version: 4.2.8
Release: 0

BuildRoot: %{_tmppath}/%{name}-%{version}-build
Expand All @@ -38,17 +38,17 @@ BuildRequires: yast2-storage-ng >= 4.0.141
# Y2Packager::will_be_obsoleted_by
BuildRequires: yast2 >= 4.1.68

# Pkg::PrdLicenseLocales
BuildRequires: yast2-pkg-bindings >= 4.0.8
# Pkg::Resolvables
BuildRequires: yast2-pkg-bindings >= 4.2.0

# Augeas lenses
BuildRequires: augeas-lenses

# Newly added RPM
Requires: yast2-country-data >= 2.16.3

# Pkg::PrdLicenseLocales
Requires: yast2-pkg-bindings >= 4.0.8
# Pkg::Resolvables
Requires: yast2-pkg-bindings >= 4.2.0

# Y2Packager::will_be_obsoleted_by
Requires: yast2 >= 4.1.68
Expand Down
133 changes: 133 additions & 0 deletions src/lib/y2packager/resolvable.rb
@@ -0,0 +1,133 @@
# ------------------------------------------------------------------------------
# Copyright (c) 2019 SUSE LINUX GmbH, Nuremberg, Germany.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of version 2 of the GNU General Public License as published by the
# Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
# ------------------------------------------------------------------------------

require "yast"

Yast.import "Pkg"

module Y2Packager
#
# This class represents a libzypp resolvable object (package, pattern, patch,
# product, source package)
#
# @note The returned Resolvables might not be valid anymore after changing
# the package manager status (installing/removing packages, changing
# repositories, etc.). After such a change you need to load the resolvables
# again, avoid storing them for later if possible.
#
# @example All installed packages
# Y2Packager::Resolvable.find(kind: :package, status: :installed)
#
# @example Available (not installed) "yast2" packages
# Y2Packager::Resolvable.find(kind: :package, status: :available, name: "yast2")
#
# @example Lazy loading
# res = Y2Packager::Resolvable.find(kind: :package, status: :installed)
# # the `summary` attribute is loaded from libzypp when needed
# res.each {|r| puts "#{r.name} - {r.summary}"}
#
# @example Preloading the attributes
# # the `summary` attribute is loaded from libzypp already at the initial state
# res = Y2Packager::Resolvable.find(kind: :package, status: :installed, [:summary])
# # this returns the cached `summary` attribute, this is much more efficient
# res.each {|r| puts "#{r.name} - {r.summary}"}
#
# @since 4.2.6
class Resolvable
include Yast::Logger

#
# Find the resolvables which match the input parameters. See Yast::Pkg.Resolvables
#
# @param params [Hash<Symbol,Object>] The search filter, only the matching resolvables
# are returned.
# @param preload [Array<Symbol>] The list of attributes which should be preloaded.
# The missing attributes are lazy loaded, however for performance reasons
# you might ask to preload the attributes right at the beginning and avoid
# querying libzypp again later.
# @return [Array<Y2Packager::Resolvable>] Found resolvables or empty array if nothing found
# @see https://yast-pkg-bindings.surge.sh/ Yast::Pkg.Resolvables
def self.find(params, preload = [])
attrs = (preload + UNIQUE_ATTRIBUTES).uniq
Yast::Pkg.Resolvables(params, attrs).map { |r| new(r) }
end

#
# Is there any resolvable matching the requested parameters? This is similar to
# the .find method, just instead of a resolvable list it returns a simple Boolean.
#
# @param params [Hash<Symbol,Object>] The requested attributes
# @return [Boolean] `true` if any matching resolvable is found, `false` otherwise.
# @see .find
def self.any?(params)
Yast::Pkg.AnyResolvable(params)
end

#
# Constructor, initialize the object from a pkg-bindings resolvable hash.
#
# @param hash [Hash<Symbol,Object>] A pkg-bindings resolvable hash.
def initialize(hash)
from_hash(hash)
end

#
# Dynamically load the missing attributes from libzypp.
#
# @param method [Symbol] the method called
# @param *_args no used so far
#
# @return [<Type>] <description>
#
def method_missing(method, *_args)
return instance_variable_get("@#{method}") if instance_variable_defined?("@#{method}")

# load a missing attribute
if UNIQUE_ATTRIBUTES.all? { |a| instance_variable_defined?("@#{a}") }
load_attribute(method)
else
raise "Missing attributes for identifying the resolvable."
end
end

private

# attributes required for identifying a resolvable
UNIQUE_ATTRIBUTES = [:kind, :name, :version, :arch, :source].freeze

# Load the attributes from a Hash
#
# @param hash [Hash] The resolvable Hash obtained from pkg-bindings.
def from_hash(hash)
hash.each do |k, v|
instance_variable_set("@#{k}", v)
end
end

#
# Lazy load a missing attribute.
#
# @param attr [Symbol] The required attribute to load.
# @return [Object] The read value.
def load_attribute(attr)
attrs = Hash[(UNIQUE_ATTRIBUTES.map { |a| [a, instance_variable_get("@#{a}")] })]
resolvables = Yast::Pkg.Resolvables(attrs, [attr])

# Finding more than one result is suspicious, log a warning
log.warn("Found several resolvables: #{resolvables.inspect}") if resolvables.size > 1

resolvable = resolvables.first
raise NoMethodError unless resolvable && resolvable.key?(attr.to_s)
instance_variable_set("@#{attr}", resolvable[attr.to_s])
end
end
end
20 changes: 8 additions & 12 deletions src/modules/Packages.rb
Expand Up @@ -9,6 +9,7 @@
require "shellwords"

require "y2packager/product_upgrade"
require "y2packager/resolvable"

# Yast namespace
module Yast
Expand Down Expand Up @@ -812,26 +813,21 @@ def ForceFullRepropose
# Reset package selection, but keep the selected objects of the specified type
# @param [Array<Symbol>] keep a list of symbols specifying type of objects to be kept selected
def Reset(keep)
restore = []
restore = {}

# collect the currently selected resolvables
keep.each do |type|
resolvables = Pkg.ResolvableProperties("", type, "")

resolvables.each do |resolvable|
# only selected items but ignore the selections done by solver,
# during restoration they would be changed to be selected by YaST and they
# will be selected by solver again anyway
next if resolvable["status"] != :selected || resolvable["transact_by"] == :solver

restore << [resolvable["name"], type]
end
# get the items selected by YaST (the items selected by solver will be later selected again,
# the items selected by user are not reset by the Pkg.PkgApplReset call)
resolvables = Y2Packager::Resolvable.find(kind: type, status: :selected,
transact_by: :app_high)
restore[type] = resolvables.map(&:name)
end

# This keeps the user-made changes (BNC#446406)
Pkg.PkgApplReset

restore.each { |name, type| Pkg.ResolvableInstall(name, type) }
restore.each { |type, list| list.each { |name| Pkg.ResolvableInstall(name, type) } }

@system_packages_selected = false

Expand Down
4 changes: 4 additions & 0 deletions test/data/zypp/test_repo/README.md
@@ -0,0 +1,4 @@
The testing data was downloaded from the [YaST:Head OBS](
https://build.opensuse.org/project/show/YaST:Head) repository.

- repodata: https://download.opensuse.org/repositories/YaST:/Head/openSUSE_Leap_15.0/repodata/
Binary file not shown.
32 changes: 32 additions & 0 deletions test/data/zypp/test_repo/repodata/repomd.xml
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
<revision>1558080877</revision>
<tags>
<repo>obsrepository://build.opensuse.org/YaST:Head/openSUSE_Leap_15.0</repo>
<repo>obsbuildid:1533048310</repo>
</tags>
<data type="primary">
<checksum type="sha256">4d295860e625859a714dcc91c6a2e6f7634aab288eb13cf01c09ae2a65a7eeab</checksum>
<open-checksum type="sha256">6c154c0ff76aca4f1b376bbe91f5bf00d56eff19bf5cdf08b680472e42fb4ee1</open-checksum>
<location href="repodata/4d295860e625859a714dcc91c6a2e6f7634aab288eb13cf01c09ae2a65a7eeab-primary.xml.gz"/>
<timestamp>1558080876</timestamp>
<size>88670</size>
<open-size>762992</open-size>
</data>
<data type="filelists">
<checksum type="sha256">1d3843cc83a1bd651295caf1790d9487739a0df387e7cdf1251edbb284f8c387</checksum>
<open-checksum type="sha256">4921d6efe5170264f781275d9ca0408c37d96f262191c143ba6e06562d601820</open-checksum>
<location href="repodata/1d3843cc83a1bd651295caf1790d9487739a0df387e7cdf1251edbb284f8c387-filelists.xml.gz"/>
<timestamp>1558080876</timestamp>
<size>239386</size>
<open-size>3058860</open-size>
</data>
<data type="other">
<checksum type="sha256">06ae21ca0bb3653b69dc367b078d56d7c73004d1c6dbc0c730a2eff9dd84a9ac</checksum>
<open-checksum type="sha256">f770a3ae116812d8eeba60b2912d1205175026e1efcefaf899c2d3e03f44888f</open-checksum>
<location href="repodata/06ae21ca0bb3653b69dc367b078d56d7c73004d1c6dbc0c730a2eff9dd84a9ac-other.xml.gz"/>
<timestamp>1558080876</timestamp>
<size>230366</size>
<open-size>1439396</open-size>
</data>
</repomd>
7 changes: 7 additions & 0 deletions test/data/zypp/test_repo/repodata/repomd.xml.asc
@@ -0,0 +1,7 @@
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)

iD8DBQBc3m1tWyVH2GHn0GwRAgZfAJ49sk+alvmPOAGhdAaH7Y4TwpTM6ACZAXnG
Oqgdb6W/cPGRP7c7vIhT/Y8=
=kSvT
-----END PGP SIGNATURE-----
19 changes: 19 additions & 0 deletions test/data/zypp/test_repo/repodata/repomd.xml.key
@@ -0,0 +1,19 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.5 (GNU/Linux)

mQGiBEfZbkMRBADjWPMrWcXDcSDlPYsnn5tgT1LMJqqJwBisS6fNYWAxEMof4lWJ
MIiRyDE86uAgUvwCsDxmTHeM5uV8KOmsKedxd2bU8lNPSwcSXMLrVhL0mUpPz+EP
eq3VJkLCR+HSszWGkOrh8FddeaQBR5gYiwYu3Spoc8lHiUncbw/N2CeWDwCggMO/
GsLl+mXbmn4AYFLhNGvRUPUD/2tSlTOAIiGF3Y3svpYdIidMZlNo/+nss0qOMWaM
gZ+3pgXpZ+5pdXbukBupXipEvk+6JnmZjE/sZ/iY2j3WDb9Bh45ix7CA1zXMYtTk
0oAzUmxSWbJVqegcMQkR+9HT1Ys5x+7TQKuQQWXBPtp79zAQlOOa3Vy9Blgnom4R
nue8BACps5N0eQovsbBhGHr9uDcHrYRt74sgLqA83MP6voATCj+NLMFhHgIxJYrt
bItoeEeoPBaJ77AkcMJpitUBW2Ud6bDeChqf48nlnGjVBlIAwx63d0kkYPnsn5kN
BnhkkH9fUDgc934krzsf7dPrpFIkG0LI/TlSshI3HDI71m0r07QqWWFTVCBPQlMg
UHJvamVjdCA8WWFTVEBidWlsZC5vcGVuc3VzZS5vcmc+iGYEExECACYFAlrseIYC
GwMFCRcxukMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRBbJUfYYefQbHQDAJ9Q
4JMWDMaX74S6exNQQiMTdO3whACcDz1jkagJ0BBQY8usbzaJsjCmOm2IRgQTEQIA
BgUCR9luQwAKCRA7MBG3a51lI3yCAJ4jfMqPAn8uPoosOSeA80t7MQdYOACgq9yA
o67MnrLsqTdAM0Ud3ld3NhA=
=JmLZ
-----END PGP PUBLIC KEY BLOCK-----
22 changes: 3 additions & 19 deletions test/packages_test.rb
Expand Up @@ -1238,27 +1238,11 @@ def product(properties = {})
it "does not select previously unselected items" do
allow(Yast::Pkg).to receive(:PkgApplReset)

allow(Yast::Pkg).to receive(:ResolvableProperties).and_return(
[product("name" => "p1", "status" => :selected), product("name" => "p2")]
)
allow(Y2Packager::Resolvable).to receive(:find)
.with(kind: :product, status: :selected, transact_by: :app_high)
.and_return([Y2Packager::Resolvable.new(product("name" => "p1"))])

expect(Yast::Pkg).to receive(:ResolvableInstall).with("p1", :product)
expect(Yast::Pkg).not_to receive(:ResolvableInstall).with("p2", :product)

Yast::Packages.Reset([:product])
end

# When restoring the selected products ignore the items selected by solver
# (to not change their "transact_by" value). They will be selected again by
# solver if they are still needed.
it "does not restore items selected by solver" do
allow(Yast::Pkg).to receive(:PkgApplReset)

allow(Yast::Pkg).to receive(:ResolvableProperties).and_return(
[product("name" => "p1", "status" => :selected, "transact_by" => :solver)]
)

expect(Yast::Pkg).not_to receive(:ResolvableInstall).with("p1", :product)

Yast::Packages.Reset([:product])
end
Expand Down

0 comments on commit 2192a6d

Please sign in to comment.