Skip to content

Commit

Permalink
Automatically select the driver packages (bsc#953522)
Browse files Browse the repository at this point in the history
- 4.1.27
  • Loading branch information
lslezak committed Feb 15, 2019
1 parent 21f9766 commit 1637bae
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 2 deletions.
7 changes: 7 additions & 0 deletions package/yast2-packager.changes
@@ -1,3 +1,10 @@
-------------------------------------------------------------------
Thu Feb 14 07:46:00 UTC 2019 - lslezak@suse.cz

- Automatically preselect the driver packages from new repositories
(bsc#953522)
- 4.1.27

-------------------------------------------------------------------
Fri Feb 8 17:21:41 UTC 2019 - knut.anderssen@suse.com

Expand Down
2 changes: 1 addition & 1 deletion package/yast2-packager.spec
Expand Up @@ -17,7 +17,7 @@


Name: yast2-packager
Version: 4.1.26
Version: 4.1.27
Release: 0

BuildRoot: %{_tmppath}/%{name}-%{version}-build
Expand Down
28 changes: 27 additions & 1 deletion src/clients/sw_single.rb
Expand Up @@ -7,6 +7,9 @@
#
require "shellwords"

require "y2packager/known_repositories"
require "y2packager/system_packages"

module Yast
# Purpose: contains dialog loop for workflows:
# "Install/Remove software"
Expand Down Expand Up @@ -449,9 +452,10 @@ def GetPackagerOptions

# use default parameters for missing or invalid values
if mode.nil?
preselect_system_packages

# use summary mode if there is something to install
# (probably a suggested or recommended package) (bnc#465194)
Pkg.PkgSolve(true) # select the packages
mode = if Pkg.IsAnyResolvable(:any, :to_install) || Pkg.IsAnyResolvable(:any, :to_remove)
:summaryMode
else
Expand All @@ -467,6 +471,22 @@ def GetPackagerOptions
deep_copy(ret)
end

# select the system packages (drivers) from the new repositories
def preselect_system_packages
known_repos = Y2Packager::KnownRepositories.new
system_packages = Y2Packager::SystemPackages.new(known_repos.new_repositories)
system_packages.select
end

def save_known_repositories
known_repos = Y2Packager::KnownRepositories.new
# nothing new, no need to update the file
return if known_repos.new_repositories.empty?

known_repos.update
known_repos.write
end

# =============================================================
def StartSWSingle
Wizard.CreateDialog
Expand Down Expand Up @@ -570,7 +590,10 @@ def StartSWSingle
result = :next
# start the repository manager
elsif result == :repo_mgr
save_known_repositories
WFM.CallFunction("repositories", [:sw_single_mode])
# preselect the driver packages from new repositories
preselect_system_packages
force_restart = true
elsif result == :online_update_configuration
required_package = "yast2-online-update-configuration"
Expand Down Expand Up @@ -767,6 +790,9 @@ def StartSWSingle
Builtins.size(@packagelist).zero?
force_restart = true
end

# remember the current repositories for the next time
save_known_repositories
end
end
end while force_restart
Expand Down
98 changes: 98 additions & 0 deletions src/lib/y2packager/known_repositories.rb
@@ -0,0 +1,98 @@
# ------------------------------------------------------------------------------
# Copyright (c) 2019 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.
# ------------------------------------------------------------------------------

require "yaml"
require "yast"

module Y2Packager
# Track the known repositories from which the system packages (drivers)
# have been installed (or suggested to the user).
# @see https://github.com/yast/yast-packager/wiki/Selecting-the-Driver-Packages
class KnownRepositories
include Yast::Logger

STATUS_FILE = "/var/lib/YaST2/system_packages_repos.yaml".freeze

# Constructor
def initialize
Yast.import "Pkg"
Yast.import "Installation"
end

def repositories
@repositories ||= read_repositories
end

def write
log.info("Writing known repositories #{repositories.inspect} to #{status_file}")

# accessible only for the root user, the repository URLs should not contain
# any passwords but rather be safe than sorry
File.open(status_file, "w", 0o600) do |f|
f.write(repositories.to_yaml)
end
end

def update
# add the current repositories
repositories.concat(current_repositories)
# remove duplicates and sort them
repositories.uniq!
repositories.sort!
end

#
# Return new (unknown) repositories
#
# @return [Array<String>] List of new repositories (URLs)
#
def new_repositories
log.info "current repositories: #{current_repositories.inspect}"
log.info "known repositories: #{repositories.inspect}"

new_repos = current_repositories - repositories
log.info "New repositories: #{new_repos.inspect}"
new_repos
end

private

def read_repositories
if !File.exist?(status_file)
log.info("Status file #{status_file} not found")
return []
end

status = YAML.load_file(status_file)
# unify the file in case it was manually modified
status.uniq!
status.sort!

log.info("Read known repositories from #{status_file}: #{status}")
status
end

def current_repositories
# only the enabled repositories
repo_ids = Yast::Pkg.SourceGetCurrent(true)

urls = repo_ids.map { |r| Yast::Pkg.SourceGeneralData(r)["url"] }
urls.uniq!
urls.sort
end

def status_file
# add the current target prefix
File.join(Yast::Installation.destdir, STATUS_FILE)
end
end
end
92 changes: 92 additions & 0 deletions src/lib/y2packager/system_packages.rb
@@ -0,0 +1,92 @@
# ------------------------------------------------------------------------------
# Copyright (c) 2019 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.
# ------------------------------------------------------------------------------

require "yast"

module Y2Packager
# Preselect the system packages (drivers) from the specified repositories.
# @see https://github.com/yast/yast-packager/wiki/Selecting-the-Driver-Packages
class SystemPackages
include Yast::Logger

# @return [Array<String>] Repositories from which the driver packages should be selected
attr_reader :repositories

#
# Constructor
#
# @param repository_urls [Array<String>] Repositories from which the driver
# packages should be selected
#
def initialize(repository_urls)
log.info "System packages repositories: #{repository_urls.inspect}"
@repositories = repository_urls
end

def packages
@packages ||= find_packages
end

def select
return if packages.empty?
log.info "Preselecting system packages: #{packages.inspect}"
packages.each { |p| Yast::Pkg.PkgInstall(p) }
end

private

#
# Create repository ID to URL mapping
#
# @return [Array<Integer>] List of repository IDs
#
def repo_ids(urls)
repo_ids = Yast::Pkg.SourceGetCurrent(true)
repo_ids.each_with_object([]) do |i, list|
list << i if urls.include?(Yast::Pkg.SourceGeneralData(i)["url"])
end
end

def find_packages
if repositories.empty?
log.info "No new repository found, not searching system packages"
return []
end

original_solver_flags = Yast::Pkg.GetSolverFlags

# solver flags for selecting minimal recommended packages (e.g. drivers)
Yast::Pkg.SetSolverFlags(
"ignoreAlreadyRecommended" => false,
"onlyRequires" => true
)
# select the packages
Yast::Pkg.PkgSolve(true)

ids = repo_ids(repositories)

pkgs = Yast::Pkg.ResolvableProperties("", :package, "")
pkgs = pkgs.select do |p|
# the packages from the specified repositories selected by the solver
p["status"] == :selected && ids.include?(p["source"]) && p["transact_by"] == :solver
end

# set back the original solver flags
Yast::Pkg.SetSolverFlags(original_solver_flags)

pkgs.map! { |p| p["name"] }
log.info "Found system packages: #{pkgs}"

pkgs
end
end
end
72 changes: 72 additions & 0 deletions test/known_repositories_test.rb
@@ -0,0 +1,72 @@
#!/usr/bin/env rspec

require_relative "test_helper"
require "y2packager/known_repositories"

describe Y2Packager::KnownRepositories do
Yast.import "Pkg"

let(:repo_url) { "http://example.com/repo" }
let(:repos) { [repo_url] }
let(:source_id) { 42 }

before do
allow(Yast::WFM).to receive(:scr_root).and_return("/")
end

describe "#repositories" do
it "returns empty list if the file does not exist" do
expect(File).to receive(:exist?).with(Y2Packager::KnownRepositories::STATUS_FILE)
.and_return(false)
expect(subject.repositories).to eq([])
end

it "reads the repository list from file" do
expect(File).to receive(:exist?).with(Y2Packager::KnownRepositories::STATUS_FILE)
.and_return(true)
expect(YAML).to receive(:load_file).with(Y2Packager::KnownRepositories::STATUS_FILE)
.and_return(repos)

expect(subject.repositories).to eq(repos)
end
end

describe "#write" do
it "writes the known repositories to the file" do
allow(subject).to receive(:repositories).and_return(repos)

file = double("file")
expect(File).to receive(:open).with(Y2Packager::KnownRepositories::STATUS_FILE, "w", 0o600)
.and_yield(file)
expect(file).to receive(:write).with(repos.to_yaml)

subject.write
end
end

describe "#new_repositories" do
it "return the unknown repositories" do
allow(subject).to receive(:repositories).and_return(repos)

allow(Yast::Pkg).to receive(:SourceGetCurrent).with(true).and_return([source_id])
allow(Yast::Pkg).to receive(:SourceGeneralData).with(source_id)
.and_return("url" => "http://new.example.com")

expect(subject.new_repositories).to eq(["http://new.example.com"])
end
end

describe "#update" do
it "add the current repositories to the known repositories" do
allow(subject).to receive(:repositories).and_return(repos)

allow(Yast::Pkg).to receive(:SourceGetCurrent).with(true).and_return([source_id])
allow(Yast::Pkg).to receive(:SourceGeneralData).with(source_id)
.and_return("url" => "http://new.example.com")

subject.update
expect(repos).to eq(["http://example.com/repo", "http://new.example.com"])
end
end

end

0 comments on commit 1637bae

Please sign in to comment.