Skip to content

Commit

Permalink
Add a new API to manage products and packages
Browse files Browse the repository at this point in the history
* The implementation is not finished.
* libzypp is the only implemented backend.
  • Loading branch information
imobachgs committed Nov 2, 2021
1 parent 1c10f47 commit efd7e26
Show file tree
Hide file tree
Showing 8 changed files with 574 additions and 0 deletions.
48 changes: 48 additions & 0 deletions library/packages/src/lib/y2packager/backend.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright (c) [2021] SUSE LLC
#
# All Rights Reserved.
#
# 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.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

module Y2Packager
# Implements support for a software management system
#
# To implement support for an additional backend, just inherit from this class
# and implement the corresponding methods (e.g., #probe, #repositories, #search, etc.).
class Backend
# Initializes the backend
def probe; end

# Returns the list of repositories
#
# @return [Array<Repository>]
def repositories
[]
end

# Returns the resolvables according to the given conditions and properties
#
# @todo Return a ResolvablesCollection instance.
#
# @param conditions [Hash<Symbol,String>] Search conditions (e.g., { name: "SLES" }
# @param properties [Array<Symbol>] List of properties to include in the result.
# The default list is defined by each backend.
# @return [Array<Resolvable>]
def search(*)
[]
end
end
end
84 changes: 84 additions & 0 deletions library/packages/src/lib/y2packager/libzypp_backend.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright (c) [2021] SUSE LLC
#
# All Rights Reserved.
#
# 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.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "yast"
require "y2packager/backend"
require "y2packager/rpm_repo"
require "y2packager/package"
require "y2packager/product"

module Y2Packager
# Backend implementation for libzypp
class LibzyppBackend < Backend
# Initialize the libzypp subsystem using the pkg-bindings
def probe
Yast.import "Pkg"
Yast.import "PackageLock"
Yast::Pkg.TargetInitialize("/")
Yast::Pkg.TargetLoad
Yast::Pkg.SourceRestore
Yast::Pkg.SourceLoad
end

# Reads the repositories from the system
#
# @return [Array<RpmRepo>]
def repositories
Yast::Pkg.SourceGetCurrent(false).map do |repo_id|
repo = Yast::Pkg.SourceGeneralData(repo_id)
raise NotFound if repo.nil?

RpmRepo.new(repo_id: repo_id, repo_alias: repo["alias"],
enabled: repo["enabled"], name: repo["name"],
autorefresh: repo["autorefresh"], url: repo["raw_url"],
product_dir: repo["product_dir"])
end
end

# @todo Allow passing multiple statuses
# @todo Use a set of default properties so you do not need to explictly pass them
def search(conditions:, properties:)
resolvables = Yast::Pkg.Resolvables(
conditions,
(properties + [:kind]).uniq
)

resolvables.map do |res|
meth = "hash_to_#{res["kind"]}"
next res unless respond_to?(meth, true)

send(meth, res)
end
end

private

def hash_to_product(hsh)
Y2Packager::Product.new(
name: hsh["name"], arch: hsh["arch"], version: hsh["version"]
)
end

def hash_to_package(hsh)
Y2Packager::Package.new(
hsh["name"], hsh["source"], hsh["version"]
)
end
end
end
29 changes: 29 additions & 0 deletions library/packages/src/lib/y2packager/rpm_repo.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright (c) [2021] SUSE LLC
#
# All Rights Reserved.
#
# 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.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "y2packager/repository"

module Y2Packager
# Represents an RPM packages repository
#
# @todo Move here the RPM specific logic from the Repository class
# and rename Repository to Origin.
class RpmRepo < Repository
end
end
91 changes: 91 additions & 0 deletions library/packages/src/lib/y2packager/software_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Copyright (c) [2021] SUSE LLC
#
# All Rights Reserved.
#
# 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.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "y2packager/repository"
require "y2packager/libzypp_backend"
require "y2packager/software_search"

module Y2Packager
# This class represents the software management subsystem
#
# It allows managing software repositories, installing/removing software, and so on.
#
# @example Initialize the software management subsystem
# software = SoftwareManagement.new(LibzyppBackend.new)
# software.probe
#
# @example Convenience method to initialize the software manager
# SoftwareManager.probe
# SoftwareManager.current #=> #<Y2Packager::SoftwareManager...>
#
class SoftwareManager
# @return [Array<Backend>] List of known backends
attr_reader :backends

class << self
# Returns a SoftwareManager instance and keeps the reference for the future
#
# @note At this time, it always initializes the system using the LibzyppBackend.
# @return [SoftwareManagement] A SoftwareManagement instance for the current system
def current
@current ||= new([LibzyppBackend.new])
end

def reset
@current = nil
end
end

# @param backends [Array<Backend>] List of backends to use
def initialize(backends)
@backends = backends
end

# Initialize the software subsystem
def probe
backends.each(&:probe)
end

# Commits the changes defined in the software proposal
#
# @param [SoftwareProposal]
def commit(_proposal)
# ask the backends to install the given packages/apps
raise NotImplementedError
end

# List of repositories from all the backends
#
# @return [Array<Repository>] Defined repositories from all backends
def repositories
backends.each_with_object([]) do |backend, all|
all.concat(backend.repositories)
end
end

# Returns a search object which includes all backends
#
# @todo Allow disabling any backend.
#
# @return [SoftwareSearch]
def search
SoftwareSearch.new(*backends)
end
end
end
88 changes: 88 additions & 0 deletions library/packages/src/lib/y2packager/software_search.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright (c) [2021] SUSE LLC
#
# All Rights Reserved.
#
# 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.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

module Y2Packager
# Query the software manager for resolvables (packages, products, applications,
# and so on).
#
# Should we have two different classes? One for the conditions/properties
# and the other one that represents the query itself. See Backend#search for
# an explanation.
#
# The SoftwareSearch contains additional information, like the list of backends.
#
# @example Search by name
# query = SoftwareSearch.new(backend)
#
class SoftwareSearch
include Enumerable

# @return [Array<Backend>] Limit the search to these backends
attr_reader :backends

# @return [Array<Symbol>] Properties to include
attr_reader :properties

# @return [Hash<Symbol,String>] A hash describing the conditions (e.g., {
# name: "yast2" })
attr_reader :conditions

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

def initialize(*backends)
@backends = backends # limit the query to these backends
@properties = BASE_ATTRIBUTES.dup
@conditions = {}
end

def named(name)
with(name: name)
self
end

def including(*names)
@properties.concat(names)
self
end

def excluding(*names)
names.each { |a| @properties.delete(a) }
self
end

def with(conds = {})
@conditions.merge!(conds)
self
end

# @todo Rely on a ResolvablesCollection instead
def each(&block)
resolvables.each(&block)
end

private

def resolvables
backends.each_with_object([]) do |backend, all|
all.concat(backend.search(conditions: conditions, properties: properties))
end
end
end
end

0 comments on commit efd7e26

Please sign in to comment.