• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yast / yast-autoinstallation / 13494013411

24 Feb 2025 08:44AM UTC coverage: 69.018% (+0.03%) from 68.993%
13494013411

push

github

web-flow
Merge pull request #881 from yast/merge_SLE-15-SP7

Added pervasive encryption fields (master)

6429 of 9315 relevant lines covered (69.02%)

10.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.77
/src/modules/AutoinstSoftware.rb
1
# File:  modules/AutoinstSoftware.ycp
2
# Package:  Autoyast
3
# Summary:  Software
4
# Authors:  Anas Nashif <nashif@suse.de>
5
#
6
# $Id$
7
#
8
require "yast"
1✔
9
require "y2storage"
1✔
10
require "y2packager/product"
1✔
11
require "y2packager/resolvable"
1✔
12
require "autoinstall/package_searcher"
1✔
13
require "autoinstall/entries/registry"
1✔
14

15
module Yast
1✔
16
  class AutoinstSoftwareClass < Module
1✔
17
    include Yast::Logger
1✔
18

19
    # This file is created by pkg-bindings when the package solver fails,
20
    # it contains some details of the failure
21
    BAD_LIST_FILE = "/var/log/YaST2/badlist".freeze
1✔
22

23
    # Maximal amount of packages which will be shown
24
    # in a popup.
25
    MAX_PACKAGE_VIEW = 5
1✔
26

27
    def main
1✔
28
      Yast.import "UI"
38✔
29
      Yast.import "Pkg"
38✔
30
      textdomain "autoinst"
38✔
31

32
      Yast.import "Profile"
38✔
33
      Yast.import "Summary"
38✔
34
      Yast.import "Stage"
38✔
35
      Yast.import "SpaceCalculation"
38✔
36
      Yast.import "Packages"
38✔
37
      Yast.import "Popup"
38✔
38
      Yast.import "Report"
38✔
39
      Yast.import "Kernel"
38✔
40
      Yast.import "AutoinstConfig"
38✔
41
      Yast.import "AutoinstFunctions"
38✔
42
      Yast.import "ProductControl"
38✔
43
      Yast.import "Mode"
38✔
44
      Yast.import "Misc"
38✔
45
      Yast.import "Directory"
38✔
46
      Yast.import "Package"
38✔
47
      Yast.import "ProductFeatures"
38✔
48
      Yast.import "WorkflowManager"
38✔
49
      Yast.import "Product"
38✔
50

51
      Yast.include self, "autoinstall/io.rb"
38✔
52

53
      # All shared data are in yast2.rpm to break cyclic dependencies
54
      Yast.import "AutoinstData"
38✔
55
      Yast.import "PackagesProposal"
38✔
56

57
      @Software = {}
38✔
58

59
      # patterns
60
      @patterns = []
38✔
61

62
      # Kernel, force type of kernel to be installed
63
      @kernel = ""
38✔
64

65
      # default value of settings modified
66
      @modified = false
38✔
67

68
      @inst = []
38✔
69
      @all_xpatterns = []
38✔
70
      @packagesAvailable = []
38✔
71
      @patternsAvailable = []
38✔
72

73
      @instsource = ""
38✔
74
      AutoinstSoftware()
38✔
75
    end
76

77
    # Function sets internal variable, which indicates, that any
78
    # settings were modified, to "true"
79
    def SetModified
1✔
80
      @modified = true
2✔
81

82
      nil
2✔
83
    end
84

85
    # Functions which returns if the settings were modified
86
    # @return [Boolean]  settings were modified
87
    def GetModified
1✔
88
      @modified
6✔
89
    end
90

91
    # Import data
92
    # @param [Hash] settings settings to be imported
93
    # @return true on success
94
    def Import(settings)
1✔
95
      settings = deep_copy(settings)
32✔
96
      @Software = deep_copy(settings)
32✔
97
      @patterns = settings.fetch("patterns", [])
32✔
98
      @instsource = settings.fetch("instsource", "")
32✔
99

100
      @packagesAvailable = Pkg.GetPackages(:available, true)
32✔
101
      @patternsAvailable = Y2Packager::Resolvable.find(
32✔
102
        kind:         :pattern,
103
        user_visible: true
104
      ).map(&:name)
105

106
      regexFound = []
32✔
107
      Ops.set(
32✔
108
        settings,
109
        "packages",
110
        Builtins.filter(Ops.get_list(settings, "packages", [])) do |want_pack|
111
          next true if !Builtins.issubstring(want_pack, "/")
34✔
112

113
          want_pack = Builtins.deletechars(want_pack, "/")
×
114
          Builtins.foreach(@packagesAvailable) do |pack|
×
115
            Builtins.y2milestone("matching %1 against %2", pack, want_pack)
×
116
            if Builtins.regexpmatch(pack, want_pack)
×
117
              regexFound = Builtins.add(regexFound, pack)
×
118
              Builtins.y2milestone("match")
×
119
            end
120
          end
121
          false
×
122
        end
123
      )
124
      Ops.set(
32✔
125
        settings,
126
        "packages",
127
        Convert.convert(
128
          Builtins.union(Ops.get_list(settings, "packages", []), regexFound),
129
          from: "list",
130
          to:   "list <string>"
131
        )
132
      )
133

134
      regexFound = []
32✔
135
      @patterns = Builtins.filter(@patterns) do |want_patt|
32✔
136
        next true if !Builtins.issubstring(want_patt, "/")
34✔
137

138
        want_patt = Builtins.deletechars(want_patt, "/")
×
139
        Builtins.foreach(patternsAvailable) do |patt|
×
140
          Builtins.y2milestone("matching %1 against %2", patt, want_patt)
×
141
          if Builtins.regexpmatch(patt, want_patt)
×
142
            regexFound = Builtins.add(regexFound, patt)
×
143
            Builtins.y2milestone("match")
×
144
          end
145
        end
146
        false
×
147
      end
148
      @patterns = Convert.convert(
32✔
149
        Builtins.union(@patterns, regexFound),
150
        from: "list",
151
        to:   "list <string>"
152
      )
153

154
      to_install = settings.fetch("packages", [])
32✔
155
      unless to_install.empty?
32✔
156
        PackagesProposal.AddResolvables("autoyast", :package, to_install, optional: true)
21✔
157
      end
158
      @kernel = settings.fetch("kernel", "")
32✔
159

160
      addPostPackages(settings.fetch("post-packages", []))
32✔
161
      AutoinstData.post_patterns = settings.fetch("post-patterns", [])
32✔
162
      to_remove = settings.fetch("remove-packages", [])
32✔
163
      PackagesProposal.AddTaboos("autoyast", :package, to_remove) unless to_remove.empty?
32✔
164

165
      true
32✔
166
    end
167

168
    # Add the given list of packages to the one to be installed
169
    #
170
    # @param pkglist [Array<String>] list of additional packages to be installed
171
    def add_additional_packages(pkglist)
1✔
172
      available = pkglist & @packagesAvailable
2✔
173
      return if available.empty?
2✔
174

175
      PackagesProposal.AddResolvables("autoyast", :package, available, optional: true)
1✔
176
    end
177

178
    def AddYdepsFromProfile(entries)
1✔
179
      Builtins.y2milestone("AddYdepsFromProfile entries %1", entries)
×
180
      pkglist = []
×
181
      # Evaluating packages via RPM supplements ( e.g. autoyast(kdump) )
182
      req_packages = Y2Autoinstallation::PackagerSearcher.new(entries).evaluate_via_rpm
×
183
      entries.reject! do |e|
×
184
        packs = req_packages[e]
×
185
        if packs.empty?
×
186
          false
×
187
        else
188
          log.info "AddYdepsFromProfile add packages #{packs} for entry #{e}"
×
189
          pkglist += packs
×
190
          true
×
191
        end
192
      end
193
      pkglist.uniq!
×
194
      Builtins.y2milestone("AddYdepsFromProfile pkglist %1", pkglist)
×
195
      add_additional_packages(pkglist)
×
196
    end
197

198
    # Constructer
199
    def AutoinstSoftware
1✔
200
      if Stage.cont && Mode.autoinst
38✔
201
        Pkg.TargetInit("/", false)
×
202
        Import(Ops.get_map(Profile.current, "software", {}))
×
203
      end
204
      nil
38✔
205
    end
206

207
    # Export data
208
    # @return dumped settings (later acceptable by Import())
209
    def Export
1✔
210
      s = {}
2✔
211
      s["kernel"] = @kernel if !@kernel.empty?
2✔
212
      s["patterns"] = @patterns if !@patterns.empty?
2✔
213

214
      pkgs_to_install = Yast::PackagesProposal.GetResolvables("autoyast", :package, optional: true)
2✔
215
      s["packages"] = pkgs_to_install unless pkgs_to_install.empty?
2✔
216

217
      pkg_post = AutoinstData.post_packages
2✔
218
      s["post-packages"] = pkg_post unless pkg_post.empty?
2✔
219

220
      pkgs_to_remove = Yast::PackagesProposal.GetTaboos("autoyast", :package)
2✔
221
      s["remove-packages"] = pkgs_to_remove unless pkgs_to_remove.empty?
2✔
222

223
      s["instsource"] = @instsource
2✔
224

225
      # In the installed system the flag solver.onlyRequires in zypp.conf is
226
      # set to true. This differs from the installation process. So we have
227
      # to set "install_recommended" to true in order to reflect the
228
      # installation process and cannot use the package bindings. (bnc#990494)
229
      # OR: Each product (e.g. CASP) can set it in the control.xml file.
230
      rec = ProductFeatures.GetStringFeature(
2✔
231
        "software",
232
        "clone_install_recommended_default"
233
      )
234
      s["install_recommended"] = rec != "no"
2✔
235

236
      products = Product.FindBaseProducts
2✔
237
      raise "Found multiple base products" if products.size > 1
2✔
238

239
      s["products"] = products.map { |x| x["name"] }
2✔
240

241
      s
1✔
242
    end
243

244
    # Summary
245
    # @return Html formatted configuration summary
246
    def Summary
1✔
247
      summary = ""
×
248

249
      summary = Summary.AddHeader(summary, _("Selected Patterns"))
×
250
      if Ops.greater_than(Builtins.size(@patterns), 0)
×
251
        summary = Summary.OpenList(summary)
×
252
        Builtins.foreach(@patterns) do |a|
×
253
          summary = Summary.AddListItem(summary, a)
×
254
        end
255
        summary = Summary.CloseList(summary)
×
256
      else
257
        summary = Summary.AddLine(summary, Summary.NotConfigured)
×
258
      end
259
      summary = Summary.AddHeader(summary, _("Individually Selected Packages"))
×
260
      pkgs_to_install = Yast::PackagesProposal.GetResolvables("autoyast", :package)
×
261
      summary = Summary.AddLine(
×
262
        summary,
263
        Builtins.sformat("%1", Builtins.size(pkgs_to_install))
264
      )
265

266
      summary = Summary.AddHeader(summary, _("Packages to Remove"))
×
267
      pkgs_to_remove = Yast::PackagesProposal.GetTaboos("autoyast", :package)
×
268
      summary = Summary.AddLine(
×
269
        summary,
270
        Builtins.sformat("%1", Builtins.size(pkgs_to_remove))
271
      )
272

273
      if @kernel != ""
×
274
        summary = Summary.AddHeader(summary, _("Force Kernel Package"))
×
275
        summary = Summary.AddLine(summary, Builtins.sformat("%1", @kernel))
×
276
      end
277
      summary
×
278
    end
279

280
    # Compute list of packages selected by user and other packages needed for important
281
    # configuration modules.
282
    # @return [Array] of strings list of packages needed for autoinstallation
283
    def autoinstPackages
1✔
284
      allpackages = []
×
285

286
      # the primary list of packages
287
      allpackages = Convert.convert(
×
288
        Builtins.union(allpackages, Yast::PackagesProposal.GetResolvables("autoyast", :package)),
289
        from: "list",
290
        to:   "list <string>"
291
      )
292

293
      # In autoinst mode, a kernel should not be  available
294
      # in <packages>
295
      if Builtins.size(@kernel) == 0
×
296
        kernel_pkgs = Kernel.ComputePackages
×
297
        allpackages = Convert.convert(
×
298
          Builtins.union(allpackages, kernel_pkgs),
299
          from: "list",
300
          to:   "list <string>"
301
        )
302
      elsif Pkg.IsAvailable(@kernel)
×
303
        allpackages = Builtins.add(allpackages, @kernel)
×
304
        kernel_nongpl = Ops.add(@kernel, "-nongpl")
×
305

306
        allpackages = Builtins.add(allpackages, kernel_nongpl) if Pkg.IsAvailable(kernel_nongpl)
×
307
      else
308
        Builtins.y2warning("%1 not available, using kernel-default", @kernel)
×
309
        kernel_pkgs = Kernel.ComputePackages
×
310
        allpackages = Convert.convert(
×
311
          Builtins.union(allpackages, kernel_pkgs),
312
          from: "list",
313
          to:   "list <string>"
314
        )
315
      end
316

317
      deep_copy(allpackages)
×
318
    end
319

320
    # Configure software settings
321
    #
322
    # @return [Boolean]
323
    def Write
1✔
324
      ok = true
7✔
325

326
      Packages.Init(true)
7✔
327

328
      # products selected by YaST (not by dependencies)
329
      selected_products = Y2Packager::Resolvable.find(kind: :product,
7✔
330
        status: :selected, transact_by: :app_high).map(&:name)
331
      log.info("Products selected by YaST: #{selected_products.inspect}")
7✔
332

333
      # Resetting package selection of previous runs. This is needed
334
      # because it could be that additional repositories
335
      # are available meanwhile. (bnc#979691)
336
      Pkg.PkgApplReset
7✔
337

338
      # reselect the products which have been reset by the previous call
339
      selected_products.each { |name| Pkg.ResolvableInstall(name, :product) }
14✔
340

341
      sw_settings = Profile.current.fetch("software", {})
7✔
342
      Pkg.SetSolverFlags(
7✔
343
        "ignoreAlreadyRecommended" => Mode.normal,
344
        "onlyRequires"             => !sw_settings.fetch("install_recommended", true)
345
      )
346

347
      failed = []
7✔
348

349
      # Add storage-related software packages (filesystem tools etc.) to the
350
      # set of packages to be installed.
351
      storage_features = Y2Storage::StorageManager.instance.staging.used_features
7✔
352
      pkg_handler = Y2Storage::PackageHandler.new(storage_features.pkg_list)
7✔
353
      pkg_handler.set_proposal_packages
7✔
354

355
      # switch for recommended patterns installation (workaround for our very weird pattern design)
356
      if sw_settings.fetch("install_recommended", false) == false
7✔
357
        # set SoftLock to avoid the installation of recommended patterns (#159466)
358
        Y2Packager::Resolvable.find(kind: :pattern).each do |p|
7✔
359
          Pkg.ResolvableSetSoftLock(p.name, :pattern)
7✔
360
        end
361
      end
362

363
      Builtins.foreach(Builtins.toset(@patterns)) do |p|
7✔
364
        failed = Builtins.add(failed, p) if !Pkg.ResolvableInstall(p, :pattern)
×
365
      end
366

367
      if Ops.greater_than(Builtins.size(failed), 0)
7✔
368
        Builtins.y2error(
×
369
          "Error while setting pattern: %1",
370
          Builtins.mergestring(failed, ",")
371
        )
372
        Report.Warning(
×
373
          Builtins.sformat(
374
            _("Could not set patterns: %1."),
375
            Builtins.mergestring(failed, ",")
376
          )
377
        )
378
      end
379

380
      selected_product = AutoinstFunctions.selected_product
7✔
381
      if selected_product
7✔
382
        log.info "Selecting product #{selected_product.inspect} for installation"
7✔
383
        selected_product.select
7✔
384
      else
385
        log.info "No product has been selected for installation"
×
386
      end
387

388
      SelectPackagesForInstallation()
7✔
389

390
      pkgs_to_remove = PackagesProposal.GetTaboos("autoyast", :package).dup
7✔
391
      computed_packages = Packages.ComputeSystemPackageList
7✔
392
      Builtins.foreach(computed_packages) do |pack2|
7✔
393
        if Ops.greater_than(Builtins.size(@kernel), 0) && pack2 != @kernel &&
7✔
394
            Builtins.search(pack2, "kernel-") == 0
395
          Builtins.y2milestone("taboo for kernel %1", pack2)
1✔
396
          pkgs_to_remove.push(pack2)
1✔
397
        end
398
      end
399

400
      Packages.SelectSystemPatterns(false)
7✔
401

402
      #
403
      # Now remove all packages listed in remove-packages
404
      #
405
      Builtins.y2milestone("Packages to be removed: %1", pkgs_to_remove)
7✔
406
      if Ops.greater_than(Builtins.size(pkgs_to_remove), 0)
7✔
407
        Builtins.foreach(pkgs_to_remove) do |rp|
2✔
408
          # Pkg::ResolvableSetSoftLock( rp, `package ); // FIXME: maybe better Pkg::PkgTaboo(rp) ?
409
          Pkg.PkgTaboo(rp)
2✔
410
        end
411

412
        Pkg.DoRemove(pkgs_to_remove)
2✔
413
      end
414

415
      #
416
      # Solve dependencies
417
      #
418
      if !Pkg.PkgSolve(false)
7✔
419
        # TRANSLATORS: Error message
420
        msg = _("The package resolver run failed. Please check your software " \
1✔
421
                "section in the autoyast profile.")
422
        # TRANSLATORS: Error message, %s is replaced by "/var/log/YaST2/y2log"
423
        msg += "\n" + (_("Additional details can be found in the %s file.") %
1✔
424
          "/var/log/YaST2/y2log")
425

426
        # read the details saved by pkg-bindings
427
        if File.exist?(BAD_LIST_FILE)
1✔
428
          msg += "\n\n"
×
429
          msg += File.read(BAD_LIST_FILE)
×
430
        end
431

432
        Report.LongError(msg)
1✔
433
      end
434

435
      SpaceCalculation.ShowPartitionWarning
7✔
436

437
      ok
7✔
438
    end
439

440
    # Initialize temporary target
441
    def pmInit
1✔
442
      #        string tmproot = AutoinstConfig::tmpDir;
443

444
      #        SCR::Execute(.target.mkdir, tmproot + "/root");
445
      #        Pkg::TargetInit( tmproot + "/root", true);
446
      #        Pkg::TargetInit( "/", true);
447
      Pkg.TargetInit(Convert.to_string(SCR.Read(path(".target.tmpdir"))), true)
×
448
      Builtins.y2milestone("SourceStartCache: %1", Pkg.SourceStartCache(false))
×
449
      nil
×
450
    end
451

452
    # Add post packages
453
    # @param calcpost [Array<String>] list calculated post packages
454
    def addPostPackages(calcpost)
1✔
455
      # filter out already installed packages
456
      calcpost.reject! { |p| Package.Installed(p) }
57✔
457

458
      calcpost = deep_copy(calcpost)
39✔
459
      AutoinstData.post_packages = Convert.convert(
39✔
460
        Builtins.toset(Builtins.union(calcpost, AutoinstData.post_packages)),
461
        from: "list",
462
        to:   "list <string>"
463
      )
464
      nil
39✔
465
    end
466

467
    # returns (hard and soft) locked packages
468
    # @return [Array<String>] list of package names
469
    def locked_packages
1✔
470
      # hard AND soft locks
471
      user_transact_packages(:taboo).concat(user_transact_packages(:available))
2✔
472
    end
473

474
    def install_packages
1✔
475
      # user selected packages which have not been already installed
476
      # rubocop:disable Lint/UselessAssignment
477
      packages = Pkg.FilterPackages(
×
478
        solver_selected = false,
479
        app_selected = true,
480
        user_selected = true,
481
        name_only = true
482
      )
483
      # rubocop:enable Lint/UselessAssignment
484

485
      # user selected packages which have already been installed
486
      installed_by_user = Pkg.GetPackages(:installed, true).select do |pkg_name|
×
487
        Pkg.PkgPropertiesAll(pkg_name).any? do |p|
×
488
          p["on_system_by_user"] && p["status"] == :installed
×
489
        end
490
      end
491

492
      # Filter out kernel and pattern packages
493
      kernel_packages = Pkg.PkgQueryProvides("kernel").collect do |package|
×
494
        package[0]
×
495
      end.compact.uniq
496
      pattern_packages = Pkg.PkgQueryProvides("pattern()").collect do |package|
×
497
        package[0]
×
498
      end.compact.uniq
499

500
      (packages + installed_by_user).uniq.select do |pkg_name|
×
501
        !kernel_packages.include?(pkg_name) &&
×
502
          !pattern_packages.include?(pkg_name)
503
      end
504
    end
505

506
    # Return list of software packages of calling client
507
    # in the installed environment
508
    # @return [Hash] map of installed software package
509
    #    "patterns" -> list<string> addon selections
510
    #    "packages" -> list<string> user selected packages
511
    #      "remove-packages" -> list<string> packages to remove
512
    def ReadHelper
1✔
513
      Pkg.TargetInit("/", false)
×
514
      Pkg.TargetLoad
×
515
      Pkg.SourceStartManager(true)
×
516
      Pkg.PkgSolve(false)
×
517

518
      @all_xpatterns = Y2Packager::Resolvable.find(
×
519
        { kind: :pattern, status: :installed },
520
        [:dependencies]
521
      )
522
      to_install_packages = install_packages
×
523
      patterns = []
×
524

525
      @all_xpatterns.each do |p|
×
526
        if !patterns.include?(p.name)
×
527
          patterns << (p.name.empty? ? "no name" : p.name)
×
528
        end
529
      end
530
      Pkg.TargetFinish
×
531

532
      tmproot = AutoinstConfig.tmpDir
×
533
      SCR.Execute(path(".target.mkdir"), ::File.join(tmproot, "rootclone"))
×
534
      Pkg.TargetInit(Ops.add(tmproot, "/rootclone"), true)
×
535
      Builtins.y2debug("SourceStartCache: %1", Pkg.SourceStartCache(false))
×
536

537
      Pkg.SourceStartManager(true)
×
538
      Pkg.TargetFinish
×
539

540
      new_p = []
×
541
      Builtins.foreach(patterns) do |tmp_pattern|
×
542
        found = @all_xpatterns.find { |p| p.name == tmp_pattern }
×
543
        log.info "xpattern #{found} for pattern #{tmp_pattern}"
×
544
        next unless found
×
545

546
        req = false
×
547
        # kick out hollow patterns (always fullfilled patterns)
548
        (found.dependencies || []).each do |d|
×
549
          next unless Ops.get_string(d, "res_kind", "") == "package" &&
×
550
            (Ops.get_string(d, "dep_kind", "") == "requires" ||
×
551
              Ops.get_string(d, "dep_kind", "") == "recommends")
552

553
          req = true
×
554
        end
555
        # workaround for our pattern design
556
        # a pattern with no requires at all is always fullfilled of course
557
        # you can fullfill the games pattern with no games installed at all
558
        new_p << tmp_pattern if req
×
559
      end
560
      patterns = new_p
×
561
      log.info "found patterns #{patterns}"
×
562

563
      {
564
        "patterns"        => patterns.sort,
×
565
        # Currently we do not have any information about user deleted packages in
566
        # the installed system.
567
        # In order to prevent a reinstallation we can take the locked packages at least.
568
        # (bnc#888296)
569
        "remove-packages" => locked_packages,
570
        "packages"        => to_install_packages
571
      }
572
    end
573

574
    # Return list of software packages, patterns which have been selected
575
    # by the user and have to be installed or removed.
576
    # The evaluation will be called while the yast installation workflow.
577
    # @return [Hash] map of to be installed/removed packages/patterns
578
    #    "patterns" -> list<string> of selected patterns
579
    #    "packages" -> list<string> user selected packages
580
    #           "remove-packages" -> list<string> packages to remove
581
    def read_initial_stage
1✔
582
      install_patterns =
583
        Y2Packager::Resolvable.find(kind: :pattern, user_visible: true).map do |pattern|
×
584
          # Do not take care about if the pattern has been selected by the user or the product
585
          # definition, cause we need a base selection here for the future
586
          # autoyast installation. (bnc#882886)
587
          pattern.name if pattern.status == :selected || pattern.status == :installed
×
588
        end
589

590
      software = {}
×
591
      software["packages"] = install_packages
×
592
      software["patterns"] = install_patterns.compact
×
593
      software["remove-packages"] = locked_packages
×
594
      Builtins.y2milestone("autoyast software selection: %1", software)
×
595
      deep_copy(software)
×
596
    end
597

598
    def Read
1✔
599
      Import((Stage.initial ? read_initial_stage : ReadHelper()))
×
600
    end
601

602
    def SavePackageSelection
1✔
603
      @saved_package_selection = Read()
×
604
    end
605

606
    def SavedPackageSelection
1✔
607
      @saved_package_selection
×
608
    end
609

610
    # Selects given product (see Y2Packager::Product) and merges its workflow
611
    def merge_product(product)
1✔
612
      raise ArgumentError, "Base product expected" if !product
27✔
613

614
      log.info("AutoinstSoftware::merge_product - using product: #{product.name}")
27✔
615
      product.select
27✔
616

617
      WorkflowManager.merge_product_workflow(product)
27✔
618

619
      # Adding needed autoyast packages if a second stage is needed.
620
      # Could have been changed due merging a products
621
      log.info("Checking new second stage requirement.")
27✔
622
      Profile.softwareCompat
27✔
623
    end
624

625
    def SelectPackagesForInstallation
1✔
626
      log.info "Individual Packages for installation: #{autoinstPackages}"
3✔
627
      failed_packages = {}
3✔
628
      failed_packages = Pkg.DoProvide(autoinstPackages) unless autoinstPackages.empty?
3✔
629
      computed_packages = Packages.ComputeSystemPackageList
3✔
630
      log.info "Computed packages for installation: #{computed_packages}"
3✔
631
      if !computed_packages.empty?
3✔
632
        failed_packages = failed_packages.merge(Pkg.DoProvide(computed_packages))
3✔
633
      end
634

635
      # Blaming only packages which have been selected by the AutoYaST configuration file
636
      log.error "Cannot select following packages for installation:" unless failed_packages.empty?
3✔
637
      failed_packages.reject! do |name, reason|
3✔
638
        if @Software["packages"]&.include?(name)
5✔
639
          log.error("  #{name} : #{reason} (selected by AutoYaST configuration file)")
2✔
640
          false
2✔
641
        else
642
          log.error("  #{name} : #{reason} (selected by YAST automatically)")
3✔
643
          true
3✔
644
        end
645
      end
646

647
      unless failed_packages.empty?
3✔
648
        not_selected = ""
2✔
649
        suggest_y2log = false
2✔
650
        failed_count = failed_packages.size
2✔
651
        if failed_packages.size > MAX_PACKAGE_VIEW
2✔
652
          failed_packages = failed_packages.first(MAX_PACKAGE_VIEW).to_h
×
653
          suggest_y2log = true
×
654
        end
655
        failed_packages.each do |name, reason|
2✔
656
          not_selected << "#{name}: #{reason}\n"
2✔
657
        end
658
        # TRANSLATORS: Warning text during the installation. %s is a list of package
659
        error_message = _("These packages cannot be found in the software repositories:\n%s") %
2✔
660
          not_selected
661
        if suggest_y2log
2✔
662
          # TRANSLATORS: Error message, %d is replaced by the amount of failed packages.
663
          error_message += _("and %d additional packages") % (failed_count - MAX_PACKAGE_VIEW)
×
664
          # TRANSLATORS: Error message, %s is replaced by "/var/log/YaST2/y2log"
665
          error_message += "\n\n" + (_("Details can be found in the %s file.") %
×
666
            "/var/log/YaST2/y2log")
667
        end
668

669
        Report.Error(error_message)
2✔
670
      end
671
    end
672

673
    publish function: :merge_product, type: "void (string)"
1✔
674
    publish variable: :Software, type: "map"
1✔
675
    publish variable: :patterns, type: "list <string>"
1✔
676
    publish variable: :kernel, type: "string"
1✔
677
    publish variable: :modified, type: "boolean"
1✔
678
    publish variable: :inst, type: "list <string>"
1✔
679
    publish variable: :all_xpatterns, type: "list <map <string, any>>"
1✔
680
    publish variable: :instsource, type: "string"
1✔
681
    publish function: :SetModified, type: "void ()"
1✔
682
    publish function: :GetModified, type: "boolean ()"
1✔
683
    publish function: :Import, type: "boolean (map)"
1✔
684
    publish function: :AutoinstSoftware, type: "void ()"
1✔
685
    publish function: :Export, type: "map ()"
1✔
686
    publish function: :AddYdepsFromProfile, type: "void (list <string>)"
1✔
687
    publish function: :Summary, type: "string ()"
1✔
688
    publish function: :autoinstPackages, type: "list <string> ()"
1✔
689
    publish function: :Write, type: "boolean ()"
1✔
690
    publish function: :pmInit, type: "void ()"
1✔
691
    publish function: :addPostPackages, type: "void (list <string>)"
1✔
692
    publish function: :ReadHelper, type: "map <string, any> ()"
1✔
693
    publish function: :Read, type: "boolean ()"
1✔
694

695
  private
1✔
696

697
    # Get user transacted packages, include only the packages in the requested state
698
    # @param status [Symbol] package status (:available, :selected, :installed,
699
    # :removed)
700
    # @return [Array<String>] package names
701
    def user_transact_packages(status)
1✔
702
      # only package names (without version)
703
      names_only = true
4✔
704
      packages = Pkg.GetPackages(status, names_only)
4✔
705

706
      # iterate over each package, Pkg.ResolvableProperties("", :package, "") requires a lot of
707
      # memory
708
      packages.select do |package|
4✔
709
        Pkg.PkgPropertiesAll(package).any? do |p|
4✔
710
          p["transact_by"] == :user && p["status"] == status
4✔
711
        end
712
      end
713
    end
714
  end
715

716
  AutoinstSoftware = AutoinstSoftwareClass.new
1✔
717
  AutoinstSoftware.main
1✔
718
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc