Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Warn when an old package is selected to install #878

Draft
wants to merge 6 commits into
base: SLE-15-SP1
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions package/yast2-installation.spec
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ systemctl enable YaST2-Firstboot.service
%files
%defattr(-,root,root)

# data files
%{yast_ydatadir}

# systemd service files
%{_unitdir}/YaST2-Second-Stage.service
%{_unitdir}/YaST2-Firstboot.service
Expand Down
17 changes: 17 additions & 0 deletions src/data/old_packages/sle15_sp1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
packages:
- name: yast2
version: 4.1.77-1.1
arch: x86_64
- name: yast2
version: 4.1.77-1.1
arch: aarch64

- name: yast2-pkg-bindings
version: 4.1.2-3.5.9
arch: x86_64
- name: yast2-pkg-bindings
version: 4.1.2-3.5.9
arch: aarch64

message: |
These packages are too old, newer packages should be preferred.
5 changes: 5 additions & 0 deletions src/lib/installation/clients/inst_doit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
# current contact information at www.novell.com.
# ------------------------------------------------------------------------------

require "installation/old_package_check"

module Yast
# Asks user to really do the installation/update.
class InstDoitClient < Client
Expand All @@ -41,6 +43,9 @@ def main
# bugzilla #256627
PackagesUI.ConfirmLicenses

# warn about installing old packages
::Installation::OldPackageCheck.run

# function in installation/misc.ycp
# bugzilla #219097
@confirmed = confirmInstallation
Expand Down
96 changes: 96 additions & 0 deletions src/lib/installation/old_package.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# ------------------------------------------------------------------------------
# Copyright (c) 2020 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"

Yast.import "Pkg"
Yast.import "Report"

module Installation
# This class represents an old package which should not be installed by users.
class OldPackage
include Yast::Logger

attr_reader :name, :version, :arch, :message

# @param name [String] name of the package
# @param version [String] version of the package
# @param arch [String] architecture, e.g. "x86_64"
# @param message [String] the error message displayed to the user
# when an old package is selected
def initialize(name:, version:, arch:, message:)
@name = name
@version = version
@arch = arch
@message = message
end

# Finds the currently selected old package, if none or newer is selected then
# it returns `nil`.
# @return [Hash,nil] The selected old package or nil.
def selected_old
packages = Yast::Pkg.ResolvableProperties(name, :package, "")

# 1 = the selected is newer, the opposite is older or the same
lslezak marked this conversation as resolved.
Show resolved Hide resolved
packages.find do |p|
p["status"] == :selected && p["arch"] == arch &&
Yast::Pkg.CompareVersions(version, p["version"]) == 1
end
end

# Reads the old package configuration files and creates the respective
# OldPackage objects.
lslezak marked this conversation as resolved.
Show resolved Hide resolved
# @return [Array] Configured old packages, empty list if no configuration
# is specified
def self.read(paths = nil)
# unfortunately we cannot use Yast::Directory.find_data_file
# here because it needs an exact file name, it does not accept a glob,
# use Yast.y2paths to honor the Y2DIR setting
data_paths = paths || Yast.y2paths.map { |p| File.join(p, "data", "old_packages") }
data_paths.select { |p| File.directory?(p) }

log.debug "Found data directories: #{data_paths.inspect}"

data_files = data_paths.each_with_object([]) do |p, obj|
# find all *.yml and *.yaml files
obj.concat(Dir[File.join(p, "*.y{a,}ml")])
end

log.debug "Found data files: #{data_files.inspect}"

# remove the duplicates, this ensures the Y2DIR precedence
data_files.uniq! do |f|
File.basename(f)
end

log.debug "Unique data files: #{data_files.inspect}"

data_files.each_with_object([]) do |f, arr|
log.info "Loading file #{f.inspect}"

config = YAML.load_file(f)
message = config["message"] || ""
packages = config["packages"] || []

packages.each do |p|
arr << new(
name: p["name"],
version: p["version"],
arch: p["arch"],
message: message
)
end
end
end
end
end
30 changes: 30 additions & 0 deletions src/lib/installation/old_package_check.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# ------------------------------------------------------------------------------
# Copyright (c) 2020 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"

require "installation/old_package_report"
require "installation/old_package"

module Installation
# This class checks whether some old packages are selected
# and displays a warning to the user.
class OldPackageCheck
# Read the old package configurations and display warning for the old selected
# packages.
def self.run
old_packages = OldPackage.read
reporter = OldPackageReport.new(old_packages)
reporter.report
end
end
end
63 changes: 63 additions & 0 deletions src/lib/installation/old_package_report.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# ------------------------------------------------------------------------------
# Copyright (c) 2020 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"

Yast.import "Report"
Yast.import "HTML"

module Installation
# This class checks whether some old packages are selected
# and displays a warning to the user.
class OldPackageReport
lslezak marked this conversation as resolved.
Show resolved Hide resolved
include Yast::Logger
include Yast::I18n

attr_reader :old_packages

# @param old_packages [Array<Installation::OldPackage>] old package configurations
def initialize(old_packages)
textdomain "installation"
@old_packages = old_packages
end

# report the selected old packages to the user
def report
report_packages = old_packages.select(&:selected_old)
if report_packages.empty?
log.info "No old package selected"
return
end

log.warn("Detected old packages in the package selection: #{report_packages.inspect}")

grouped_packages = report_packages.group_by(&:message)

pkg_summary = grouped_packages.each_with_object("") do |(msg, pkgs), str|
package_names = pkgs.map do |pkg|
old = pkg.selected_old
"#{old["name"]}-#{old["version"]}-#{old["arch"]}"
end

str << "<p>"
str << Yast::HTML.List(package_names)
str << msg
str << "</p><br>"
end

message = format(_("The installer detected old package versions selected " \
"for installation: \n\n%{list}"), list: pkg_summary)
lslezak marked this conversation as resolved.
Show resolved Hide resolved

Yast::Report.LongWarning(message)
end
end
end
17 changes: 17 additions & 0 deletions test/data/old_packages/sle15_sp1_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
packages:
- name: yast2
version: 4.1.77-1.1
arch: x86_64
- name: yast2
version: 4.1.77-1.1
arch: aarch64

- name: yast2-pkg-bindings
version: 4.1.2-3.5.9
arch: x86_64
- name: yast2-pkg-bindings
version: 4.1.2-3.5.9
arch: aarch64

message: |
These packages are too old, newer packages should be preferred.
15 changes: 15 additions & 0 deletions test/old_package_check_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#! /usr/bin/env rspec

require_relative "test_helper"

require "installation/old_package_check"

describe Installation::OldPackageCheck do
describe ".run" do
it "reads old package configurations and reports the old packages" do
expect(Installation::OldPackage).to receive(:read)
expect_any_instance_of(Installation::OldPackageReport).to receive(:report)
Installation::OldPackageCheck.run
end
end
end
100 changes: 100 additions & 0 deletions test/old_package_report_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#! /usr/bin/env rspec

require_relative "test_helper"

require "installation/old_package"
require "installation/old_package_report"

describe Installation::OldPackageReport do
let(:message1) { "These packages are too old, install new ones." }
let(:message2) { "This package contains a bug." }
let(:old_package1) do
Installation::OldPackage.new(
name: "yast2",
version: "4.1.77-1.1",
arch: "x86_64",
message: message1
)
end
let(:old_package2) do
Installation::OldPackage.new(
name: "yast2-pkg-bindings",
version: "4.1.2-3.5.9",
arch: "x86_64",
message: message2
)
end

subject { Installation::OldPackageReport.new([old_package1, old_package2]) }

describe "#report" do
before do
expect(old_package1).to receive(:selected_old).at_least(:once)
.and_return(selected_package1)
expect(old_package2).to receive(:selected_old).at_least(:once)
.and_return(selected_package2)
end

context "No old package selected" do
let(:selected_package1) { nil }
let(:selected_package2) { nil }

it "does not report any error" do
expect(Yast::Report).to_not receive(:LongWarning)
subject.report
end
end

context "An old package is selected" do
let(:selected_package1) do
{
"name" => "yast2",
"version" => "4.1.77-1.1",
"arch" => "x86_64"
}
end
let(:selected_package2) { nil }

it "reports an error" do
expect(Yast::Report).to receive(:LongWarning).with(/#{message1}/)
subject.report
end
end

context "More old packages are selected" do
let(:selected_package1) do
{
"name" => "yast2",
"version" => "4.1.77-1.1",
"arch" => "x86_64"
}
end
let(:selected_package2) do
{
"name" => "yast2-pkg-bindings",
"version" => "4.1.2-3.5.9",
"arch" => "x86_64"
}
end

it "reports an error for all packages" do
expect(Yast::Report).to receive(:LongWarning) do |message|
expect(message).to include(message1)
expect(message).to include(message2)
end

subject.report
end

it "groups the packages with the same message" do
allow(old_package2).to receive(:message).and_return(message1)

expect(Yast::Report).to receive(:LongWarning) do |message|
expect(message.scan(message1).size).to eq(1)
end

subject.report
end
end
end
end
Loading