diff --git a/yastd/Gemfile b/yastd/Gemfile index eba16c4b3b..0f6189f132 100644 --- a/yastd/Gemfile +++ b/yastd/Gemfile @@ -8,3 +8,4 @@ gem "ruby-dbus" # Required by YaST gem "cheetah" gem "fast_gettext" +gem "nokogiri" diff --git a/yastd/lib/yast2/dbus/installer.rb b/yastd/lib/yast2/dbus/installer.rb index 1460197430..18107fe632 100644 --- a/yastd/lib/yast2/dbus/installer.rb +++ b/yastd/lib/yast2/dbus/installer.rb @@ -153,6 +153,18 @@ def initialize(installer, logger, *args) dbus_signal :PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as" + + # signal for installation progress + # + # parameters: + # + # - message: localized string describing current step + # - total_steps: how many steps will be done + # - current_step: current step. Always in range of 0..total_steps + # - total_substeps: total count of smaller steps done as part of big step. Can be 0, which means no substeps defined + # - current_substep: current substep. Always in range of 0..total_substeps + dbus_signal :Progress, + "message:s, total_steps:t, current_step: t, total_substeps: t, current_substep: t" end end end diff --git a/yastd/lib/yast2/dbus/installer_client.rb b/yastd/lib/yast2/dbus/installer_client.rb index 94383a5321..0266a91129 100644 --- a/yastd/lib/yast2/dbus/installer_client.rb +++ b/yastd/lib/yast2/dbus/installer_client.rb @@ -46,6 +46,16 @@ def status=(id) ) end + # Callback that triggers when installer make progress + # + # @param id [Integer] Installer status + # @see InstallerStatus + def report_progress(msg, total_steps, step, total_substeps, substep) + installer_obj["org.freedesktop.DBus.Properties"].Progress( + msg, total_steps, step, total_substeps, substep + ) + end + private # @return [DBus::ProxyObject] Returns a proxy object for diff --git a/yastd/lib/yast2/installer.rb b/yastd/lib/yast2/installer.rb index b0174158d2..5195fe00e5 100644 --- a/yastd/lib/yast2/installer.rb +++ b/yastd/lib/yast2/installer.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true require "yast" -require "y2packager/product" require "y2storage" require "yast2/installer_status" +require "yast2/software" +require "yast2/progress" require "yast2/dbus/installer_client" Yast.import "CommandLine" require "dbus" +require "forwardable" # YaST specific code lives under this namespace module Yast2 @@ -22,13 +24,17 @@ module Yast2 class Installer class InvalidValue < StandardError; end + extend Forwardable + DEFAULT_LANGUAGE = "en_US" - attr_reader :disks, :languages, :products - attr_reader :disk, :product + attr_reader :disks, :languages + attr_reader :disk attr_reader :logger attr_reader :language + def_delegators :@software, :products, :product + # @return [InstallerStatus] attr_accessor :status @@ -48,6 +54,8 @@ def initialize(dbus_client:, logger: nil) @status = InstallerStatus::IDLE @dbus_client = dbus_client @logger = logger || Logger.new(STDOUT) + @software = Software.new(@logger) + @progress = Progress.new(dbus_client) end def options @@ -67,7 +75,8 @@ def options def probe change_status(InstallerStatus::PROBING) probe_languages - probe_software + @software.probe + @software.propose probe_storage true rescue StandardError => e @@ -84,9 +93,9 @@ def disk=(name) end def product=(name) - raise InvalidValue unless products.map(&:name).include?(name) - - @product = name + @software.select_product(name) + rescue + raise InvalidValue end def language=(name) @@ -103,12 +112,12 @@ def install Yast::Installation.destdir = "/mnt" logger.info "Installing(partitioning)" change_status(InstallerStatus::PARTITIONING) - # Yast::WFM.CallFunction(["inst_prepdisk"], []) + Yast::WFM.CallFunction(["inst_prepdisk"], []) sleep 5 # Install software logger.info "Installing(installing software)" change_status(InstallerStatus::INSTALLING) - sleep 5 + @software.install(@progress) logger.info "Installing(finished)" change_status(InstallerStatus::IDLE) end @@ -126,18 +135,6 @@ def change_status(new_status) end end - def probe_software - logger.info "Probing software" - Yast.import "Pkg" - Yast.import "PackageLock" - Yast::Pkg.TargetInitialize("/") - Yast::Pkg.TargetLoad - Yast::Pkg.SourceRestore - Yast::Pkg.SourceLoad - @products = Y2Packager::Product.all - @product = @products.first&.name - end - # Returns the list of known languages # # @return [Hash] diff --git a/yastd/lib/yast2/package_callbacks.rb b/yastd/lib/yast2/package_callbacks.rb new file mode 100644 index 0000000000..1b88a15478 --- /dev/null +++ b/yastd/lib/yast2/package_callbacks.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +# 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" + +Yast.import "Pkg" + +# YaST specific code lives under this namespace +module Yast2 + # This class represents the installer status + class PackageCallbacks + class << self + attr_reader :progress + + def setup(progress) + new(progress).setup + end + + def initialize(progress) + @progress = progress + end + + def setup + Pkg.CallbackDonePackage( + fun_ref(method(:package_installed), "string (integer, string)") + + end + + private + + def fun_ref(method, signature) + Yast::FunRef.new(method, signature) + end + + # TODO: error handling + def package_installed(_error, _reason) + progress.package_installed + + "" + end + end +end + diff --git a/yastd/lib/yast2/progress.rb b/yastd/lib/yast2/progress.rb new file mode 100644 index 0000000000..1839df3dac --- /dev/null +++ b/yastd/lib/yast2/progress.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +# 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. + +# YaST specific code lives under this namespace +module Yast2 + # This class represents the installer status + class Progress + def initialize(dbus_obj) + @dbus_obj = dbus_obj + @total_pkgs = 0 + @remaining_pkgs = 0 + end + + def packages_to_install=(value) + @total_pkgs = @remaining_pkgs = value + end + + def package_installed + @remaining_pkgs -= 1 + @dbus_obj.report_progress( + # TODO: localization + "Installing packages (#{@remaining_pkgs} remains)", + 1, 0, @total_pkgs, @total_pkgs - @remaining_pkgs + ) + end + end +end diff --git a/yastd/lib/yast2/software.rb b/yastd/lib/yast2/software.rb new file mode 100644 index 0000000000..44c18f4b33 --- /dev/null +++ b/yastd/lib/yast2/software.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +# 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 "yast2/package_callbacks" +require "y2packager/product" + +Yast.import "Pkg" +Yast.import "PackageInstallation" + +# YaST specific code lives under this namespace +module Yast2 + # This class is responsible for software handling + class Software + attr_reader :product, :products + + def initialize(logger) + @logger = logger + @products = [] + @product = nil + end + + def select_product(name) + raise ArgumentError unless @products.any? { |p| p.name == name } + + @product = name + end + + def probe + logger.info "Probing software" + Yast::Pkg.TargetInitialize("/") + Yast::Pkg.TargetLoad + Yast::Pkg.SourceRestore + Yast::Pkg.SourceLoad + @products = Y2Packager::Product.all + end + + def propose + @product = @products.first&.name + + raise "No Product Available" unless @product + + Yast::Packages.Proposal(force_reset = true, reinit = true, _simple = true) + # do not return proposal hash, so intentional nil here + nil + end + + def install(progress) + count_packages(progress) + + PackageCallbacks.setup(progress) + + # TODO: error handling + Yast::Pkg.TargetInitialize(Installation.destdir) + commit_result = Yast::PackageInstallation.Commit({}) + + if commit_result.nil? || commit_result.empty? + log.error("Commit failed") + raise Yast::Pkg.LastError + end + end + + private + + attr_reader :logger + + def count_packages(progress) + count = Yast::Pkg.PkgMediaCount.reduce(0) { |sum, res| sum + res.reduce(0, :+) } + + progress.packages_to_install = count + end + end +end