Hello community, here is the log from the commit of package zypper-lifecycle-plugin for openSUSE:Factory checked in at 2020-09-29 19:05:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/zypper-lifecycle-plugin (Old) and /work/SRC/openSUSE:Factory/.zypper-lifecycle-plugin.new.4249 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "zypper-lifecycle-plugin" Tue Sep 29 19:05:00 2020 rev:6 rq:838482 version:0.6.1601367426.843fe7a Changes: -------- --- /work/SRC/openSUSE:Factory/zypper-lifecycle-plugin/zypper-lifecycle-plugin.changes 2020-08-10 14:57:53.815997284 +0200 +++ /work/SRC/openSUSE:Factory/.zypper-lifecycle-plugin.new.4249/zypper-lifecycle-plugin.changes 2020-09-29 19:05:19.814025177 +0200 @@ -1,0 +2,7 @@ +Tue Sep 29 08:18:45 UTC 2020 - Vladimir Nadvornik <nadvor...@suse.com> + +- Version 0.6.1601367426.843fe7a +- Allow wildcard matching (jsc#SLE-14168) +- Implement successor handling (jsc#SLE-16251) + +------------------------------------------------------------------- Old: ---- zypper-lifecycle-0.6.1596796104.87bdab7.tar.xz New: ---- zypper-lifecycle-0.6.1601367426.843fe7a.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ zypper-lifecycle-plugin.spec ++++++ --- /var/tmp/diff_new_pack.AAaaOr/_old 2020-09-29 19:05:20.486025855 +0200 +++ /var/tmp/diff_new_pack.AAaaOr/_new 2020-09-29 19:05:20.490025860 +0200 @@ -23,7 +23,7 @@ Name: zypper-lifecycle-plugin Url: https://github.com/SUSE/zypper-lifecycle -Version: 0.6.1596796104.87bdab7 +Version: 0.6.1601367426.843fe7a Release: 0 Requires: zypper >= 1.13.10 BuildRoot: %{_tmppath}/%{name}-%{version}-build ++++++ zypper-lifecycle-0.6.1596796104.87bdab7.tar.xz -> zypper-lifecycle-0.6.1601367426.843fe7a.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zypper-lifecycle-0.6.1596796104.87bdab7/test-data/SLES.lifecycle new/zypper-lifecycle-0.6.1601367426.843fe7a/test-data/SLES.lifecycle --- old/zypper-lifecycle-0.6.1596796104.87bdab7/test-data/SLES.lifecycle 2020-08-07 12:28:24.000000000 +0200 +++ new/zypper-lifecycle-0.6.1601367426.843fe7a/test-data/SLES.lifecycle 2020-09-29 10:17:06.000000000 +0200 @@ -1,7 +1,36 @@ # test data for SLES product -# this should be installed in /var/lib/lifecycle/data/SLES.lifecycle -# format: name,version,date +# this should be installed in /usr/share/lifecycle/data/SLES.lifecycle +# format: name,version,date,successor +# successor is optional +# date can be empty if successor is specified + # version can contain wildcard character * aaa_base, 13.2*, 2016-05-05 -kernel-default, 3.12.51-60.25.1, 2016-05-01 + + +# Name can contain wildcard character * + +kernel-*, 3.12.51-60.25.1, 2016-05-01 + +# or it can be specified as regexp enclosed in / / +# See Ruby documentation for regexp syntax. +# Typically the regexp should match whole name, +# so ^ and $ should be used + +/^kernel-.*$/, 3.12.51-60.25.1, 2016-05-01 + +# Successor specifies a package that replaces the given package +# when available - jsc#SLE-16356 +# +# Successor use the same syntax as name, additionally +# it can reference matching groups from name with "<1>", "<2>" etc. +# the groups are generated from wildcards, in regexp they must be +# marked with ( ) + +# the following 4 lines are equivalent + +hdf5_*-gnu*-hpc, *, , hdf5_*-gnu<2>-hpc +hdf5_*-gnu*-hpc, *, , /^hdf5_(.*)-gnu<2>-hpc$/ +/^hdf5_.*-gnu(.*)-hpc$/, *, , hdf5_*-gnu<1>-hpc +/^hdf5_.*-gnu(.*)-hpc$/, *, , /^hdf5_(.*)-gnu<1>-hpc$/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zypper-lifecycle-0.6.1596796104.87bdab7/zypper-lifecycle new/zypper-lifecycle-0.6.1601367426.843fe7a/zypper-lifecycle --- old/zypper-lifecycle-0.6.1596796104.87bdab7/zypper-lifecycle 2020-08-07 12:28:24.000000000 +0200 +++ new/zypper-lifecycle-0.6.1601367426.843fe7a/zypper-lifecycle 2020-09-29 10:17:06.000000000 +0200 @@ -116,9 +116,9 @@ @installed_products[p[:name]] = p if p[:installed] == "true" end - package_list = Zypper::xml_call("--no-refresh -x se -s -t package", 'search-result/solvable-list') + @all_packages = Zypper::xml_call("--no-refresh -x se -s -t package", 'search-result/solvable-list') @installed_package = {} - package_list.each do |p| + @all_packages.each do |p| if p[:kind] == 'package' && p[:status] == 'installed' @installed_package[p[:name]] ||= [] @installed_package[p[:name]] << p @@ -144,7 +144,7 @@ end end - package_list.each do |p| + @all_packages.each do |p| p[:product] = product_by_repo[p[:repository]] end @@ -156,6 +156,8 @@ end def load_lifecycle_data() + @lifecycle_data = [] + @successors = {} @installed_products.values.each do |product| [ "/var/lib/lifecycle/data/#{product[:name]}.lifecycle", @@ -165,20 +167,8 @@ print "trying to load #{file}... " if @verbose begin CSV.foreach(file, :skip_blanks => true, :skip_lines => /^\s*#/ ) do |line| - name, version, date = line.map(&:strip) - date = Time.parse(date).strftime("%s") - version_re = Regexp.new( '^' + Regexp.quote(version).gsub(/\\\*/, '.*') + '$') - #print "match #{name} #{version_re}\n" - (@installed_package[name] || []).each do |p| - #print "#{p}\n" - if version_re.match(p[:edition]) - p[:package_eol] = date - end - if version_re.match(p[:update_edition]) - p[:update_package_eol] = date - end - p[:version_specified] = version - end + line.each{ |f| f = "" unless f; f.strip! } + @lifecycle_data.append(line) end print "ok\n" if @verbose rescue Errno::ENOENT => e @@ -189,6 +179,93 @@ end end end + parse_lifecycle_data(1) + # currenly we do not print successor EOL, so second pass is not needed + # parse_lifecycle_data(2) + end + + + def parse_lifecycle_data(pass) + if pass==1 + target_hash = @installed_package + else + target_hash = @successors + end + @lifecycle_data.each do |line| + begin + name, version, date, successor = line + + if !date || date.empty? + date = nil + else + date = Time.parse(date).strftime("%s") + end + version_re = Regexp.new( '^' + Regexp.quote(version).gsub(/\\\*/, '.*') + '$') + #print "match #{name} #{version_re}\n" + name_re = nil + if name.start_with?("/") && name.end_with?("/") + name_re = Regexp.new(name[1..-2]) + matching_names = target_hash.keys.select { |p| name_re.match(p) } + matching_installed = matching_names.map { |p| target_hash[p] }.flatten(1) + elsif name.include? "*" + name_re = Regexp.new( '^' + Regexp.quote(name).gsub(/\\\*/, '(.*)') + '$') + matching_names = target_hash.keys.select { |p| name_re.match(p) } + matching_installed = matching_names.map { |p| target_hash[p] }.flatten(1) + else + matching_installed = target_hash[name] + end + (matching_installed || []).each do |p| + if version_re.match(p[:edition]) + p[:package_eol] = date + end + if version_re.match(p[:update_edition]) + p[:update_package_eol] = date + end + p[:version_specified] = version + + if pass == 1 && successor + handle_successor(p, successor, name_re) + end + end + rescue Exception => e + print "\nError parsing #{line}: #{e.message}\n" + @retval = 2 + end + end + end + + def handle_successor(package, successor, name_re) + matches = [package[:name]] + if name_re + m = name_re.match(package[:name]) + matches += m.captures if m + end + if successor.start_with?("/") && successor.end_with?("/") + successor_re = successor[1..-2] + else + successor_re = '^' + Regexp.quote(successor).gsub(/\\\*/, '.*') + '$' + end + for i in 0..9 + successor_re.gsub!(/<#{i}>/, Regexp.quote(matches[i] || '')) + end + successor_re = Regexp.new(successor_re) + matching_successors = @all_packages.select { |s| successor_re.match(s[:name]) && package[:name] != s[:name] && package[:arch] == s[:arch] } + + # select the successor with highest version + successor_version = package[:edition] + successor_pkg = nil + matching_successors.each do |s| + if Gem::Version.new(s[:edition]) > Gem::Version.new(successor_version) + successor_pkg = s + successor_version = s[:edition] + end + end + + if successor_pkg + package[:successor] = successor_pkg + @successors[successor_pkg[:name]] ||= [] + @successors[successor_pkg[:name]] << successor_pkg + end end def solve_package_eol() @@ -211,6 +288,10 @@ up_eol = p[:product][:endoflife_time_t] if !up_eol && p[:product] && p[:product][:endoflife_time_t] # default to product eol p[:update_eol] = up_eol.to_i if up_eol end + + if p[:successor] + p[:eol] = NowTS + end end end @@ -301,7 +382,10 @@ vendor = p[:product][:vendor] if p[:product] eol = eol_string(p[:eol], vendor) up = '' - if p[:update_edition] + if p[:successor] + up = ", installed #{p[:edition]}, successor available #{p[:successor][:name]}-#{p[:successor][:edition]}" + up_eol = '' + elsif p[:update_edition] up = ", installed #{p[:edition]}, update available #{p[:update_edition]}" up_eol = '' up_eol = eol_string(p[:update_eol], vendor) if p[:update_eol] && p[:update_eol] < NaTS @@ -366,7 +450,7 @@ print ".\n" end - packages = @installed_package.values.flatten.select {|p| (p[:package_eol] || p[:update_edition]) } + packages = @installed_package.values.flatten.select {|p| (p[:package_eol] || p[:update_edition] || p[:successor]) } print_packages = packages.select {|p| !p[:already_reported] } if print_packages.length > 0 print "\nPackage end of support if different from product:\n" @@ -415,7 +499,7 @@ print "\nNo products whose support ends before #{date_str}.\n" end packages = @installed_package.values.flatten.select {|p| p[:eol] && p[:eol] <= date_ts } - dif_packages = packages.select {|p| (p[:package_eol] || p[:update_edition]) } + dif_packages = packages.select {|p| (p[:package_eol] || p[:update_edition] || p[:successor]) } print_packages = dif_packages.select {|p| !p[:already_reported] } if packages.length > 0 if print_packages.length > 0