Hello community,

here is the log from the commit of package yast2-installation for 
openSUSE:Leap:15.2 checked in at 2020-02-27 06:41:19
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Leap:15.2/yast2-installation (Old)
 and      /work/SRC/openSUSE:Leap:15.2/.yast2-installation.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "yast2-installation"

Thu Feb 27 06:41:19 2020 rev:161 rq:779094 version:4.2.35

Changes:
--------
--- /work/SRC/openSUSE:Leap:15.2/yast2-installation/yast2-installation.changes  
2020-02-21 23:50:00.528644829 +0100
+++ 
/work/SRC/openSUSE:Leap:15.2/.yast2-installation.new.26092/yast2-installation.changes
       2020-02-27 06:41:21.149590755 +0100
@@ -1,0 +2,26 @@
+Fri Feb 21 09:45:10 UTC 2020 - Ladislav Slezák <lsle...@suse.cz>
+
+- Added "yupdate" script to simplify patching the installer
+  (bsc#1163691)
+- 4.2.35
+
+-------------------------------------------------------------------
+Thu Feb 20 15:12:39 UTC 2020 - Imobach Gonzalez Sosa <igonzalezs...@suse.com>
+
+- Rely on the new Y2Network::NtpServer class (jsc#SLE-7188).
+- 4.2.34
+
+-------------------------------------------------------------------
+Thu Feb 20 11:24:41 UTC 2020 - Imobach Gonzalez Sosa <igonzalezs...@suse.com>
+
+- Allow to modify the control file at installation time using a
+  skelcd-* package (bsc#1164468).
+- 4.2.33
+
+-------------------------------------------------------------------
+Wed Feb 19 09:40:45 UTC 2020 - Steffen Winterfeldt <snw...@suse.com>
+
+- don't start getty on hvc0 & ttyAMA0 before Yast2-Firstboot (bsc#1157233)
+- 4.2.32
+
+-------------------------------------------------------------------

Old:
----
  yast2-installation-4.2.31.tar.bz2

New:
----
  yast2-installation-4.2.35.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ yast2-installation.spec ++++++
--- /var/tmp/diff_new_pack.ao5uLq/_old  2020-02-27 06:41:21.601591696 +0100
+++ /var/tmp/diff_new_pack.ao5uLq/_new  2020-02-27 06:41:21.605591705 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-installation
-Version:        4.2.31
+Version:        4.2.35
 Release:        0
 Summary:        YaST2 - Installation Parts
 License:        GPL-2.0-only
@@ -42,8 +42,8 @@
 BuildRequires:  yast2-packager >= 4.2.27
 # using /usr/bin/udevadm
 BuildRequires:  yast2-storage-ng >= 4.2.71
-## y2remote based version
-BuildRequires:  yast2-network >= 4.0.13
+# Y2Network::NtpServer
+BuildRequires:  yast2-network >= 4.2.55
 # new root password cwm widget
 BuildRequires:  yast2-users >= 3.2.8
 # storage-ng based version
@@ -78,7 +78,8 @@
 Requires:       yast2-services-manager >= 3.2.1
 # Yast::OSRelease.ReleaseVersionHumanReadable
 Requires:       yast2 >= 4.2.56
-Requires:       yast2-network >= 4.0.13
+# Y2Network::NtpServer
+Requires:       yast2-network >= 4.2.55
 # for AbortException and handle direct abort
 Requires:       yast2-ruby-bindings >= 4.0.6
 # for the first/second stage of installation
@@ -191,6 +192,8 @@
 
 # systemd service files
 %{_unitdir}
+# yupdate script
+%{_bindir}/
 %{yast_clientdir}
 %{yast_moduledir}
 %{yast_desktopdir}

++++++ YaST2-Firstboot.service ++++++
--- /var/tmp/diff_new_pack.ao5uLq/_old  2020-02-27 06:41:21.629591755 +0100
+++ /var/tmp/diff_new_pack.ao5uLq/_new  2020-02-27 06:41:21.629591755 +0100
@@ -3,6 +3,7 @@
 After=apparmor.service local-fs.target plymouth-start.service 
YaST2-Second-Stage.service
 Conflicts=plymouth-start.service
 Before=getty@tty1.service serial-getty@ttyS0.service 
serial-getty@ttyS1.service serial-getty@ttyS2.service
+Before=serial-getty@hvc0.service serial-getty@ttyAMA0.service
 Before=display-manager.service
 ConditionPathExists=/var/lib/YaST2/reconfig_system
 OnFailure=shutdown.target

++++++ YaST2-Second-Stage.service ++++++
--- /var/tmp/diff_new_pack.ao5uLq/_old  2020-02-27 06:41:21.641591779 +0100
+++ /var/tmp/diff_new_pack.ao5uLq/_new  2020-02-27 06:41:21.641591779 +0100
@@ -3,6 +3,7 @@
 After=apparmor.service local-fs.target plymouth-start.service
 Conflicts=plymouth-start.service
 Before=getty@tty1.service serial-getty@ttyS0.service 
serial-getty@ttyS1.service serial-getty@ttyS2.service
+Before=serial-getty@hvc0.service serial-getty@ttyAMA0.service
 Before=display-manager.service
 ConditionPathExists=/var/lib/YaST2/runme_at_boot
 

++++++ yast2-installation-4.2.31.tar.bz2 -> yast2-installation-4.2.35.tar.bz2 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-installation-4.2.31/Rakefile 
new/yast2-installation-4.2.35/Rakefile
--- old/yast2-installation-4.2.31/Rakefile      2020-02-18 10:19:25.000000000 
+0100
+++ new/yast2-installation-4.2.35/Rakefile      2020-02-21 13:28:20.000000000 
+0100
@@ -9,6 +9,7 @@
   # TODO: move to src/client and verify if needed
   conf.install_locations["control/*.rb"] = Packaging::Configuration::YAST_DIR 
+ "/clients"
   conf.install_locations["startup"] = Packaging::Configuration::YAST_LIB_DIR
+  conf.install_locations["bin/*"] = 
File.join(Packaging::Configuration::DESTDIR, "/usr/bin/")
 end
 
 # safety check - make sure the RNG file is up to date
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-installation-4.2.31/bin/yupdate 
new/yast2-installation-4.2.35/bin/yupdate
--- old/yast2-installation-4.2.31/bin/yupdate   1970-01-01 01:00:00.000000000 
+0100
+++ new/yast2-installation-4.2.35/bin/yupdate   2020-02-21 13:28:20.000000000 
+0100
@@ -0,0 +1,771 @@
+#!/usr/bin/env ruby
+
+# This script updates the YaST files in the inst-sys with the files from
+# a GitHub repository or a running tarball web server.
+#
+# See the help text below for the details or see the documentation in
+# the doc/yupdate.md file.
+#
+# Note: The reason why all classes are in a single file is that
+#       it allows to easy update the script to the latest version
+#       or add it to an older (not supported) installer with:
+#
+#   curl 
https://raw.githubusercontent.com/yast/yast-installation/master/bin/yupdate > 
/usr/bin/yupdate
+#   chmod +x /usr/bin/yupdate
+#   yupdate ...
+#
+
+require "fileutils"
+require "find"
+require "json"
+require "net/http"
+require "open-uri"
+require "pathname"
+require "shellwords"
+require "singleton"
+require "socket"
+require "tmpdir"
+require "uri"
+
+# for logging to y2log
+require "yast"
+
+module YUpdate
+  # version of the script
+  class Version
+    MAJOR = 0
+    MINOR = 1
+    PATCH = 0
+
+    STRING = "#{MAJOR}.#{MINOR}.#{PATCH}".freeze
+  end
+
+  # handle the "help" command line option
+  class HelpCommand
+    def run
+      name = File.basename(__FILE__)
+      puts <<-HELP
+This is a helper script for updating the YaST installer in the installation 
system.
+
+Usage: #{name} <command> <options>
+
+Commands:
+
+    patch <github_repo> <branch>  Patch the installer with the sources from 
GitHub,
+                            <github_repo> is a repository name (including the 
organization
+                            or the user name, if missing "yast" is used by 
default),
+                            <branch> is the Git branch to use
+
+    patch <tarball_url>     Patch the installer with the sources provided by
+                            a generic HTTP server
+
+    patch <host[:port]>     Patch the installer with the sources provided by
+                            a "rake server" task (for details see
+                            https://github.com/yast/yast-rake/#server),
+                            the default port is 8000
+
+    servers <host[:port]>   List the rake servers running on the remote 
machine,
+                            ask the specified server to report all `rake 
server`
+                            instances
+
+    overlay create [<dir>]  Create a new writable overlay for directory <dir>,
+                            if no directory is specified it creates overlays
+                            for the default YaST directories
+
+    overlay list            Print the created writable overlays
+
+    overlay reset           Remove all overlays, restore the system to
+                            the original state
+
+    overlay files           Print the changed files (removed files are not 
listed)
+
+    overlay diff            Print the diff of the changed overlay files
+
+    version                 Print the script version
+
+    help                    Print this help
+
+WARNING: This script is intended only for testing and development purposes,
+        using this tool makes the installation not supported!
+
+        See more details in
+        https://github.com/yast/yast-installation/blob/master/doc/yupdate.md
+HELP
+    end
+  end
+
+  # a helper module for printing and logging
+  module Logger
+    include Yast::Logger
+
+    # print the message on STDOUT and also write it to the y2log
+    # @param message [String] the message
+    def msg(message)
+      puts message
+      log.info(message)
+    end
+  end
+
+  # a simple /etc/install.inf parser/writer,
+  # we need to disable the YaST self-update feature to avoid conflicts
+  class InstallInf
+    attr_reader :path
+
+    # read the file
+    def initialize(path = "/etc/install.inf")
+      @path = path
+      @values = File.read(path).lines.map(&:chomp)
+    end
+
+    # get value for the key
+    def [](key)
+      line = find_line(key)
+      return nil unless line
+
+      line.match(/^#{Regexp.escape(key)}:\s*(.*)/)[1]
+    end
+
+    # set value for the key
+    def []=(key, val)
+      line = find_line(key)
+
+      if line
+        # update the existing key
+        line.replace("#{key}: #{val}")
+      else
+        # add a new key
+        values << "#{key}: #{val}"
+      end
+    end
+
+    # write the file back
+    def write
+      File.write(path, values.join("\n"))
+    end
+
+  private
+
+    attr_reader :values
+
+    def find_line(key)
+      values.find { |l| l.start_with?(key + ":") }
+    end
+  end
+
+  # Class for managing the OverlayFS mounts
+  #
+  # Each OverlayFS mount these directories:
+  #  - upper and lower directories - the files in the upper directory
+  #    shadow the files in the lower directory, the result can mounted
+  #    at another 3rd place
+  #  - working directory - for storing additional metadata
+  #    (e.g. removed files)
+  #
+  # See more details in
+  # https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt
+  #
+  # The yupdate script uses /var/lib/YaST2/overlayfs for managing the 
OverlayFS,
+  # specifically:
+  #
+  #  - /var/lib/YaST2/overlayfs/original - contains the original state
+  #  - /var/lib/YaST2/overlayfs/upper - contains the new/changed files
+  #  - /var/lib/YaST2/overlayfs/workdir - temporary metadata
+  #
+  # To avoid conflicts the original path names are converted to use the
+  # underscore (_) instead of slash (/) in the name, moreover the underscores
+  # are double-escaped to support underscores in the original names. E.g.
+  # /usr/lib/YaST2 (for which the real path is /mounts/mp_0001/usr/lib/YaST2
+  # is mounted to 
/var/lib/YaST2/overlayfs/original/_mounts_mp__0001_usr_lib_YaST2.
+  class OverlayFS
+    include YUpdate::Logger
+
+    OVERLAY_PREFIX = "/var/lib/YaST2/overlayfs".freeze
+
+    YAST_OVERLAYS = [
+      "/usr/lib/YaST2",
+      "/usr/lib64/YaST2",
+      "/usr/share/autoinstall",
+      "/usr/share/applications/YaST2"
+    ].freeze
+
+    attr_reader :dir, :orig_dir
+
+    # manage the OverlayFS for this directory
+    # @param directory [String] the directory
+    def initialize(directory)
+      @orig_dir = directory
+      # expand symlinks
+      @dir = File.realpath(directory)
+      raise "Path is not a directory: #{dir}" unless File.directory?(dir)
+    end
+
+    # create an OverlayFS overlay for this directory if it is not writable
+    def create
+      return if File.writable?(dir)
+      msg "Adding overlay for #{orig_dir}..."
+
+      FileUtils.mkdir_p(upperdir)
+      FileUtils.mkdir_p(workdir)
+      FileUtils.mkdir_p(origdir)
+
+      # make the original content available in a separate directory
+      system("mount", "--bind", dir, origdir)
+      # mark the mount as a private otherwise the overlay would propagate
+      # through the bind mount and we would see the changed content here
+      system("mount", "--make-private", origdir)
+
+      system("mount", "-t", "overlay", "overlay", "-o", "lowerdir=#{dir},"\
+        "upperdir=#{upperdir},workdir=#{workdir}", dir)
+    end
+
+    # delete the OverlayFS for this directory, all changes will be reverted 
back
+    def delete
+      system("umount", dir)
+      system("umount", origdir)
+      FileUtils.rm_rf([upperdir, workdir, origdir])
+    end
+
+    # print the modified files in this directory
+    def print_files
+      iterate_files { |f, _modif, _orig| puts f }
+    end
+
+    # print the diff for the changed files in this directory
+    def print_diff
+      iterate_files do |f, _modif, orig|
+        next unless File.exist?(f) && File.exist?(orig)
+        system("diff", "-u", orig, f)
+      end
+    end
+
+    # find all OverlayFS mounts in the system
+    def self.find_all
+      mounts = `mount`
+      mounts.lines.each_with_object([]) do |line, arr|
+        arr << new(Regexp.last_match[1]) if line =~ /^overlay on (.*) type 
overlay /
+      end
+    end
+
+    # return the default set of YaST overlays
+    def self.default_overlays
+      yast_overlays.map { |o| new(o) }
+    end
+
+    def upperdir
+      OverlayFS.escape_path("upper", dir)
+    end
+
+    def workdir
+      OverlayFS.escape_path("workdir", dir)
+    end
+
+    def origdir
+      OverlayFS.escape_path("original", dir)
+    end
+
+    def self.escape_path(subdir, path)
+      # escape (double) underscores for correct reverse conversion
+      File.join(OVERLAY_PREFIX, subdir, path.gsub("_", "__").tr("/", "_"))
+    end
+
+    def self.unescape_path(path)
+      Pathname.new(path).basename.to_s.gsub(/([^_])_([^_])/, "\\1/\\2")
+              .sub(/\A_/, "/").gsub("__", "_").gsub("//", "/")
+    end
+
+  private
+
+    # find the files in this directory
+    # the block is called with three parameters:
+    # - the path to the new file in the overlay directory
+    # - the original path in /
+    # - the path to the original file in the overlay directory
+    def iterate_files(&block)
+      return unless block_given?
+
+      Find.find(upperdir) do |f|
+        next unless File.file?(f)
+        upperdir_path = Pathname.new(upperdir)
+        relative_path = Pathname.new(f).relative_path_from(upperdir_path)
+        original_path = File.join(origdir, relative_path)
+        # unescape the path
+        pth = unescape_path(upperdir_path)
+        block.call(File.join(pth, relative_path), f, original_path)
+      end
+    end
+
+    def self.yast_overlays
+      # /usr/share/YaST2/ needs to be handled specially, it is writable
+      # but contains symlinks to read-only subdirectories, so let's make
+      # the subdirectories writable
+      YAST_OVERLAYS + Dir["/usr/share/YaST2/*"].each_with_object([]) do |f, 
arr|
+        arr << f if File.directory?(f) && !File.writable?(f)
+      end
+    end
+
+    private_class_method :yast_overlays
+  end
+
+  # a generic HTTP downloader
+  class Downloader
+    include YUpdate::Logger
+    attr_reader :url
+
+    # Create the downloader for the specified URL (only HTTP/HTTPS is 
supported)
+    # @param url [String] the URL for downloading
+    def initialize(url)
+      @url = url
+    end
+
+    # download the file, returns the response body or if a block is
+    # given it passes the response to it, handles HTTP redirection 
automatically
+    def download(&block)
+      msg "Downloading #{url}"
+
+      if block_given?
+        URI.open(url) { |f| block.call(f) }
+      else
+        URI.open(url, &:read)
+      end
+    end
+  end
+
+  # specialized tarball downloader, it can extract the downloaded
+  # tarball on the fly (without saving the actual tarball) to the target 
directory
+  class TarballDownloader < Downloader
+    def initialize(url)
+      super
+    end
+
+    # start the downloading, extract the tarball to the specified directory
+    def extract_to(dir)
+      download do |input|
+        if input.content_type !~ /application\/(x-|)gzip/
+          raise "Unknown MIME type: #{input.content_type}"
+        end
+
+        # pipe the response body directly to the tar process
+        IO.popen(["tar", "-C", dir, "--warning=no-timestamp", "-xz"], "wb") do 
|io|
+          while (buffer = input.read(4096))
+            io.write(buffer)
+          end
+        end
+      end
+    end
+  end
+
+  # specialized tarball downloader which can download the sources
+  # from GitHub (the "git" tool is missing in the inst-sys),
+  # instead of "git clone" we download the archive tarball
+  class GithubDownloader < TarballDownloader
+    attr_reader :repo, :branch
+
+    def initialize(repo, branch)
+      super("https://github.com/#{repo}/archive/#{branch}.tar.gz";)
+      @repo = repo
+      @branch = branch
+    end
+  end
+
+  # installing Ruby gems using the "gem" tool
+  class GemInstaller
+    # we need this gem for running the "rake install" command
+    NEEDED_GEM = "yast-rake".freeze
+
+    # install the YaST required gems
+    def install_required_gems
+      install_gems(required_gems)
+    end
+
+  private
+
+    # is the gem installed?
+    def gem_installed?(gem_name)
+      gem(gem_name)
+      true
+    rescue Gem::LoadError
+      false
+    end
+
+    # find the needed gems for running "rake install"
+    def required_gems
+      gems = []
+      gems << NEEDED_GEM if !gem_installed?(NEEDED_GEM)
+      # handle the rake gem specifically, it is present in the system, but
+      # the /usr/bin/rake file is missing
+      gems << "rake" if !File.exist?("/usr/bin/rake")
+      gems
+    end
+
+    # install the specified gems
+    def install_gems(gem_names)
+      return if gem_names.empty?
+      add_gem_overlay
+      system("gem", "install", "--no-document", "--no-format-exec", *gem_names)
+    end
+
+    # make sure that the gem directory is writable
+    def add_gem_overlay
+      overlay = OverlayFS.new(Gem.dir)
+      overlay.create
+    end
+  end
+
+  # install the YaST sources using the "rake install" call
+  class Installer
+    include YUpdate::Logger
+
+    attr_reader :src_dir
+
+    # @param src_dir [String] the source directory with unpacked sources
+    def initialize(src_dir)
+      @src_dir = src_dir
+    end
+
+    # install the sources to the inst-sys
+    def install
+      Dir.mktmpdir do |tmp|
+        # first install the files into a temporary location
+        # using "rake install DESTDIR=..."
+        install_sources(tmp)
+        # then find the changed files and update them in the inst-sys
+        copy_to_system(tmp)
+      end
+    end
+
+  private
+
+    # globs for ignored some files
+    SKIP_FILES = [
+      # vim temporary files
+      "*/.*swp",
+      # backup files
+      "*/*.bak",
+      # skip documentation
+      "/usr/share/doc/*",
+      # skip manual pages
+      "/usr/share/man/*",
+      # skip sysconfig templates
+      "/usr/share/fillup-templates/*"
+    ].freeze
+
+    # install the sources to the specified (temporary) directory
+    def install_sources(target)
+      msg "Preparing files..."
+
+      # check for Makefile.cvs, we cannot install packages using autotools
+      makefile_cvs = Dir["#{src_dir}/**/Makefile.cvs"].first
+      if makefile_cvs
+        raise "Found Makefile.cvs, autotools based packages cannot be 
installed!"
+      end
+
+      rakefile = Dir["#{src_dir}/**/Rakefile"].first
+      raise "Rakefile not found, cannot install the package" unless rakefile
+
+      src_dir = File.dirname(rakefile)
+      Dir.chdir(src_dir) do
+        `rake install DESTDIR=#{target.shellescape} 2> /dev/null`
+      end
+    end
+
+    # should be the file skipped?
+    def skip_file?(file)
+      SKIP_FILES.any? { |glob| File.fnmatch?(glob, file) }
+    end
+
+    # copy the changed files to the ins-sys
+    def copy_to_system(src)
+      msg "Copying to system..."
+      src_path = Pathname.new(src)
+      cnt = 0
+      Find.find(src) do |path|
+        # TODO: what about symlinks or empty directories?
+        next unless File.file?(path)
+
+        relative_path = Pathname.new(path).relative_path_from(src_path).to_s
+        system_file = File.absolute_path(relative_path, "/")
+        system_dir = File.dirname(system_file)
+
+        next if skip_file?(system_file)
+
+        if File.exist?(system_file)
+          next if FileUtils.identical?(system_file, path)
+          add_overlay(system_dir)
+          # replace a symlink (likely pointing to a read-only location)
+          FileUtils.rm_f(system_file) if File.symlink?(system_file)
+          FileUtils.cp(path, system_file)
+          msg "Updated: #{system_file}"
+        else
+          # ensure the directory is writable
+          if File.exist?(system_dir)
+            add_overlay(system_dir)
+          else
+            # FIXME: maybe an overlay is needed for the upper directory...
+            FileUtils.mkdir_p(system_dir)
+          end
+
+          FileUtils.cp(path, system_file)
+          msg "Added: #{system_file}"
+        end
+        cnt += 1
+      end
+
+      msg "Number of modified files: #{cnt}"
+    end
+
+    # ensure that the target directory is writable
+    def add_overlay(dir)
+      o = OverlayFS.new(dir)
+      o.create
+    end
+  end
+
+  # handler for the "overlay" command option
+  class OverlayCommand
+    def initialize(argv)
+      @argv = argv
+    end
+
+    def run
+      command = @argv.shift
+
+      case command
+      when "list"
+        puts OverlayFS.find_all.map(&:dir)
+      when "create"
+        dir = @argv.shift
+
+        if dir
+          ovfs = OverlayFS.new(dir)
+          ovfs.create
+        else
+          OverlayFS.default_overlays.map(&:create)
+        end
+      when "reset"
+        OverlayFS.find_all.map(&:delete)
+      when "files"
+        OverlayFS.find_all.map(&:print_files)
+      when "diff"
+        OverlayFS.find_all.map(&:print_diff)
+      else
+        InvalidCommand.new(command)
+      end
+    end
+  end
+
+  # inst-sys test
+  class InstSys
+    # check if the script is running in the inst-sys,
+    # the script might not work as expected in an installed system
+    # and using OverlayFS is potentially dangerous
+    def self.check!
+      # the inst-sys contains the /.packages.initrd file with a list of 
packages
+      return if File.exist?("/.packages.initrd")
+
+      # exit immediately if running in an installed system
+      $stderr.puts "ERROR: This script can only work in the installation 
system (inst-sys)!"
+      exit 1
+    end
+  end
+
+  # parse the command line options
+  class Options
+    def self.parse(argv)
+      command = argv.shift
+
+      case command
+      when "version"
+        VersionCommand.new
+      when "overlay"
+        InstSys.check!
+        OverlayCommand.new(argv)
+      when "patch"
+        InstSys.check!
+        PatchCommand.new(argv)
+      when "servers"
+        ServersCommand.new(argv)
+      when "help", "--help", nil
+        HelpCommand.new
+      else
+        InvalidCommand.new(command)
+      end
+    end
+  end
+
+  # handle the "version" command line option
+  class VersionCommand
+    def run
+      puts "#{File.basename(__FILE__)} #{Version::STRING}"
+    end
+  end
+
+  # handle the "servers" command line option
+  class ServersCommand
+    def initialize(argv)
+      @argv = argv
+    end
+
+    def run
+      host = @argv.shift
+      raise "Missing server name argument" unless host
+
+      servers = RemoteServer.find(host)
+      servers.each do |s|
+        puts "URL: #{s.url}, directory: #{s.dir}"
+      end
+    end
+  end
+
+  # handle the "patch" command line option
+  class PatchCommand
+    include YUpdate::Logger
+
+    def initialize(argv)
+      @argv = argv
+    end
+
+    def run
+      arg1 = @argv.shift
+      arg2 = @argv.shift
+
+      return 1 unless arg1
+
+      prepare_system
+
+      if arg1 && arg2
+        # update from github
+        install_from_github(arg1, arg2)
+      elsif arg1.start_with?("http") && arg1.end_with?(".tar.gz")
+        # upgrade from URL
+        install_from_tar(arg1)
+      elsif !arg2
+        # otherwise treat it as a hostname providing a tarball server
+        install_from_servers(arg1)
+      else
+        raise "Invalid arguments"
+      end
+
+      # TODO: only when something has been updated?
+      disable_self_update
+    end
+
+  private
+
+    def install_from_github(repo, branch)
+      # add the default "yast" GitHub organization if missing
+      repo = "yast/#{repo}" unless repo.include?("/")
+      downloader = GithubDownloader.new(repo, branch)
+      install_tar(downloader)
+    end
+
+    def install_from_tar(url)
+      downloader = TarballDownloader.new(url)
+      install_tar(downloader)
+    end
+
+    def install_from_servers(hostname)
+      servers = RemoteServer.find(hostname)
+
+      servers.each do |s|
+        msg "Installing from #{s.url}..."
+        url = "#{s.url}/archive/current.tar.gz"
+        install_from_tar(url)
+      end
+    end
+
+    def install_sources(src_dir)
+      i = Installer.new(src_dir)
+      i.install
+    end
+
+    # prepare the inst-sys for installation:
+    # - make the YaST directories writable
+    # - install the needed Ruby gems (yast-rake)
+    def prepare_system
+      OverlayFS.default_overlays.map(&:create)
+      g = GemInstaller.new
+      g.install_required_gems
+    end
+
+    def install_tar(downloader)
+      Dir.mktmpdir do |download_dir|
+        downloader.extract_to(download_dir)
+        install_sources(download_dir)
+      end
+    end
+
+    # /etc/install.inf key
+    SELF_UPDATE_KEY = "SelfUpdate".freeze
+
+    # disable the self update in the install.inf file
+    def disable_self_update
+      inf = InstallInf.new
+      return if inf[SELF_UPDATE_KEY] == "0"
+
+      msg "Disabling the YaST SelfUpdate feature in install.inf!"
+      inf[SELF_UPDATE_KEY] = "0"
+      inf.write
+    end
+  end
+
+  # handle invalid command line options
+  class InvalidCommand
+    def initialize(cmd)
+      @cmd = cmd
+    end
+
+    def run
+      raise "Invalid command: #{cmd}"
+    end
+
+  private
+
+    attr_reader :cmd
+  end
+
+  # Query the remote server for the running servers
+  class RemoteServer
+    attr_reader :url, :dir
+
+    def initialize(url, dir)
+      @url = url
+      @dir = dir
+    end
+
+    def self.find(host)
+      host += ":8000" unless host.include?(":")
+
+      url = "http://#{host}/servers/index.json";
+      u = URI(url)
+      u.path = ""
+
+      downloader = Downloader.new(url)
+      JSON.parse(downloader.download).map do |server|
+        u.port = server["port"]
+        new(u.to_s, server["dir"])
+      end
+    end
+  end
+
+  # the main script application
+  class Application
+    include YUpdate::Logger
+
+    def run(argv = ARGV)
+      cmd = Options.parse(argv)
+      cmd.run
+    rescue StandardError => e
+      # the global exception handler
+      msg("ERROR: #{e.message}")
+      exit 1
+    end
+  end
+end
+
+# do not execute the script when the file is loaded by some other script
+# e.g. by a test, allow testing parts of the code without executing it as a 
whole
+if __FILE__ == $PROGRAM_NAME
+  # main
+  app = YUpdate::Application.new
+  app.run
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-installation-4.2.31/doc/yupdate.md 
new/yast2-installation-4.2.35/doc/yupdate.md
--- old/yast2-installation-4.2.31/doc/yupdate.md        1970-01-01 
01:00:00.000000000 +0100
+++ new/yast2-installation-4.2.35/doc/yupdate.md        2020-02-21 
13:28:20.000000000 +0100
@@ -0,0 +1,260 @@
+# The yupdate Script
+
+This is a documentation for the `yupdate` helper script,
+which is included in the YaST installer in SLE15-SP2/openSUSE
+Leap 15.2 (or newer) and in the openSUSE Tumbleweed since
+build 2020xxxx.
+
+## The Introduction
+
+**Problem**: You are developing a feature for the installer and you need to
+test your changes frequently. For extra fun, the change is spread across
+multiple repositories.
+
+The YaST installation system is quite different to an
+usual Linux installed system. The root filesystem
+is stored in a RAM disk and most files are read-only.
+That makes it quite difficult to modify the YaST installer
+if you need to debug a problem or test a fix.
+
+There are some possibilities for updating the YaST installer
+(see [Alternative](#Alternative))
+but they are usually not trivial and need special preparations.
+For this reason we created a special `yupdate` script which makes
+the process easier.
+
+However, in some cases this easier way cannot be used, see the
+[limitations](#limitations) section below.
+
+
+## Self-update
+
+After patching the installer the `yupdate` script disables
+the YaST self-update feature because it could conflict with it
+and overwrite the changes.
+
+If you need some changes from the self-update then use the `startshell=1`
+boot option, start the installer and allow the self-update step to finish,
+then abort the installation and use the `ypdate` script to apply the
+changes on top of the self-update.
+
+##  Warning
+
+:warning: **Patching the installer with the `yupdate` script makes
+the installation unsupported!** :warning:
+
+The script is intended for developers to test new features or bug fixes.
+
+It can be used by customers for testing as well, but it should not be used
+on production systems!
+
+## Installation
+
+The `yupdate` script should run in the inst-sys. Since SLE15-SP2/openSUSE
+Leap 15.2, openSUSE Tumbleweed 2020xxxx, it ~~is~~ will be preinstalled.
+
+For older releases, run:
+
+```shell
+curl 
https://raw.githubusercontent.com/yast/yast-installation/master/bin/yupdate > 
/usr/bin/yupdate
+chmod +x /usr/bin/yupdate
+```
+
+You can also use this command to update the included script
+to the latest version.
+
+## Basic Use Cases
+
+This script is intended to help in the following scenarios.
+
+### Make the inst-sys Writable
+
+As already mentioned, the files in the installation system are read only. To be
+able to patch the installer the script must be able to make the files writable.
+It does that automatically for the updated files, but maybe you would like to
+use this feature also for some other non-YaST files.
+
+To make a directory writable in the inst-sys run command
+
+```shell
+yupdate overlay create <dir>
+```
+
+This will create a writable overlay above the specified directory. If you do 
not
+specify any directory it will create writable overlays for the default YaST
+directories.
+
+Then you can easily edit the files using the included `vim` editor
+or by other tools like `sed` or overwrite by external files.
+
+### Patch YaST from GitHub Sources
+
+To update or install an YaST package directly from the GitHub source code
+repository use command
+
+```shell
+yupdate patch <github_slug> <branch>
+```
+
+where `github_slug` is a `user`/`repository` name, if the `user` value is
+missing the default "yast" is used. The `branch` in the source branch to
+install, for example `master` or `SLE-15-SP2`.
+
+
+#### Examples
+
+```shell
+# install the latest version of yast2-installation from upstream
+yupdate patch yast-installation master
+# install from a fork
+yupdate patch my_fork/yast-installation my_branch
+```
+
+#### Notes
+
+- Make sure that you use a branch compatible with the running inst-sys,
+  installing the latest version in an older release might not work
+  as expect, the installer might crash or behave unexpectedly.
+- There is no dependency resolution, if the new installed package
+  requires newer dependant packages then they must be installed manually.
+
+### Patch YaST from Locally Modified Sources
+
+Installing from GitHub sources is easy, but sometimes you do not want to
+push every single change to GitHub, you would like to just use the current
+files from you local Git checkout.
+
+In that case run
+
+```shell
+rake server
+```
+
+in your YaST module Git checkout. This will run a web server providing source
+tarball similar to the GitHub archive used in the previous case.
+
+*Note: You need "yast-rake" Ruby gem version 0.2.37 or newer.*
+
+Then run
+
+```shell
+yupdate patch <host_name>
+```
+
+where `<host_name>`  is the machine host name or the IP address where you run
+the `rake server` task. To make it easier the rake task prints these values at
+the start.
+
+By default this will use port 8000, if the server uses another port just add
+`:` followed by the port number.
+
+*Note: Make sure the server port is open in the firewall configuration,
+see the [documentation](https://github.com/yast/yast-rake/#server) for
+more details.*
+
+#### Patching Multiple Packages
+
+The `yupdate patch` command installs the sources from all running `rake server`
+servers. If you need to update sources from several packages you can just
+run `rake server` in all of them and install them with a single `yupdate`
+call.
+
+### Patch YaST from a Generic Tarball Archive
+
+This is similar to the previous cases, but the source tarball is not generated
+dynamically by a server, but it is a statically hosted file.
+
+Example:
+
+```shell
+yupdate patch http://myserver.example.com/test/yast2.tar.gz
+```
+
+## Other Commands
+
+### Listing OverlayFS Mounts
+
+To see the list of mounted OverlayFS run
+
+```shell
+yupdate overlay list
+```
+
+### Listing Updated Files
+
+To see the list of changed files
+
+```shell
+yupdate overlay files
+```
+
+### Displaying Changes in the System
+
+To see the applied changes to the system run
+
+```shell
+yupdate overlay diff
+```
+
+This will display a diff for all changed files, it does not report
+deleted or new files.
+
+### Restoring the System
+
+To revert all changes run
+
+```shell
+yupdate overlay reset
+```
+
+This will remove *all* OverlayFS mounts and restore the system to the original
+state.
+
+## Limitations
+
+- The script only works with Ruby source files, it cannot compile and
+  install C/C++ or other sources (the compiler and development libraries
+  are missing in the inst-sys)
+- Works only with the packages which use `Rakefile` for installation,
+  it does not work with autotools based packages (again, autoconf/automake
+  are also missing in the inst-sys)
+
+## Alternative
+
+1. For all repos, run `rake osc:build`
+2. Collect the resulting RPMs
+3. Run a server, eg. with `ruby -run -e httpd -- -p 8888 .`
+4. Type a loooong boot line to pass them all as DUD=http://....rpm
+   (or write that into a file and use the [info](
+   https://en.opensuse.org/SDB:Linuxrc#p_info) option
+   or build a single DUD file from the RPMs with the [`mkdud`](
+   https://github.com/wfeldt/mkdud) script)
+
+## Implementation Details
+
+### OverlayFS
+
+To make the inst-sys directories writable we use the Linux OverlayFS
+which can merge already existing file systems ("union filesystem").
+
+See more details in the [Linux Kernel Documentation](
+https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt).
+
+### Installing the Files
+
+For installing the sources the script uses the `rake install DESTDIR=...`
+command and install the files into a temporary directory. Then it compares
+the new files with the original files and if there is a change the old
+file is rewritten by the new file.
+
+This also skips some not needed files like documentation, manual pages,
+editor backup files, etc...
+
+This saves some memory as we do not need to shadow the not modified files
+with the same content.
+
+### Logging
+
+The messages printed on the console are also saved in the `y2log` file.
+That means it should be easy to find out that someone patched the installer
+when analyzing logs from a bug report.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/package/YaST2-Firstboot.service 
new/yast2-installation-4.2.35/package/YaST2-Firstboot.service
--- old/yast2-installation-4.2.31/package/YaST2-Firstboot.service       
2020-02-18 10:19:25.000000000 +0100
+++ new/yast2-installation-4.2.35/package/YaST2-Firstboot.service       
2020-02-21 13:28:20.000000000 +0100
@@ -3,6 +3,7 @@
 After=apparmor.service local-fs.target plymouth-start.service 
YaST2-Second-Stage.service
 Conflicts=plymouth-start.service
 Before=getty@tty1.service serial-getty@ttyS0.service 
serial-getty@ttyS1.service serial-getty@ttyS2.service
+Before=serial-getty@hvc0.service serial-getty@ttyAMA0.service
 Before=display-manager.service
 ConditionPathExists=/var/lib/YaST2/reconfig_system
 OnFailure=shutdown.target
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/package/YaST2-Second-Stage.service 
new/yast2-installation-4.2.35/package/YaST2-Second-Stage.service
--- old/yast2-installation-4.2.31/package/YaST2-Second-Stage.service    
2020-02-18 10:19:25.000000000 +0100
+++ new/yast2-installation-4.2.35/package/YaST2-Second-Stage.service    
2020-02-21 13:28:20.000000000 +0100
@@ -3,6 +3,7 @@
 After=apparmor.service local-fs.target plymouth-start.service
 Conflicts=plymouth-start.service
 Before=getty@tty1.service serial-getty@ttyS0.service 
serial-getty@ttyS1.service serial-getty@ttyS2.service
+Before=serial-getty@hvc0.service serial-getty@ttyAMA0.service
 Before=display-manager.service
 ConditionPathExists=/var/lib/YaST2/runme_at_boot
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/package/yast2-installation.changes 
new/yast2-installation-4.2.35/package/yast2-installation.changes
--- old/yast2-installation-4.2.31/package/yast2-installation.changes    
2020-02-18 10:19:25.000000000 +0100
+++ new/yast2-installation-4.2.35/package/yast2-installation.changes    
2020-02-21 13:28:20.000000000 +0100
@@ -1,4 +1,30 @@
 -------------------------------------------------------------------
+Fri Feb 21 09:45:10 UTC 2020 - Ladislav Slezák <lsle...@suse.cz>
+
+- Added "yupdate" script to simplify patching the installer
+  (bsc#1163691)
+- 4.2.35
+
+-------------------------------------------------------------------
+Thu Feb 20 15:12:39 UTC 2020 - Imobach Gonzalez Sosa <igonzalezs...@suse.com>
+
+- Rely on the new Y2Network::NtpServer class (jsc#SLE-7188).
+- 4.2.34
+
+-------------------------------------------------------------------
+Thu Feb 20 11:24:41 UTC 2020 - Imobach Gonzalez Sosa <igonzalezs...@suse.com>
+
+- Allow to modify the control file at installation time using a
+  skelcd-* package (bsc#1164468).
+- 4.2.33
+
+-------------------------------------------------------------------
+Wed Feb 19 09:40:45 UTC 2020 - Steffen Winterfeldt <snw...@suse.com>
+
+- don't start getty on hvc0 & ttyAMA0 before Yast2-Firstboot (bsc#1157233)
+- 4.2.32
+
+-------------------------------------------------------------------
 Mon Feb 17 17:44:48 UTC 2020 - Stefan Hundhammer <shundham...@suse.com>
 
 - Fixed user-visible messages (bsc#1084015)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/package/yast2-installation.spec 
new/yast2-installation-4.2.35/package/yast2-installation.spec
--- old/yast2-installation-4.2.31/package/yast2-installation.spec       
2020-02-18 10:19:25.000000000 +0100
+++ new/yast2-installation-4.2.35/package/yast2-installation.spec       
2020-02-21 13:28:20.000000000 +0100
@@ -16,7 +16,7 @@
 #
 
 Name:           yast2-installation
-Version:        4.2.31
+Version:        4.2.35
 Release:        0
 Group:          System/YaST
 License:        GPL-2.0-only
@@ -41,8 +41,8 @@
 BuildRequires:  yast2-packager >= 4.2.27
 # using /usr/bin/udevadm
 BuildRequires:  yast2-storage-ng >= 4.2.71
-## y2remote based version
-BuildRequires:  yast2-network >= 4.0.13
+# Y2Network::NtpServer
+BuildRequires:  yast2-network >= 4.2.55
 # new root password cwm widget
 BuildRequires:  yast2-users >= 3.2.8
 # storage-ng based version
@@ -77,7 +77,8 @@
 Requires:       yast2-services-manager >= 3.2.1
 # Yast::OSRelease.ReleaseVersionHumanReadable
 Requires:       yast2 >= 4.2.56
-Requires:       yast2-network >= 4.0.13
+# Y2Network::NtpServer
+Requires:       yast2-network >= 4.2.55
 # for AbortException and handle direct abort
 Requires:       yast2-ruby-bindings >= 4.0.6
 # for the first/second stage of installation
@@ -192,6 +193,8 @@
 
 # systemd service files
 %{_unitdir}
+# yupdate script
+%{_bindir}/
 %{yast_clientdir}
 %{yast_moduledir}
 %{yast_desktopdir}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/src/lib/installation/dialogs/ntp_setup.rb 
new/yast2-installation-4.2.35/src/lib/installation/dialogs/ntp_setup.rb
--- old/yast2-installation-4.2.31/src/lib/installation/dialogs/ntp_setup.rb     
2020-02-18 10:19:25.000000000 +0100
+++ new/yast2-installation-4.2.35/src/lib/installation/dialogs/ntp_setup.rb     
2020-02-21 13:28:20.000000000 +0100
@@ -22,6 +22,7 @@
 require "yast"
 require "cwm/dialog"
 require "installation/widgets/ntp_server"
+require "y2network/ntp_server"
 
 module Installation
   module Dialogs
@@ -68,7 +69,7 @@
         # TODO: use Yast::NtpClient.ntp_conf if configured
         # to better handle going back
         servers = dhcp_ntp_servers
-        servers = [ntp_fallback] if servers.empty? && 
default_ntp_setup_enabled?
+        servers = [ntp_fallback.hostname] if servers.empty? && 
default_ntp_setup_enabled?
 
         servers
       end
@@ -98,23 +99,11 @@
 
       # The fallback servers for NTP configuration
       #
-      # It propose a random pool server in range 0..3
+      # It propose a random server from the default pool
       #
-      # @return [String] the fallback servers
+      # @return [Y2Network::NtpServer] the fallback server
       def ntp_fallback
-        "#{rand(4)}.#{ntp_host}.pool.ntp.org"
-      end
-
-      def ntp_host
-        # copied from timezone/dialogs.rb:
-        base_products = Yast::Product.FindBaseProducts
-
-        if base_products.any? { |p| p["name"] =~ /openSUSE/i }
-          "opensuse"
-        else
-          # TODO: use a SUSE server when available in the future
-          "novell"
-        end
+        Y2Network::NtpServer.default_servers.sample
       end
     end
   end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/src/lib/installation/updates_manager.rb 
new/yast2-installation-4.2.35/src/lib/installation/updates_manager.rb
--- old/yast2-installation-4.2.31/src/lib/installation/updates_manager.rb       
2020-02-18 10:19:25.000000000 +0100
+++ new/yast2-installation-4.2.35/src/lib/installation/updates_manager.rb       
2020-02-21 13:28:20.000000000 +0100
@@ -16,6 +16,7 @@
 require "pathname"
 require "installation/driver_update"
 require "installation/update_repository"
+require "yast2/execute"
 
 module Installation
   # This class takes care of managing installer updates
@@ -110,14 +111,27 @@
     # @see Installation::UpdateRepository#apply
     # @see Installation::DriverUpdate#apply
     # @see #repositories
+    # @raise CouldNotUpdateControlFile
     def apply_all
       (repositories + driver_updates).each(&:apply)
       repositories.each(&:cleanup)
+      replace_control_file
     end
 
     # Determines whether the manager has repositories with updates
     def repositories?
       !repositories.empty?
     end
+
+  private
+
+    NEW_CONTROL_FILE_PATH = "/usr/lib/skelcd/CD1/control.xml".freeze
+
+    # Replaces the control file with the updated one (if it exists)
+    def replace_control_file
+      return unless File.exist?(NEW_CONTROL_FILE_PATH)
+      log.info("Updating control.xml file in inst-sys")
+      Yast::Execute.locally!("/sbin/adddir", 
File.dirname(NEW_CONTROL_FILE_PATH), "/")
+    end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/test/dialogs/ntp_setup_test.rb 
new/yast2-installation-4.2.35/test/dialogs/ntp_setup_test.rb
--- old/yast2-installation-4.2.31/test/dialogs/ntp_setup_test.rb        
2020-02-18 10:19:25.000000000 +0100
+++ new/yast2-installation-4.2.35/test/dialogs/ntp_setup_test.rb        
2020-02-21 13:28:20.000000000 +0100
@@ -36,16 +36,22 @@
     end
 
     context "no NTP server set in DHCP and default NTP is enabled in 
control.xml" do
+      let(:default_servers) do
+        [
+          Y2Network::NtpServer.new("0.opensuse.pool.ntp.org"),
+          Y2Network::NtpServer.new("1.opensuse.pool.ntp.org")
+        ]
+      end
+
       before do
         allow(Yast::ProductFeatures).to receive(:GetBooleanFeature)
           .with("globals", "default_ntp_setup").and_return(true)
-        allow(Yast::Product).to receive(:FindBaseProducts)
-          .and_return(["name" => "openSUSE-Tumbleweed-Kubic"])
+        allow(Y2Network::NtpServer).to 
receive(:default_servers).and_return(default_servers)
       end
 
-      it "proposes to use a random openSUSE pool server" do
+      it "proposes to use a random server from the default pool" do
         expect(::Installation::Widgets::NtpServer).to 
receive(:new).and_wrap_original do |original, arg|
-          expect(arg.first).to match(/\A[0-3]\.opensuse\.pool\.ntp\.org\z/)
+          expect(default_servers.map(&:hostname)).to include(arg.first)
           original.call(arg)
         end
         subject.run
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-installation-4.2.31/test/helpers.rb 
new/yast2-installation-4.2.35/test/helpers.rb
--- old/yast2-installation-4.2.31/test/helpers.rb       2020-02-18 
10:19:25.000000000 +0100
+++ new/yast2-installation-4.2.35/test/helpers.rb       2020-02-21 
13:28:20.000000000 +0100
@@ -20,4 +20,21 @@
   def load_fixture(*path)
     File.read(fixtures_dir(*path))
   end
+
+  # Execute the passed block and capture both $stdout and $stderr streams.
+  # @return [Array] A [STDOUT, STDERR] pair
+  def capture_stdio
+    stdout_orig = $stdout
+    stderr_orig = $stderr
+
+    begin
+      $stdout = StringIO.new
+      $stderr = StringIO.new
+      yield
+      [$stdout.string, $stderr.string]
+    ensure
+      $stdout = stdout_orig
+      $stderr = stderr_orig
+    end
+  end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-installation-4.2.31/test/test_helper.rb 
new/yast2-installation-4.2.35/test/test_helper.rb
--- old/yast2-installation-4.2.31/test/test_helper.rb   2020-02-18 
10:19:25.000000000 +0100
+++ new/yast2-installation-4.2.35/test/test_helper.rb   2020-02-21 
13:28:20.000000000 +0100
@@ -45,8 +45,9 @@
     add_filter "/test/"
   end
 
+  bindir = File.expand_path("../../bin", __FILE__)
   # For coverage we need to load all ruby files
-  SimpleCov.track_files("#{srcdir}/**/*.rb")
+  SimpleCov.track_files("{#{srcdir}/**/*.rb,#{bindir}/*}")
 
   # use coveralls for on-line code coverage reporting at Travis CI
   if ENV["TRAVIS"]
@@ -63,3 +64,14 @@
   config.include Yast::I18n # available in it/let/before/...
   config.include Helpers    # custom helpers
 end
+
+# require the "bin/yupdate" script for testing it, unfortunately we cannot use
+# a simple require/require_relative for it, let's share the workaround in a 
single place
+def require_yupdate
+  # - "require"/"require_relative" do not work for files without the ".rb" 
extension
+  # - adding the "yupdate.rb" -> "yupdate" symlink works but then code coverage
+  #   somehow does not find the executed code and reports zero coverage there
+  # - "load" works fine but we need to ensure calling it only once
+  #   to avoid the "already initialized constant" Ruby warnings
+  load File.expand_path("../bin/yupdate", __dir__) unless defined?(YUpdate)
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/test/updates_manager_test.rb 
new/yast2-installation-4.2.35/test/updates_manager_test.rb
--- old/yast2-installation-4.2.31/test/updates_manager_test.rb  2020-02-18 
10:19:25.000000000 +0100
+++ new/yast2-installation-4.2.35/test/updates_manager_test.rb  2020-02-21 
13:28:20.000000000 +0100
@@ -102,8 +102,12 @@
   end
 
   describe "#apply_all" do
+    let(:new_control_file?) { false }
+
     before do
       allow(manager).to receive(:repositories).and_return([repo0, repo1])
+      allow(File).to receive(:exist?).with("/usr/lib/skelcd/CD1/control.xml")
+        .and_return(new_control_file?)
     end
 
     it "applies all the updates" do
@@ -124,6 +128,22 @@
         manager.apply_all
       end
     end
+
+    context "when a new control file is available" do
+      let(:new_control_file?) { true }
+
+      it "updates the control file" do
+        expect(Yast::Execute).to receive(:locally!)
+          .with("/sbin/adddir", "/usr/lib/skelcd/CD1", "/")
+        manager.apply_all
+      end
+    end
+
+    it "does not replace the control file" do
+      expect(Yast::Execute).to_not receive(:locally!)
+        .with("/sbin/adddir", /skelcd/, "/")
+      manager.apply_all
+    end
   end
 
   describe "#repositories?" do
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/test/yupdate/gem_installer_test.rb 
new/yast2-installation-4.2.35/test/yupdate/gem_installer_test.rb
--- old/yast2-installation-4.2.31/test/yupdate/gem_installer_test.rb    
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-4.2.35/test/yupdate/gem_installer_test.rb    
2020-02-21 13:28:20.000000000 +0100
@@ -0,0 +1,40 @@
+#! /usr/bin/env rspec
+
+require_relative "../test_helper"
+require_yupdate
+
+describe YUpdate::GemInstaller do
+  describe "#install_required_gems" do
+    before do
+      allow(File).to receive(:exist?).with("/usr/bin/rake").and_return(true)
+      allow_any_instance_of(YUpdate::OverlayFS).to receive :create
+      allow(subject).to receive(:gem).and_raise(Gem::LoadError)
+      allow(subject).to receive(:system)
+    end
+
+    it "installs the required gems" do
+      expect(subject).to receive(:system).with(
+        "gem",
+        "install",
+        "--no-document",
+        "--no-format-exec",
+        "yast-rake"
+      )
+      subject.install_required_gems
+    end
+
+    it "skips already installed gems" do
+      expect(subject).to receive(:gem).and_return(true)
+      expect(subject).to_not receive(:system)
+      subject.install_required_gems
+    end
+
+    it "it makes the gem directory writable" do
+      ovfs = double
+      expect(YUpdate::OverlayFS).to 
receive(:new).with(Gem.dir).and_return(ovfs)
+      expect(ovfs).to receive(:create)
+
+      subject.install_required_gems
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/test/yupdate/inst_sys_test.rb 
new/yast2-installation-4.2.35/test/yupdate/inst_sys_test.rb
--- old/yast2-installation-4.2.31/test/yupdate/inst_sys_test.rb 1970-01-01 
01:00:00.000000000 +0100
+++ new/yast2-installation-4.2.35/test/yupdate/inst_sys_test.rb 2020-02-21 
13:28:20.000000000 +0100
@@ -0,0 +1,39 @@
+#! /usr/bin/env rspec
+
+require_relative "../test_helper"
+require_yupdate
+
+describe YUpdate::InstSys do
+  let(:file) { "/.packages.initrd" }
+
+  describe ".check!" do
+    context "when running in an inst-sys" do
+      before do
+        expect(File).to receive(:exist?).with(file).and_return(true)
+      end
+
+      it "does not exit" do
+        expect(described_class).to_not receive(:exit)
+        described_class.check!
+      end
+    end
+
+    context "when running in a normal system" do
+      before do
+        expect(File).to receive(:exist?).with(file).and_return(false)
+        allow(described_class).to receive(:exit).with(1)
+      end
+
+      it "exits with status 1" do
+        expect(described_class).to receive(:exit).with(1)
+        # capture the std streams just to not break the rspec output
+        capture_stdio { described_class.check! }
+      end
+
+      it "prints an error on STDERR" do
+        _stdout, stderr = capture_stdio { described_class.check! }
+        expect(stderr).to match(/ERROR: .*inst-sys/)
+      end
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.2.31/test/yupdate/overlayfs_test.rb 
new/yast2-installation-4.2.35/test/yupdate/overlayfs_test.rb
--- old/yast2-installation-4.2.31/test/yupdate/overlayfs_test.rb        
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-4.2.35/test/yupdate/overlayfs_test.rb        
2020-02-21 13:28:20.000000000 +0100
@@ -0,0 +1,35 @@
+#! /usr/bin/env rspec
+
+require_relative "../test_helper"
+require_yupdate
+
+describe YUpdate::OverlayFS do
+  # testing data
+  let(:orig_path) { "/test/test__test" }
+  let(:escaped_path) { "/var/lib/YaST2/overlayfs/upper/_test_test____test" }
+
+  before do
+    # mock the checks for existing directory
+    allow(File).to receive(:realpath) { |d| d }
+    allow(File).to receive(:directory?).and_return(true)
+  end
+
+  describe "#upperdir" do
+    it "returns the path in the 'upper' subdirectory" do
+      o = YUpdate::OverlayFS.new(orig_path)
+      expect(o.upperdir).to match(/\/upper\//)
+    end
+  end
+
+  describe ".escape_path" do
+    it "escapes the path and adds a prefix" do
+      expect(YUpdate::OverlayFS.escape_path("upper", orig_path)).to 
eq(escaped_path)
+    end
+  end
+
+  describe ".unescape_path" do
+    it "unescapes the path and removes the prefix" do
+      expect(YUpdate::OverlayFS.unescape_path(escaped_path)).to eq(orig_path)
+    end
+  end
+end


Reply via email to